使用原始函数名称作为参数名称访问函数参数。

3

我正尝试理解像下面这样的用户定义函数的行为(基于对问题的第一个答案),它将其提供的参数作为命名列表返回:

function(a, b, ...) {                                                                      
    argg <- c(as.list(environment()), list(...))
    print(argg)
}

本质上,像上面这样的函数,在其中一个参数名称也是一个只有...作为唯一参数的原始函数名称的情况下,会产生意想不到的行为。

以下是一些可复现的示例。

示例1-函数的行为如预期那样,缺少参数不会导致错误

#define function as above
fun1 <- function(a, b, ...) {                                                                      
          argg <- c(as.list(environment()), list(...))
          print(argg)
            }
        
    #run function
    fun1(a = 1)
    
    #returns the below. note that $b has the missing argument and this does not cause an error
    #$a
    #[1] 1
    
    #$b

    

示例2 - 如果" c "是显式参数之一且缺失,则函数返回错误

#define function as above but with new explicit argument, called 'c'
#note that c() is a primitive function whose only parameter is ...

fun2 <- function(a, b, c, ...) {                                                                      
          argg <- c(as.list(environment()), list(...))
          print(argg)
            }
        
    #run function
    fun2(a = 1)

#returns error:
#Error in c(as.list(environment()), list(...)) :
#  argument "c" is missing, with no default

示例3 - 使用参数替换'c',一个带有非...参数的原始函数

#define function same way as fun2, but change 'c' parameter to 'switch'
#note that switch() is a primitive function that has parameters other than ...
fun3 <- function(a, b, switch, ...) {                                                                      
          argg <- c(as.list(environment()), list(...))
          print(argg)
            }

#run function
fun3(a = 1)

#returns the below. note that $b and $switch have the missing argument and this does not cause an error
#$a
#[1] 1

#$b

#$switch

我已经尝试了许多以上的变化,但这些变化似乎没有意义,因为基本模式应该是清晰的,因此可以很容易地复制,而无需特定的代码段;只要说,据我所知,如果函数的一个参数 a.) 与只有一个参数 ... 的原始函数同名,并且 b.) 也缺失,则该函数返回一个错误。我测试过其他更改(如从用户定义的函数参数中移除...;在调用函数或定义函数时更改参数指定的顺序;更改在调用函数或定义函数时指定的其他参数的名称和数量等)都不会影响行为是否符合预期。

另一个要注意的问题是,如果我定义一个具有与fun2相同参数的函数,并且仍然缺少c 参数,而我没有试图在其中访问函数的参数,我就不会看到任何错误。例如:

    #define function with same parameters but different content to fun2

    fun4 <- function(a, b, c, ...) {                                                                      
              return(a+b)
                }
            
        #run function
        fun4(a = 1, b = 2)

#returns
#[1] 3

请问有人能解释一下为什么我会看到这种行为模式以及为什么原始函数在只有...作为参数时似乎扮演了关键角色。
请不要提交回答或评论,建议使用“变通方法”或询问手头问题的实际意义。我提出这个问题不是为了解决特定的实际问题,也没有理由让我使用原始函数名称作为参数;相反,我想理解为什么错误发生时它们会发生,以便更清楚地了解R中的函数以及访问其参数的过程的工作方式。

c 函数在您的函数环境中。R 不会去全局环境中查找 c 函数(在 base 中)。由于 c 是空/缺失的,因此无法评估 c(...),因为 c 不是一个函数。如果您对 R 在“引擎盖下”做了什么更深入的理解感兴趣,我建议阅读 Hadley 的《Advanced R》书籍 https://adv-r.hadley.nz/index.html。 - Baraliuh
1个回答

2

问题不是由于...引起的。当你调用c()时,R会在环境中查找函数定义。在函数外部,它通常会将其查找为base::c。但在函数内部,它首先查找函数调用中参数c中的定义,然后找不到。这种调用方式表明它可以通过告诉R具体在哪里查找c的定义来正常工作:

fun4 <- function(a, b, c, ...) {                                                                      
  argg <- base::c(as.list(environment()), list(...))
  print(argg)
}

#run function
fun4(a = 1)
#> $a
#> [1] 1
#> 
#> $b
#> 
#> 
#> $c

环境 - 来自 Advanced R

为了展示代码中每个对象的调用位置,您可以使用Hadley Wickham的Advanced R中的技巧来查看R在哪里找到每个对象。在函数中,如果c不是一个参数,它会在base中找到它,否则它会在函数环境中“找到”它(其中也定义了ab):

library(rlang)

where <- function(name, env = caller_env()) {
  if (identical(env, empty_env())) {
    stop("Can't find ", name, call. = FALSE)
  } else if (env_has(env, name)) {
    env
  } else {
    where(name, env_parent(env))
  }
}


fun5 <- function(a, b, ...) {   
  print(where("a"))
  print(where("b"))
  print(where("c"))
}

#run function
fun5(a = 1)
#> <environment: 0x000000001de35890>
#> <environment: 0x000000001de35890>
#> <environment: base>

fun6 <- function(a, b, c, ...) {   
  print(where("a"))
  print(where("b"))
  print(where("c"))
}

#run function
fun6(a = 1)
#> <environment: 0x000000001e1381f0>
#> <environment: 0x000000001e1381f0>
#> <environment: 0x000000001e1381f0>

reprex 软件包 (v2.0.1) 于2021-12-15创建


1
谢谢,这是一个很好的解释。 - Inta Jones
很高兴能帮到你!在阅读《Advanced R》并理解其中的内容之前,我对环境有些困惑,但这确实有助于理解R语言。 - Andy Baxter

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