如何获取传递给函数的所有参数及其值?

4

我有一个函数

adebo.deepSearch = function(z, pi_0 = 0.3, families=list(), ... )
    {

    }

我想通过一个名为grabFunctionParameters的函数捕获传递的所有参数名称和值,例如:
adebo.deepSearch = function(z, pi_0 = 0.3, families=list(), ... )
    {
    args = grabFunctionParameters();
    }

其中args将是一个包含“键”和“值”的列表,例如

args[["pi_0"] = 0.3;

包括省略号(...)中的所有键和值。

理想(可变参数)解决方案是外部函数grabFunctionParameters()

解决方案:


这里是提供的已接受答案:
# https://dev59.com/Yb_qa4cB1Zd3GeqPLZt8
# nice work :: B. Christian Kamgang
# .GlobalEnv$.function.args.memory ... key memory on last function call ... so I could reference outside the function
grabFunctionParameters <- function() {
    pf <- parent.frame()    
    args_names <- ls(envir = pf, all.names = TRUE, sorted = FALSE)
    if("..." %in% args_names) {
    dots <- eval(quote(list(...)), envir = pf)
    }  else {
    dots = list()
    }
    args_names <- sapply(setdiff(args_names, "..."), as.name)
    if(length(args_names)) {
    not_dots <- lapply(args_names, eval, envir = pf) 
    } else {
    not_dots <- list()
    }   
   idx <- names(dots) != "";
   list(.keys. = names(not_dots), .vals. = unname(not_dots), .fn. = as.character(sys.call(1L)[[1L]]), .scope. = pf, .dot.keys. = names(dots[idx]), .dot.vals. = unname(dots[idx])); 
} 


这是提供的已接受答案(格式略有不同):
grabFunctionParameters <- function() 
    {
    pf          = parent.frame();    
    my.names    = ls(envir = pf, all.names = TRUE, sorted = FALSE);
    
    dots        = if("..." %in% my.names) { eval(quote(list(...)), envir = pf); } else { list(); }  
    dots.idx    = ( names(dots) != "" );
    
    remaining   = sapply( setdiff(my.names, "..."), as.name);
    
    not.dots    = if(length(remaining) > 0) { lapply( remaining, eval, envir = pf);  } else { list(); }
    
   
    res = list();
    
        res$.fn.            = as.character( sys.call(1L)[[1L]] );
        res$.scope.         = pf;
        res$.keys.          = names( not.dots );
        res$.vals.          = not.dots;                             # unname(not_dots);  # I want keys on "vals"
        res$.dots.keys.     = names( dots[dots.idx] );
        res$.dots.vals.     = dots[dots.idx];                       # unname(dots[dots.idx]); 

    res;
    } 

如何获取函数中传递的所有参数?参考names(list(...))names(match.call()[-1])。它们可以在函数grabFunctionParameters中被引用吗?也许需要包含一个enviro?获取名称是解决问题的一半。 - mshaffer
根据函数体的其余部分,您可以重写该函数以接受命名列表作为参数。 - the_man_in_black
哪个函数?grabFunctionParameters吗?目标是创建一个可变参数的解决方案,我只是提供了一个单一的例子,其中包含 adebo.deepSearch = function(z, pi_0 = 0.3, families=list(), ... ) - mshaffer
as.list(match.call()[-1]) - Roland
如果未传递默认参数,则as.list(match.call()[-1])不包括默认参数?它提供变量,但不提供值:> adebo.deepSearch(z) $z z 并且我可以有一个外部函数grabFunctionParameters(可变解决方案)吗? - mshaffer
除非你能提供一个合理的解释为什么这很有用,否则我不会再花时间在这上面了。看起来像是一个xy问题。 - Roland
4个回答

4
您可以使用mget函数获取函数环境。
adebo.deepSearch <- function(z, pi_0 = 0.3, families=list(), ... ) {
  c(mget(ls(environment(), sorted=F)), match.call(expand.dots=F)$...)
}
adebo.deepSearch(foo=1, z=2)
# $z
# [1] 2
# 
# $pi_0
# [1] 0.3
# 
# $families
# list()
# 
# $foo
# [1] 1

这个 match.callmget 能否从外部函数(可变解决方案)中调用...“两次调用”之前我在“adebo”,参数是什么... grabFunctionParameters - mshaffer
tt <- "c(match.call(expand.dots=F)$..., mget(ls(environment())))";adebo.deepSearch <- function(z, pi_0 = 0.3, families=list(), ... ) eval(parse(text=tt));adebo.deepSearch(foo=1, z=2) 这段代码怎么样? - jay.sf
1
相当简单而优雅,太棒了! - undefined

4
这里有一个可能的解决方案。这个解决方案需要指定没有默认值的函数参数(像下面的 z) 。
grabFunctionParameters <- function() {
  pf <- parent.frame()                                   # get caller environment
  dots <- eval(quote(list(...)), envir = pf)             # get ... in the caller
  nms <- sapply(ls(envir = pf, sorted = FALSE), as.name) # get argument names different from names in ... in the caller
  out <- c(lapply(nms, eval, envir = pf), dots)          # get all arguments/values
  out[names(out) != ""]                                  # remove unnamed values in ... (if any)
}

应用场景示例

adebo.deepSearch = function(z, pi_0 = 0.3, families=list(), ... ) {
  args = grabFunctionParameters();
  args
}

一些场景

adebo.deepSearch(z=4)
# $z
# [1] 4
# 
# $pi_0
# [1] 0.3
# 
# $families
# list()
# 
adebo.deepSearch(z=4, pi_0=9, families = list(z=1:2))  
# $z
# [1] 4
# 
# $pi_0
# [1] 9
# 
# $families
# $families$z
# [1] 1 2
# 
# 
adebo.deepSearch(z=4, pi_0=9, ac=5, bc=6)  # some additional arguments for ...
# $z
# [1] 4
# 
# $pi_0
# [1] 9
# 
# $families
# list()
# 
# $ac
# [1] 5
# 
# $bc
# [1] 6

更新:这是对上述函数的更新,使其更加通用。

该函数总是返回一个列表:

  • 如果调用者(函数)没有参数(或只有未命名值的...),则返回一个空列表。
  • 形式参数名称(不在...中)可以以点号开头。 之前的函数要求调用者带有...;而具有以点号开头的形式参数名称(不在...中)的调用者则不会被返回。

新函数

grabFunctionParameters <- function() {
    pf <- parent.frame()    
    args_names <- ls(envir = pf, all.names = TRUE, sorted = FALSE)
    if("..." %in% args_names) {
    dots <- eval(quote(list(...)), envir = pf)
    }  else {
    dots = list()
    }
    args_names <- sapply(setdiff(args_names, "..."), as.name)
    if(length(args_names)) {
    not_dots <- lapply(args_names, eval, envir = pf) 
    } else {
    not_dots <- list()
    }
    out <- c(not_dots, dots)
    out[names(out) != ""]                                  # remove unnamed values in ... (if any)
}   

一些场景
fn1 <- function() grabFunctionParameters()                              # the initial function (before the update) required ... argument
fn2 <- function(x=1, .a=2, b=list(), ...) grabFunctionParameters()      # the initial function did not return .a 
fn3 <- function(.x, .a=2, b=list(), ...) grabFunctionParameters()
fn4 <- function(...) grabFunctionParameters()
fn5 <- function(x, .a) grabFunctionParameters()                        # the initial function required ... argument


fn1()     # correct since the caller has no argument. Previously not allowed!
# list()

fn2()
# $x
# [1] 1
# 
# $.a
# [1] 2
# 
# $b
# list()
                                    
fn2(.a=10, ac=4, bc=7, .xy=1)      #    
# $x
# [1] 1
# 
# $.a
# [1] 10
# 
# $b
# list()
# 
# $ac
# [1] 4
# 
# $bc
# [1] 7
# 
# $.xy
# [1] 1

fn3(10)
# $.x
# [1] 10
# 
# $.a
# [1] 2
# 
# $b
# list()

fn3()       # throw an error! (.x required!). This will not happen if we use mget function and not lapply/supply inside grabFunctionParameters above. 
# Error in FUN(X[[i]], ...) : argument ".x" is missing, with no default

fn4(a = 5, b = 6, c = 6, 6, 7, 9)       # unnamed values are dropped
# $a
# [1] 5
# 
# $b
# [1] 6
# 
# $c
# [1] 6

fn5(6, 8)
# $x
# [1] 6
# 
# $.a
# [1] 8

我使用了lapply,因为mget(mget(nms))如果未指定,则会将调用函数的必需参数作为“name”类对象返回(这对于您的问题有点奇怪),而lapply则返回有意义的错误消息。尝试以下代码以了解更多信息。f <- function() mget(c("a", "b"), envir = parent.frame()); f1 <- function(a, b=1) f(); f2 <- function(a, b=1) grabFunctionParameters(); f1(); f2(); sapply(f1(), class) - B. Christian Kamgang
有了这些键,我可以变幻多端地遍历值。这是一个非常好的解决方案,我期待着将其投入使用。对于模拟,我可以使用键值对来构建日志文件,添加为模拟结果的一列等等。这使得创建完全可复制的研究非常有用。 - mshaffer
是的,按预期工作:$.fn. [1] "includeGithubFolder" $.scope. $.keys. [1] "url" $.vals. $.vals.$url [1] "https://github.com/MonteShaffer/humanVerse/tree/main/misc/" $.dot.vals. list()``` - mshaffer
ls(envir = pf, all.names = TRUE, sorted = FALSE); had to be updated to ls(pos = pf, envir = pf, all.names = TRUE, sorted = FALSE); - mshaffer
@b-christian-kamgang 我正在处理一个函数,但它不起作用。plot.myTukeyPlot = function(..., heresy=FALSE, margin = 0.25, border = 10 ) - mshaffer
显示剩余36条评论

0
adebo <- function(z, pi_0 = 0.3, families = list(), ...) {
  args <- formals(adebo)
  return(args)
}

adebo()
#> $z
#> 
#> 
#> $pi_0
#> [1] 0.3
#> 
#> $families
#> list()
#> 
#> $...

2021年02月23日由reprex package(v1.0.0)创建


不幸的是,这将返回每个参数的默认值,而不是实际传递的值。 - Limey
我建议在R中不要在函数名称中使用点,因为这可能会导致方法分派出现问题。例如,可以查看?print:print.aov、print.formula等是aov / formula对象的特定打印方法。 - Wolf
使用 environment(f) 获取环境的作用域;使用 dots <- match.call(expand.dots = FALSE)$... 获取省略号 ... 的值,这是可变参数的解决方案,需要使用外部函数来获取。 - mshaffer
相信我,对于这个项目,我需要 ... - mshaffer
酸橙:对。抱歉,那不是一个解决方案。 - Wolf

0

这不是一个解决方案,而是一个捕获“…”部分的想法。将作为quosure或quosure列表传递给...的参数返回。

adebo <- function(z, pi_0 = 0.3, families = list(), ...) {
  rlang::enquos(...)
}

adebo(trash = "trash", idea = "idea")
#> <list_of<quosure>>
#> 
#> $trash
#> <quosure>
#> expr: ^"trash"
#> env:  empty
#> 
#> $idea
#> <quosure>
#> expr: ^"idea"
#> env:  empty

reprex包(v1.0.0)于2021-02-23创建


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接