【问题标题】:Confused by ...()?对 ...() 感到困惑?
【发布时间】:2012-09-21 02:41:10
【问题描述】:

another question 中,sapply(substitute(...()), as.character) 在函数内部用于获取传递给函数的名称。 as.character 部分听起来不错,但 ...() 到底是做什么的?

substitute 之外的代码无效:

> test <- function(...) ...()
> test(T,F)
Error in test(T, F) : could not find function "..."

更多测试用例:

> test <- function(...) substitute(...())
> test(T,F)
[[1]]
T

[[2]]
F

> test <- function(...) substitute(...)
> test(T,F)
T

【问题讨论】:

  • 除此之外,我很好奇为什么substitute(...) 只返回... 的第一个元素。 (请注意,test &lt;- function(...) substitute({...}); test(T, F) 将以某种方式返回它们。
  • @mnel,如果你在 c 方面比我好(不是一个困难的成就),请查看 */src/main 中的 coerce.c 文件中的函数 substitutesubstituteListdo_substitute。他们确实以特殊的方式处理椭圆。
  • @mnel 我不认为...()... 的元素创建函数。 ... 是一个配对列表,substitute() 的第一个参数应该是一个表达式。在表达式中( 可用于简单地对事物进行分组。正如substitute 所做的只是返回... 的解析树,我想知道() 是否只是解析器的一个技巧或类似的技巧,以使其以某种方式对... 进行分组。正如比尔的电子邮件所示,没有任何函数创建,因为它会显示在生成的解析树中,不是吗?
  • 我强烈建议不要使用这种结构。曾经。 Bill D. 可能已经发现了一个很棒的偷偷摸摸的把戏,但鉴于它几乎没有记录在案,因此真的不能保证它在所有情况下都能正常工作,或者未来某个 R 核心的 mod 不会破坏它。有时我们为了自己的利益太聪明了 :-(。如果帕特里克“地狱”伯恩斯能证明这一点,我会收回这条评论。:-)
  • substitute(...[]) 也一样...

标签: r


【解决方案1】:

以下是...() 为何如此运作的示意图。稍后我会填写更多细节和参考资料,但这涉及到关键点。

  1. 在对其任何组件执行替换之前,substitute() 首先解析 R 语句。

  2. ...() 解析为调用对象,而... 解析为名称对象。

  3. ... 是一个特殊对象,仅用于函数调用。因此,实现替换的 C 代码在调用对象中找到 ... 时会采取特殊措施来处理它。当... 作为符号出现时采取类似的预防措施。 (相关代码在R_SRCDIR/src/main/coerce.c中的函数do_substitutesubstitutesubstituteList(尤其是后两者)中。)

因此,...() 中的() 的作用是使语句被解析为调用(又名语言)对象,因此替换将返回点的完全扩展值。 ... 即使在 () 的外部也被替换,这似乎令人惊讶,但是:(a) 调用在内部存储为类似列表的对象,并且 (b) 相关的 C 代码似乎没有区别在该列表的第一个元素和后续元素之间。


顺便说一句:为了检查substitute 的行为或各种对象的类,我发现设置一个小沙箱很有用,如下所示:

f <- function(...) browser()
f(a = 4, 77, B = "char")
## Then play around within the browser
class(quote(...))  ## quote() parses without substituting
class(quote(...()))
substitute({...})
substitute(...(..., X, ...))
substitute(2 <- (makes * list(no - sense))(...))

【讨论】:

  • 我仍然不明白的一点是 ...substitute(...()) 中的替换如何更改返回对象的类。我检查过的调用对象中的所有其他替换(例如substitute(j())substitute(j(...)),甚至j &lt;- pairlist(x=4, y=5); substitute(j()))都返回调用对象。我认为答案在 C 级别调用 CDRCARSETCDRCONS,我只是无法完全遵循/游戏。
  • 继续走这条奇怪的路,试试这个:substitute((...)),然后这个:as.list(substitute((...)))。似乎这两种情况下的结果都是函数call 的“不适当”对象'(',因为这个函数只接受一个参数。在第一种情况下,print 假定这是一个正确的调用,并且只显示第一个参数。在第二种情况下,我们可以看到调用的所有元素。