参数通过`...`(三个点)分配到不同调用堆栈层的多个函数

时间:2014-11-15 14:45:07

标签: r arguments dispatch

IMO,它不仅仅是question that is being referred to的完全重复:

我的问题涉及nn = 2...和{{{{}} bar()中的参数的 foo() - 步骤调度foobar()) 1}} 都在foobar()内直接调用,但foo()只调用bar(),后者又调用...和解决方案答案没有考虑到这一点。我希望社区能够认识到这些方面并提出这个问题。

但是,我已经提供了一个答案,说明了我目前的解决方案,该解决方案考虑了所提问题答案中给出的重要建议。然而,我很高兴听到更好的解决方案或不同的方法。


我想知道是否有一种聪明的方法来精确控制参数通过R"三个点"参数foobar()

考虑以下用例:

  • 您有一个功能foo(),可以拨打bar()来调用foo()
  • bar()y都有一个名为foobar()的论据,但每个都有不同含义
  • 在致电y时,您想说的是foo() y bar()foobar(x = "John Doe", y = "hello world") y } foo()"。 这就是我想要完成的事情

如果您只是致电bar()bar(x = x, y = y)只会被调度到bar(x = x, ...),因为在y的调用中,事情必须是明确的,以便被派遣(即,呼叫必须是bar()而不是foo <- function(x, y = "some character", ...) { message("foo ----------") message("foo/threedots") try(print(list(...))) message("foo/y") try(print(y)) bar(x = x, ...) } bar <- function(x, y = TRUE, ...) { message("bar ----------") message("bar/threedots") try(print(list(...))) message("bar/y") try(print(y)) return(paste0("hello: ", x)) } foobar <- function(x, ...) { message("foobar ----------") message("foobar/threedots") try(print(list(...))) foo(x = x, ...) } foobar(x = "John Doe", y = "hi there") # foobar ---------- # foobar/threedots # $y # [1] "hi there" # # foo ---------- # foo/threedots # list() # foo/y # [1] "hi there" # bar ---------- # bar/threedots # list() # bar/y # [1] TRUE # [1] "hello: John Doe" 。此外,它将是来自foobar(x = "John Doe", y_foo = "hello world!", y_bar = FALSE) &#的错误&#34; foo <- function(x, y = "some character", ...) { message("foo ----------") message("foo/threedots") try(print(list(...))) message("foo/y") arg <- paste0("y_", sys.call()[[1]]) if (arg %in% names(list(...))) { y <- list(...)[[arg]] } try(print(y)) bar(x = x, ...) } bar <- function(x, y = TRUE, ...) { message("bar ----------") message("bar/threedots") try(print(list(...))) message("bar/y") arg <- paste0("y_", sys.call()[[1]]) if (arg %in% names(list(...))) { y <- list(...)[[arg]] } try(print(y)) return(paste0("hello: ", x)) } foobar(x = "John Doe", y_foo = "hello world!", y_bar = FALSE) # foobar ---------- # foobar/threedots # $y_foo # [1] "hello world!" # # $y_bar # [1] FALSE # # foo ---------- # foo/threedots # $y_foo # [1] "hello world!" # # $y_bar # [1] FALSE # # foo/y # [1] "hello world!" # bar ---------- # bar/threedots # $y_foo # [1] "hello world!" # # $y_bar # [1] FALSE # # bar/y # [1] FALSE # [1] "hello: John Doe" 无论如何,我的观点是:

...

我在概念上希望能做的是这样的事情:

setGeneric(
  name = "foo",
  signature = c("x", "..."),
  def = function(x, ...) standardGeneric("foo")      
)
setMethod(
  f = "foo", 
  signature = signature(x = "character", "..." = "MyThreeDotsForAFunctionImCalling"), 
 definition = function(x, ...) bar(x = x)
)
## --> does not work

这是一种有效的方法,但也感觉很奇怪:

{{1}}

你会如何实现这样的目标?

我还使用S4方法调度来查看我是否可以为签名参数{{1}}定义方法,但这并不是很好(这可能是一个非常糟糕的主意反正):

{{1}}

1 个答案:

答案 0 :(得分:0)

当前解决方案

请注意,如果可能,我不希望foobar()foo()bar()包含后续调用函数的任何显式参数(例如{ {1}}或y_foo)。他们应该能够接受随后被称为函数的任何输入,并相应地传递它们。当然,这需要为各自的功能清楚地记录下来。

解释

args_bar

应用

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
    )
  ))
}

#' @title
#' Does something foobar
#'
#' @description 
#' Calls \code{\link[foo.package]{foo}}.
#' 
#' @section Argument dispatch via ...:
#' 
#' Calling subsequent functions is handled by function 
#' \code{\link{withThreedots}}. In order for it to dispatch the correct 
#' arguments to the various functions further down the calling stack, 
#' you need to wrap them in a individual lists and name them according to 
#' the following convention: \code{args_<function-name>}. 
#' 
#' For example, arguments that should be passed to 
#' \code{\link[foo.package]{foo} would need to be stated as follows:
#' \code{args_foo = list(y = "hello world!")}. The same goes for arguments
#' that \code{\link[foo.package]{foo} passes to its subsequent functions.
#'     
#' @param x \code{\link{character}}. Some argument.
#' @param ... Further arguments to be passed to subsequent functions.
#'    In particular:
#'    \itemize{
#'      \item{\code{\link[foo.package]{foo}}. Make sure to also check if 
#'        this function in turn can pass along arguments via \code{...}. 
#'        In this case, you can also include those arguments.}
#'    }
#'    See section \strong{Argument dispatch via ...} for details about the 
#'    expected object structure of things to pass via \code{...}.
#' @example inst/examples/existsNested.r
#' @seealso \code{\link[foo.package]{foo}}
#' @export 
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)
}

使用内置S4调度程序

如果可以为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 定义S4方法并让内置调度程序执行...当前所做的工作,那么真正优秀的是什么。可以使用withThreedots()行中的类实例而不是列表args_<function>。有谁知道这样的事情可以做到吗?