省略号...作为替代函数?

Don*_*nen 27 r function ellipsis

我无法理解括号如何/为什么在它们不应该起作用的地方起作用\xc2\xae。

\n
f = function(...) substitute(...()); f(a, b)\n[[1]]\na\n[[2]]\nb\n# but, substitute returns ..1\nf2 = function(...) substitute(...); f2(a, b)\na\n
Run Code Online (Sandbox Code Playgroud)\n

通常会抛出错误,找不到在不正确的上下文中使用的函数“...”或\'...\' ,例如在调用时(\\(...) ...())(5)

\n

我尝试过的\
n我查看了源代码substitute找出为什么这里不会发生这种情况。R 内部1.1.1 和 1.5.2 表示...是 SEXPTYPE DOTSXP,一对承诺列表。这些承诺是由 提取的substitute

\n
#  \\-substitute #R\n#    \\-do_substitute #C\n#      \\-substituteList #C recursive\n#        \\-substitute #C\n
Run Code Online (Sandbox Code Playgroud)\n

逐行进行,我陷入了困境substituteList,其中h是当前正在处理的元素...。这在第 2832 行递归发生if (TYPEOF(h) == DOTSXP) h = substituteList(h, R_NilValue);。我没有...()在源代码中找到案例的异常处理,所以我怀疑在此之前发生了什么。

\n

?substitute我们发现纯粹词汇基础上的替代作品。这是否意味着...()这是一个解析器技巧?

\n
parse(text = "(\\\\(...) substitute(...()))(a, b)") |> getParseData() |> subset(text == "...", select = c(7, 9))\n\n#>                   token  text\n#> 4        SYMBOL_FORMALS   ...\n#> 10 SYMBOL_FUNCTION_CALL   ...\n
Run Code Online (Sandbox Code Playgroud)\n

第二个省略号在词法分析期间被识别为函数调用的名称。它不像|>以前那样有自己的令牌。输出是一个对列表 ( typeof(f(a, b))) ,在本例中与常规列表(?)相同。我想这不是解析器技巧。但不管它是什么,它已经存在了一段时间了!

\n

老把戏

\n

问题
\n如何...()工作?

\n

Mik*_*gan 22

注意:在参考文档和源代码时,我提供了R官方Subversion 存储库的非官方 GitHub 镜像的链接。这些链接绑定到 GitHub 存储库中的提交 97b6424 ,该链接映射到 Subversion 存储库中的修订版(本次编辑时的最新版本)。 81461

\n
\n

substitute是一个“特殊”,其参数不被评估(doc)。

\n
typeof(substitute)\n
Run Code Online (Sandbox Code Playgroud)\n
[1] "special"\n
Run Code Online (Sandbox Code Playgroud)\n

这意味着 的返回值substitute可能与解析器逻辑不一致,具体取决于内部处理未评估参数的方式。

\n

通常,以(伪代码) (doc )形式substitute接收调用。调用的上下文决定了如何处理。具体来说, if在函数内部被调用并作为形式参数...(<exprs>)LANGSXPpairlist(R_DotsSymbol, <exprs>)substituteSYMSXP R_DotsSymbolsubstitute...rho作为其执行环境,则结果为

\n
findVarInFrame3(rho, R_DotsSymbol, TRUE)\n
Run Code Online (Sandbox Code Playgroud)\n

substituteList在 C 实用程序( source )的主体中是 aDOTSXPR_MissingArg\xe2\x80\x94,后者当且仅当在f不带参数的情况下调用 ( doc )。在其他上下文中,结果是R_UnboundValueor (例外)其他一些SEXP\xe2\x80\x94,后者当且仅当一个值绑定到...中的名称时rho。每一个案件都由 专门处理substituteList

\n

处理的多样性R_DotsSymbol是这些 R 语句给出不同结果的原因:

\n
f0 <- function() substitute(...(n = 1)); f0()\n## ...(n = 1)\nf1 <- function(...) substitute(...(n = 1)); f1()\n## $n\n## [1] 1\ng0 <- function() {... <- quote(x); substitute(...(n = 1))}; g0()\n## Error in g0() : \'...\' used in an incorrect context\ng1 <- function(...) {... <- quote(x); substitute(...(n = 1))}; g1()\n## Error in g1() : \'...\' used in an incorrect context\nh0 <- function() {... <- NULL; substitute(...(n = 1))}; h0()\n## $n\n## [1] 1\nh1 <- function(...) {... <- NULL; substitute(...(n = 1))}; h1()\n## $n\n## [1] 1\n
Run Code Online (Sandbox Code Playgroud)\n

考虑到如何...(n = 1)解析,您可能期望f1return call("...", n = 1)、bothg0g1return call("x", n = 1)、bothh0h1抛出错误,但由于上述原因,情况并非如此,主要是未记录的原因。

\n

内部结构

\n

当在 R 函数内部调用时f

\n
f <- function(...) substitute(...(<exprs>))\n
Run Code Online (Sandbox Code Playgroud)\n

substitute评估对 C 实用程序do_substitute\xe2\x80\x94 的调用,您可以通过查看此处\xe2\x80\x94 来了解这一点,其中argList获取LISTSXP形式的 a pairlist(x, R_MissingArg),其中xLANGSXP形式的 a pairlist(R_DotsSymbol, <exprs>)()。

\n

如果你关注 的正文,那么你会发现传递给fromdo_substitute的值是 a的形式( source )。tsubstituteListdo_substituteLISTSXPpairlist(copy_of_x)

\n

由此可见,调用 ( sourcewhile )内部的循环恰好有一次迭代,并且循环 ( source )主体中的语句是substituteListCAR(el) == R_DotsSymbolfalse位于该迭代中。

\n

false条件分支 ( source ) 中,h获取值\n pairlist(substituteList(copy_of_x, env))。循环退出并substituteList返回hdo_substitute,后者又返回CAR(h)到 R (源1 , 2 , 3)。

\n

因此, 的返回值substitutesubstituteList(copy_of_x, env),并且仍然需要推断出 this 的身份SEXP在对的调用内部substituteListwhile循环进行了1+m迭代,其中m是 的数量<exprs>CAR(el) == R_DotsSymbol在第一次迭代中,循环体中的语句是true是。

\n

true条件分支 ( source ) 中,h是 aDOTSXPR_MissingArg,因为fhas...作为形式参数 ( doc )。继续,你会发现substituteList返回:

\n
    \n
  • R_NilValue如果hR_MissingArg在第一次while迭代中并且 m = 0,
  • \n
\n

或者,否则,

\n
    \n
  • a列出(if在第一次迭代中为 a )LISTSXP中的表达式,后跟(if ),全部未计算且没有替换,因为 的执行环境在调用时为空。hhDOTSXPwhile<exprs>m > 1fsubstitute
  • \n
\n

的确:

\n
f <- function(...) substitute(...())\nis.null(f())\n## [1] TRUE\nf <- function(...) substitute(...(n = 1))\nidentical(f(a = sin(x), b = zzz), pairlist(a = quote(sin(x)), b = quote(zzz), n = 1))\n## [1] TRUE\n
Run Code Online (Sandbox Code Playgroud)\n

杂项

\n

FWIW,它帮助我在添加一些打印语句到coerce.c. 例如,我之前在( sourceUNPROTECT(3); )的正文中添加了以下内容:do_substitute

\n
    Rprintf("CAR(t) == R_DotsSymbol? %d\\n",\n            CAR(t) == R_DotsSymbol);\n    if (TYPEOF(CAR(t)) == LISTSXP || TYPEOF(CAR(t)) == LANGSXP) {\n        Rprintf("TYPEOF(CAR(t)) = %s, length(CAR(t)) = %d\\n",\n                type2char(TYPEOF(CAR(t))), length(CAR(t)));\n        Rprintf("CAR(CAR(t)) = R_DotsSymbol? %d\\n",\n                CAR(CAR(t)) == R_DotsSymbol);\n        Rprintf("TYPEOF(CDR(CAR(t))) = %s, length(CDR(CAR(t))) = %d\\n",\n                type2char(TYPEOF(CDR(CAR(t)))), length(CDR(CAR(t))));\n    }\n    if (TYPEOF(s) == LISTSXP || TYPEOF(s) == LANGSXP) {\n        Rprintf("TYPEOF(s) = %s, length(s) = %d\\n",\n                type2char(TYPEOF(s)), length(s));\n        Rprintf("TYPEOF(CAR(s)) = %s, length(CAR(s)) = %d\\n",\n                type2char(TYPEOF(CAR(s))), length(CAR(s)));\n    }\n
Run Code Online (Sandbox Code Playgroud)\n

这帮助我确认了上一行的通话内容substituteList

\n
f <- function(...) substitute(...(n = 1))\ninvisible(f(hello, world, hello(world)))\n
Run Code Online (Sandbox Code Playgroud)\n
CAR(t) == R_DotsSymbol? 0\nTYPEOF(CAR(t)) = language, length(CAR(t)) = 2\nCAR(CAR(t)) = R_DotsSymbol? 1\nTYPEOF(CDR(CAR(t))) = pairlist, length(CDR(CAR(t))) = 1\nTYPEOF(s) = pairlist, length(s) = 1\nTYPEOF(CAR(s)) = pairlist, length(CAR(s)) = 4\n
Run Code Online (Sandbox Code Playgroud)\n
invisible(substitute(...()))\n
Run Code Online (Sandbox Code Playgroud)\n
CAR(t) == R_DotsSymbol? 0\nTYPEOF(CAR(t)) = language, length(CAR(t)) = 1\nCAR(CAR(t)) = R_DotsSymbol? 1\nTYPEOF(CDR(CAR(t))) = NULL, length(CDR(CAR(t))) = 0\nTYPEOF(s) = pairlist, length(s) = 1\nTYPEOF(CAR(s)) = language, length(CAR(s)) = 1\n
Run Code Online (Sandbox Code Playgroud)\n

显然,使用调试符号编译 R 并在调试器下运行 R 也有帮助。

\n

另一个谜题

\n

刚刚注意到这个奇怪的现象:

\n
g <- function(...) substitute(...(n = 1), new.env())\ngab <- g(a = sin(x), b = zzz)\ntypeof(gab)\n## [1] "language"\ngab\n## ...(n = 1)\n
Run Code Online (Sandbox Code Playgroud)\n

这里有人可以进行另一次深入研究,以找出当您提供不同于(包括)时结果是 aLANGSXP而不是 a的原因。LISTSXPenvenvironment()env = NULL

\n

  • 在这种情况下,“argList”获取“do_substitute”内部的值“pairlist(R_DotsSymbol, R_MissingArg)”,而不是“pairlist(pairlist(R_DotsSymbol, &lt;exprs&gt;), R_MissingArg)”。(事实上​​,“...”本身是一个“SYMSXP”,而“...()”是一个“LANGSXP”,因此“do_substitute”在这里表现一致。)因此,“substituteList”返回到“do_substitute” `LISTSXP`pairlist(a, b)`,并且`do_substitute`返回给R`SYMSXP``a`。 (2认同)