【问题标题】:Precedence of a function call in RR中函数调用的优先级
【发布时间】:2014-07-04 13:09:44
【问题描述】:

operator precedence 的标准 R 帮助页面上,它们不包括函数调用,在我看来这似乎相当草率。这给我带来了一些问题,所以我决定对substitute 进行反复试验,发现优先级似乎介于[[^ 之间:

> substitute(a^b())[[1]]
`^`
> substitute(a[b]())[[1]]
a[b]

在中缀表示法中,它们是(^ a (b ()))(([ a b) ())(将调用运算符表示为())。简而言之,第一个示例显示指数函数在参数ab() 上被调用,而在第二个示例中,最终结果是对函数a[b] 的调用。

这种优先级是否适用于所有情况?函数调用的优先级不是恒定的,这似乎很奇怪,但如果它确实是恒定的,它就不会包含在上述帮助页面中是没有意义的。

【问题讨论】:

    标签: r operator-precedence function-calls


    【解决方案1】:

    函数调用的优先级常量

    在底层,R 非常像 lisp。

    It has SEXPs 喜欢lisp; SEXP 是一个元组(列表),其中元组的第一个元素 ([[1]]) 是运算符,其余元素(通常是其他 SEXP)是运算符的参数。

    当你写作时

    paste("a",1 + 2)
    

    R 明白

    (`paste`,"a",(`+`, 1, 2))
    

    当您运行替换时,您将获得 SEXP(尽管它们像 R 代码一样漂亮地打印出来),并且(最外层)SEXP 的第一个元素是 last 运算符,它将应用于表达式 - 即最低优先级。

    您可能知道,您可以使用以下方式查看表达式的各个部分:

    > str(as.list(quote(a^b())))
    List of 3
     $ : symbol ^
     $ : symbol a
     $ : language b()
    

    将此理解应用于您的示例中的优先级。

    a^b() 的最后一个运算符是什么?

    让我们逐步考虑

    1. 替换并评估a
    2. 替换并评估b
    3. 不带参数地评估(步骤的结果)2(这称为调用)
    4. 替换并评估^
    5. 使用参数13 评估4

    所以最后一个运算符是名为^的值

    接下来,a[b]()的最后一个运算符是什么?

    1. 替换并评估a
    2. 替换并评估b
    3. 替换并评估[
    4. 使用参数(步骤结果)1 和(步骤结果)2 评估(步骤结果)3
    5. 评估(步骤的结果)4

    在这种情况下(步骤的结果)4 具有方便的名称 a[b]

    因此,最后一个运算符是对a[b] 的调用(不带参数的评估)。


    编辑:警告

    我在这里简化了实际情况,因为由于 R 的特殊性,函数参数作为未计算的(环境,表达式)对传递给函数(运算符),(而不是通过引用或值),而'commit' 的顺序与上面的大致相同,真正的调度顺序实际上是相反的——甚至错过了步骤。不过,您现在不必担心。

    【讨论】:

    • 在第一个问题的表达式中取出多余的右括号,但也很想取出 [[1]],因为表达式中并没有。你同意吗?
    【解决方案2】:

    也许不是“优先”问题,而是解析问题。 (但仔细想想,这似乎是优先级,并且是由于需要完成 "[" 和 "]" 之间的所有参数的参数匹配。)在第一个实例中,解析树构造为:

                `^`
                /  \
               a    b
    
    > substitute(a^b())[1]
    `^`()
    > substitute(a^b())[[1]]
    `^`
    > substitute(a^b())[[2]]
    a
    > substitute(a^b())[[3]]
    b()
    

    在第二种情况下,它被构造为

                 a[b]
                /
               NULL
    

    但第一个元素也有一个结构:

                `[`
                / \
               a   b
    
    > substitute(a[b]())[[1]][[1]]
    `[`
    > substitute(a[b]())[[1]][[2]]
    a
    > substitute(a[b]())[[1]][[3]]
    b
    

    我认为由于两个函数(^[)可能会出现歧义,只有后者可以实际提供函数,因此需要先对其进行处理。 a^b 的求值结果永远不可能是一个函数,所以将其处理为 ^(a, b() )

    当真正让这样的东西真正起作用时,我认为第二个不是很有用。为了从工作区中提取和替换,您需要一个额外的提取步骤:

    b <- list(mean)
    > eval( substitute(a^b(1:10) , list(a=2) ))
    Error in eval(expr, envir, enclos) : could not find function "b"
    > eval( substitute(a^b[[1]](1:10) , list(a=2) ))
    [1] 45.25483
    

    按照@hadley 的建议,我从 pryr 复制了他的 ast 函数,它的配套函数 call_tree 位于 draw_tree.r 模块的 the pryr repository at github 中。我需要这样做,因为我在路上,我的笔记本电脑仍然停留在没有 pryr 二进制文件的过时 R 版本。还需要安装和加载 pkg:stringr 以获取str_c

    这样我们就可以看出区别了:

    ast(a[b]())
    \- ()
     \- ()
      \- `[
      \- `a
      \- `b 
    ast(a^b())
    \- ()
     \- `^
     \- `a
     \- ()
      \- `b 
    

    漂亮的@hadley。

    【讨论】:

    • pryr::ast()自动绘制树
    猜你喜欢
    • 2021-08-10
    • 1970-01-01
    • 2020-06-29
    • 1970-01-01
    • 1970-01-01
    • 2013-03-16
    • 2011-12-29
    • 1970-01-01
    • 2019-10-01
    相关资源
    最近更新 更多