【问题标题】:How do I call the `function` function?如何调用`function`函数?
【发布时间】:2019-02-08 16:39:41
【问题描述】:

我正在尝试调用函数 `function` 在 R 代码中定义一个函数。

众所周知™️,`function`是一个.Primitive,当用户使用常规语法时,R 在内部使用它来定义函数,即

mean1 = function (x, ...) base::mean(x, ...)

但是没有什么能阻止我直接调用那个原语。 我是这么想的。我可以直接调用其他原语(甚至重新定义它们;例如,在疯狂的时刻I overrode R’s builtin `for`)。所以这原则上是可能的。

但我无法让它为 `function` 工作。这是我尝试过的:

# Works
mean2 = as.function(c(formals(mean), quote(mean(x, ...))))

# Works
mean3 = eval(call('function', formals(mean), quote(mean(x, ...))))

# Error: invalid formal argument list for "function"
mean4 = `function`(formals(mean), quote(mean(x, ...)))

mean3 特别有效的事实向我表明mean4 应该有效。但事实并非如此。为什么?

我检查了 R 源代码中 `function` 原语的定义。 do_function is defined in eval.c。我看到它调用了CheckFormals,它确保每个参数都是一个符号,但它失败了。但它为什么要检查这个,这是什么意思?

最重要的是:有没有办法直接调用`function` 原语?


澄清一下:有一些简单的解决方法(这个问题列出了两个,至少还有第三个)。但我想了解这(不)是如何工作的。

【问题讨论】:

标签: r


【解决方案1】:

这是因为function 是一个特殊的原语:

typeof(`function`)
#> [1] "special"

没有评估参数,因此您实际上传递了quote(formals(mean)) 而不是formals(mean) 的值。我认为没有评估技巧就可以直接调用function,除非有一个空的形式列表,它只是NULL

【讨论】:

  • 我实际上尝试过引用形式(以及使用alist),但正如您所注意到的,这也不起作用。 typeof(`for`) 也很“特殊”,但手动调用`for` 可以工作。
  • 是的,因为for 接受一个符号和两个表达式,其中有解析器语法。没有明确的解析器语法来创建对列表文字,这是 function() 所采用的。
  • 确实如此。我刚刚发现您可以使用手动生成的pairlist 调用它——但又一次只能间接调用它,因为正如您所说,没有pairlist 文字语法。
【解决方案2】:

为了完整起见,Lionel 的回答毕竟暗示了一种调用 `function` 的方式。不幸的是,它相当受限制,因为除了NULL,我们不能传递任何参数定义:

mean5 = `function`(NULL, mean(x, ...))
formals(mean5) = formals(mean)

(请注意正文周围没有引用!)

这当然是完全不切实际的(无论如何formals<- 内部调用as.function。)

【讨论】:

    【解决方案3】:

    在对源代码进行了一点挖掘之后,这里有一些观察:

    1. 实际的函数创建由mkCLOSXP()完成。这就是 function() {}as.function.default().Primitive("function")(又名`function`)所调用的内容

    2. as.function.default() 被路由到do_asfunction(),即also calls CheckFormals()。但是,它直接在上面几行构造了这些形式。

    3. 正如您所指出的,另一个调用 CheckFormals() 的地方是在 do_function() 内部。但是,我认为do_function() 不会被任何 .Primitive("function") 调用,因此这是在用户输入时调用CheckFormals() 的唯一情况。

      李>
    4. CheckFormals() 确实正确地验证了 pairlist 对象。

    您可以通过使用inline::cfunction 运行CheckFormals() 函数的部分内容自己检查最后一点

    inline::cfunction( c(x="ANY"),
      'Rprintf("is list?: %d\\nTag1 OK?: %d\\nTag2 OK?: %d\\nTag3 NULL?: %d\\n",
         isList(x), TYPEOF(TAG(x)) == SYMSXP, TYPEOF(TAG(CDR(x))) == SYMSXP,
         CDR(CDR(x)) == R_NilValue); return R_NilValue;' )( formals(mean) )
    
    # is list?: 1
    # Tag1 OK?: 1
    # Tag2 OK?: 1
    # Tag3 NULL?: 1
    

    因此,在您将formals(means) 传递给.Primitive("function") 和它被do_function() 转发给CheckFormals() 之间的某个地方,该参数将失去其有效性。 (我不太了解 R 源代码,无法告诉您这是如何发生的。)但是,由于 do_function() 仅由 .Primitive("function") 调用,因此您不会在任何其他示例中遇到这种情况。

    【讨论】:

    • 你的推理是正确的。我认为谜题中缺少的部分是 functionnames.c 中的声明:与您自己的 C 函数不同,do_function 不会被评估参数调用;取而代之的是,它使用 unevalated 参数调用,因此传递的不是pairlist,而是一个引用的表达式,可以计算为pairlist。
    • @KonradRudolph:你认为这是有意的行为吗? do_function`function`.Primitive("function") 都不能在内部使用(由grep 确定)。那么,如果用户不能直接调用它,该函数是为谁设计的呢?
    • 函数.Primitive("function") 是在您将解析器创建的对function 的调用评估为解释器创建的函数对象时调用的。请注意,这两种对象的打印方式相同,这可能会造成混淆。检查quote(function(foo, bar) NULL) 和未引用版本之间的差异。您还可以使用.Internal(inspect(quote(function(foo, bar) NULL))) 验证解析器在对function 的调用中正确创建了形式列表。
    猜你喜欢
    • 1970-01-01
    • 2021-06-12
    • 1970-01-01
    • 1970-01-01
    • 2011-01-11
    • 2020-05-17
    • 1970-01-01
    • 2012-03-09
    • 2019-09-17
    相关资源
    最近更新 更多