R中关于函数参数的困惑

4
如果我声明一个函数,我可以引用之前的参数:
blah <- function( a=1, b=a ) { print(sprintf("a=%d, b=%d", a, b)) }

输出为:
> blah(10)
[1] "a=10, b=10"
> blah(10, b=30)
[1] "a=10, b=30"

然而,以下内容无法正常工作:
> blah(a=10, b=a)
Error in sprintf("a=%d, b=%d", a, b) : object 'a' not found

实际上,这更或多或少是人们所期望的;那么为什么声明blah <- function(a=10, b=a)会起作用?为什么这里的作用域与调用函数时不同?

另外,为什么只有在调用sprintf时才出现错误?为什么不在调用函数时立即抛出错误?我很困惑。

编辑:

在这里解释我的困惑。当我声明一个函数时,参数不会被评估。R具有惰性求值,变量在需要时才会被评估。考虑一下:

> blah <- function( a=1, b=print("foo") ) { print( "So far, so good") ; print( b )  }
>

没有进行评估。现在我正在调用 blah:

> blah()
[1] "So far, so good"
[1] "foo"
[1] "foo"

函数中的第一条语句被执行,然后print("foo")。然而,在此时,a已经在函数作用域中。那么为什么b=a不会被执行呢?当它发生时,我们已经在函数中了,a已经被声明了。 编辑2: 在您得出错误结论之前,请注意,在R函数声明中引用先前的参数完全没有问题,这是由于R的惰性评估。我不明白的是,为什么在函数声明中它可以工作,但在调用时却不能。我并不是说它应该或不应该工作,只是想知道作用域的基本机制。

你的环境中没有变量"a",所以才会出现这个错误。如果你首先定义a = 150,那么它就可以工作了。你函数中的"a"和环境中的"a"是不同的对象。 - user3710546
那么为什么当我声明一个函数时它就能工作呢? - January
好的,确实如此,请自行尝试。 - January
在一个干净的 R 会话中,定义 blah <- function(a=10, b=a) { },然后执行 ls() - user3710546
当我这样做时,我只看到一个对象,“blah”。 - January
显示剩余2条评论
2个回答

3

这两个表达式被评估的位置不同。当你调用一个函数时,参数在当前作用域中被评估。当你定义一个函数时,参数在函数作用域内被评估。通常情况下,这是你想要的行为。

因此,当你调用函数时,预期你控制所有传递给函数的值。你不应该知道函数内部正在使用哪些变量。使用延迟评估,这个结构也可以工作:

blah <- function( a=1, b=x ) {
   x < a+10
   print(sprintf("a=%d, b=%d", a, b))
}
blah(1)

因此,尝试调用

blah(1, b=x+5)

从技术上讲,你甚至不应该知道函数内部存在变量x,因此这样做甚至更加没有意义。

通过这个例子,你可以看到环境的差异。在这里,我们使用parent.frame()来获取调用函数的环境。

myenv <- function() parent.frame()
foo <- function( a=myenv() ) {
    print(environment())
    print(a)
}
foo()
# <environment: 0x10c8b1948>
# <environment: 0x10c8b1948>
foo( a=myenv() )
# <environment: 0x10bd85ad8>
# <environment: R_GlobalEnv>

因此,默认参数值时,函数在与其自身相同的环境中运行。当您显式传递参数时,它将在调用它的环境中运行(在这种情况下,是全局环境)。

这意味着,当调用一个函数并设置其他参数值时,您不能使用函数参数的名称作为变量名。


谢谢,那就是我问题的答案。 - January

3
当您执行以下操作时:
> blah(a=10, b=a)

你正在说:我在我的全局环境中有一个值为10,并将其赋值给参数a。我在我的全局环境中有一个a变量,并将其值赋给参数b
变量a与函数的参数a完全不同!如果未定义变量a并且想要将其传递给函数,则R会报错。
通过执行以下操作,您将遇到完全相同的错误:
 > blah(a=10, b=nonExistingVariable)
 Error in sprintf("a=%d, b=%d", a, b) : object 'NonExistingVariable' not found

我认为代码的最后一行比任何解释都更明确,我会缩短我的解释。 - Colonel Beauvel
好的,那么为什么 blah <- function(a=1, b=a) 能够工作呢? - January
在一月份,你定义了一个函数并将其分配给变量 blah,但你没有使用这个函数。 - Colonel Beauvel

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