gpt4 book ai didi

r - 泛化 `...`(三个点)参数调度 : S4 methods for argument set including `...`

转载 作者:行者123 更新时间:2023-12-04 02:19:01 26 4
gpt4 key购买 nike

实际问题

是否可以为 的一组签名参数定义方法?包括 ... (相对于 专门 用于 ... )? “开箱即用”是不可能的,但理论上它会是 完全有可能 (涉及一些调整)或者这是由于 S4 机制的设计方式而根本无法完成的事情?

我正在寻找类似的东西

setGeneric(
name = "foo",
signature = c("x", "..."),
def = function(x, ...) standardGeneric("foo")
)
setMethod(
f = "foo",
signature = signature(x = "character", "..." = "ThreedotsRelevantForMe"),
definition = function(x, ...) bar(x = x)
)

幸运的是,马丁·摩根(Martin Morgan)向我指出了 dotsMethods它说:

Currently, “...” cannot be mixed with other formal arguments: either the signature of the generic function is “...” only, or it does not contain “...”. (This restriction may be lifted in a future version.)



背景

考虑以下基于 ... 概括调度机制的尝试。从一个简单的案例(只有 一个 多个函数应该使用通过 ... 传递的参数;例如,在 ... 中使用 plot() 将参数传递给 par() )到涉及以下情况的场景方面(取自 here):
  • 当您想将参数传递给 不止一个 ,因此 r ,收件人,
  • 当这些收件人可以位于 c调用堆栈的不同层
  • 当他们甚至可能使用 相同的参数名称 但关联 不同的含义在它们自己的范围/闭包/框架/环境中处理这些论点

  • 另请注意,虽然这样做确实是一种好习惯,但顶级函数/接口(interface)应该 不一定需要关注定义(很多) 显式 随后调用的函数/接口(interface)的参数,以便正确传递参数。 IMO,这个选择应该留给开发人员,因为有时一个或另一个选择更有意义。

    如果我可以替换当前通过 withThreedots() 处理的调度,那就太酷了(AFAICT 需要以某种方式与 S4 调度程序进行 的实际拆分 ... ),因此理想情况下只需能够调用 foo(x = x, ...)而不是 withThreedots("foo", x = x, ...)foobar() :

    定义
    withThreedots <- function(fun, ...) {
    threedots <- list(...)
    idx <- which(names(threedots) %in% sprintf("args_%s", fun))
    eval(substitute(
    do.call(FUN, c(THREE_THIS, THREE_REST)),
    list(
    FUN = as.name(fun),
    THREE_THIS = if (length(idx)) threedots[[idx]],
    THREE_REST = if (length(idx)) threedots[-idx] else threedots
    )
    ))
    }
    foobar <- function(x, ...) {
    withThreedots("foo", x = x, ...)
    }
    foo <- function(x = x, y = "some text", ...) {
    message("foo/y")
    print(y)
    withThreedots("bar", x = x, ...)
    }
    bar <- function(x = x, y = 1, ...) {
    message("bar/y")
    print(y)
    withThreedots("downTheLine", x = x, ...)
    }
    downTheLine <- function(x = x, y = list(), ...) {
    message("downTheLine/y")
    print(y)
    }

    申请
    foobar(x = 10) 
    foobar(x = 10, args_foo = list(y = "hello world!"))
    foobar(x = 10, args_bar = list(y = 10))
    foobar(x = 10, args_downTheLine = list(y = list(a = TRUE)))

    foobar(x = 10,
    args_foo = list(y = "hello world!"),
    args_bar = list(y = 10),
    args_downTheLine = list(y = list(a = TRUE))
    )

    # foo/y
    # [1] "hello world!"
    # bar/y
    # [1] 10
    # downTheLine/y
    # $a
    # [1] TRUE

    概念方法(主要是伪代码)

    我想我正在寻找类似的东西:

    定义
    setGeneric(
    name = "foobar",
    signature = c("x"),
    def = function(x, ...) standardGeneric("foobar")
    )
    setMethod(
    f = "foobar",
    signature = signature(x = "ANY"),
    definition = function(x, ...) pkg.foo::foo(x = x, ...)
    )

    假设:foo()在包/命名空间 pkg.foo 中定义
    setGeneric(
    name = "foo",
    signature = c("x", "y", "..."),
    def = function(x, y = "some text", ...) standardGeneric("foo")
    )
    setMethod(
    f = "foo",
    signature = signature(x = "ANY", y = "character", "..." = "Threedots.pkg.foo.foo"),
    definition = function(x, y, ...) {
    message("foo/y")
    print(y)
    pkg.bar::bar(x = x, ...)
    }
    )

    假设:bar()在包/命名空间 pkg.bar 中定义:
    setGeneric(
    name = "bar",
    signature = c("x", "y", "..."),
    def = function(x, y = 1, ...) standardGeneric("bar")
    )
    setMethod(
    f = "bar",
    signature = signature(x = "ANY", y = "numeric", "..." = "Threedots.pkg.bar.bar"),
    definition = function(x, y, ...) {
    message("bar/y")
    print(y)
    pkg.a::downTheLine(x = x, ...)
    )
    setGeneric(
    name = "downTheLine",
    signature = c("x", "y", "..."),
    def = function(x, y = list(), ...) standardGeneric("downTheLine")
    )

    假设:downTheLine()在包/命名空间 pkg.a 中定义:
    setMethod(
    f = "downTheLine",
    signature = signature(x = "ANY", y = "list", "..." = "Threedots.pkg.a.downTheLine"),
    definition = function(x, y, ...) {
    message("downTheLine/y")
    print(y)
    return(TRUE)
    )

    说明调度员需要做什么

    关键部分是它必须能够区分 ... 中的那些元素。与各自当前相关的 fun被调用(基于对常规 三点签名参数的完整 S4 调度)以及应该传递给 fun 的函数的那些元素可能正在调用(即 ... 的更新状态;类似于上面 withThreedots() 内部发生的情况):
    s4Dispatcher <- function(fun, ...) {
    threedots <- splitThreedots(list(...))
    ## --> automatically split `...`:
    ## 1) into those arguments that are part of the signature list of `fun`
    ## 2) remaining part: everything that is not part of
    ## the signature list and that should thus be passed further along as an
    ## updated version of the original `...`

    args_this <- threedots$this
    ## --> actual argument set relevant for the actual call to `fun`
    threedots <- threedots$threedots
    ## --> updated `...` to be passed along to other functions

    mthd <- selectMethod(fun, signature = inferSignature(args_this))
    ## --> `inferSignature()` would need to be able to infer the correct
    ## signature vector to be passed to `selectMethod()` from `args_this`

    ## Actual call //
    do.call(mthd, c(args_this, threedots))
    }

    下面是“键入的三点参数容器”的生成器的外观示例。

    请注意,为了使这种机制跨包工作,还提供一种声明某个函数的命名空间的可能性(参数 ns 和字段 .ns )可能是有意义的:
    require("R6")
    Threedots <- function(..., fun, ns = NULL) {
    name <- if (!is.null(ns)) sprintf("Threedots.%s.%s", ns, fun) else
    sprintf("Threedots.%s", fun)
    eval(substitute({
    INSTANCE <- R6Class(CLASS,
    portable = TRUE,
    public = list(
    .args = "list", ## Argument list
    .fun = "character", ## Function name
    .ns = "character", ## Namespace of function
    initialize = function(..., fun, ns = NULL) {
    self$.fun <- fun
    self$.ns <- ns
    self$.args <- structure(list(), names = character())
    value <- list(...)
    if (length(value)) {
    self$.args <- value
    }
    }
    )
    )
    INSTANCE$new(..., fun = fun, ns = ns)
    },
    list(CLASS = name, INSTANCE = as.name(name))
    ))
    }

    例子
    x <- Threedots(y = "hello world!", fun = "foo", ns = "pkg.foo")

    x
    # <Threedots.pkg.foo.foo>
    # Public:
    # .args: list
    # .fun: foo
    # .ns: pkg.foo
    # initialize: function

    class(x)
    # [1] "Threedots.pkg.foo.foo" "R6"

    x$.args
    # $y
    # [1] "hello world!"

    实际调用将如下所示:
    foobar(x = 10) 
    foobar(x = 10, Threedots(y = "hello world!", fun = "foo", ns = "pkg.foo"))
    foobar(x = 10, Threedots(y = 10, fun = "bar", ns = "pkg.bar"))
    foobar(x = 10, Threedots(y = list(a = TRUE), fun = "downTheLine", ns = "pkg.a")))

    foobar(x = 10,
    Threedots(y = "hello world!", fun = "foo", ns = "pkg.foo"),
    Threedots(y = 10, fun = "bar", ns = "pkg.bar),
    Threedots(y = list(a = 10), fun = "downTheLine", ns = "pkg.a")
    )

    最佳答案

    ?setGeneric并搜索“...”,然后搜索 ?dotsMethods .可以定义在 ... 上调度的泛型。 (仅,不与调度的其他参数混合)。

    .A = setClass("A", contains="numeric")
    .B = setClass("B", contains="A")

    setGeneric("foo", function(...) standardGeneric("foo"))
    setMethod("foo", "A", function(...) "foo,A-method")

    setGeneric("bar", function(..., verbose=TRUE) standardGeneric("bar"),
    signature="...")
    setMethod("bar", "A", function(..., verbose=TRUE) if (verbose) "bar,A-method")

    导致
    > foo(.A(), .B())
    [1] "foo,A-method"
    > bar(.A(), .B())
    [1] "bar,A-method"
    > bar(.A(), .B(), verbose=FALSE)
    >

    我不知道这是否适合您的其他情况。

    关于r - 泛化 `...`(三个点)参数调度 : S4 methods for argument set including `...` ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26963900/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com