R语言 语法名称不使用字符串引号的原因是什么?

1l5u6lss  于 2023-06-03  发布在  其他
关注(0)|答案(3)|浏览(135)

bounty还有3天到期。此问题的答案有资格获得+50声望奖励。GKi希望引起更多关注这个问题。

我已经为syntactic namename of a function使用了双引号,并得到了一个注解 * 使用反引号 。这里我得到了一个评论, 将函数名作为字符串传递给match.fun(因此*apply函数或do.call)是完全可以的。

A <- matrix(1:4, 2)
B <- matrix(4:1, 2)
apply(A, 2, `*`, B)  # Works: backtick quotes
apply(A, 2, "*", B)  # Works: double quotes
apply(A, 2, '*', B)  # Works: single quotes
# apply(A, 2, *, B)  # Error: unexpected '*' in "apply(A, 2, *"

`%x%` <- function(lhs, rhs) lhs * rhs  # Works: backtick quotes
"%x%" <- function(lhs, rhs) lhs * rhs  # Works: double quotes
'%x%' <- function(lhs, rhs) lhs * rhs  # Works: single quotes
# %x% <- function(lhs, rhs) lhs * rhs  # Error: unexpected SPECIAL in "%x%"

我想知道函数名使用单引号'或双引号"而不是反引号```有什么缺点?在哪些情况下应使用哪种报价类型?

smtd7mpg

smtd7mpg1#

Hadley威克姆的Advanced R的第2.2.1节:
您还可以使用单引号或双引号创建非语法绑定(例如"_abc" <- 1)而不是反引号,但不应该这样做,因为必须使用不同的语法来检索值。在赋值箭头的左手使用字符串的能力是一个历史性的人工制品,在R支持反引号之前使用。
?Quotes(我的粗体):
标识符由一系列字母、数字、句点(.)和下划线组成。它们不能以数字或下划线开头,也不能以句点后跟数字开头。保留字不是有效的标识符。
...
这样的标识符也被称为语法名称,并且可以直接在R代码中使用。几乎总是可以使用其他名称,只要它们被引用。首选的引号是反引号(' ` '),deparse通常会使用它,但在许多情况下,可以使用单引号或双引号(因为字符常量通常会转换为名称)。反引号可能必不可少的一个地方是在公式中分隔变量名:参见公式。

piztneat

piztneat2#

必须强调的是

`*`

"*"

本身是两个不同的对象:前者是一个函数,而后者只是一个特征向量。使用一个或另一个是否有任何区别,这取决于用例。因为在大多数情况下,当你将一个函数传递给另一个函数时,通常会调用match.fun(这发生在*applydo.call和基本上任何接受函数作为参数的基函数上),传递任何一个对象都没有任何区别。但是,如果您使用外部包或其他源中的某些函数,则无法确保执行了对match.fun的调用。例如,假设你有这个函数:

ex_fun<-function(a, b, FUN) {
    return(FUN(a, b))
}

这个方法的作用是:

ex_fun(1,3,`*`)
#[1] 3

这不会:

ex_fun(1,3,"*")
#Error in FUN(a, b) : could not find function "FUN"

另一点:R中的一切都是函数,甚至赋值。所以当你使用像这样的东西:

var <- value

解析器将上述指令转换为:

`<-`(var, value)

在这个函数中,对as.symbol的隐式调用是在var上执行的,因此下面的语法是有效的:

"foo" <- 3 * 3
foo
#[1] 9

但是和以前一样,foo"foo"仍然是不同的对象。
match.fun的另一个隐式用法发生在调用函数时。当我们像对待函数一样对待符号时,表达式的求值会查找函数而不是泛型对象。例如:

a <- 4
a(2)
#Error in a(2) : could not find function "a"

错误消息很清楚:这并不是因为a不是一个函数对象,而是因为一个名为a的模式函数对象不存在。例如,我们可以声明像基函数一样命名的对象,并且解析器仍然会在调用时调用该函数:

log <- 7
log(2) 
#[1] 0.6931472
log
#[1] 7

当解析器理解我们正在调用一个函数时,它调用match.fun。这个方法的作用是:

"*"(3, 4)
#[1] 12

当然,这不会:

FUN <- "*"
FUN(3, 4)
#Error in FUN(3, 4) : could not find function "FUN"
hof1towb

hof1towb3#

使用双引号会给静态分析带来挑战。

getParseData(parse(text = 'x <- 1; x(1); c(x = 1)'))
#>    line1 col1 line2 col2 id parent                token terminal text
#> 7      1    1     1    6  7      0                 expr    FALSE     
#> 1      1    1     1    1  1      3               SYMBOL     TRUE    x
#> 3      1    1     1    1  3      7                 expr    FALSE     
#> 2      1    3     1    4  2      7          LEFT_ASSIGN     TRUE   <-
#> 4      1    6     1    6  4      5            NUM_CONST     TRUE    1
#> 5      1    6     1    6  5      7                 expr    FALSE     
#> 6      1    7     1    7  6      0                  ';'     TRUE    ;
#> 19     1    9     1   12 19      0                 expr    FALSE     
#> 10     1    9     1    9 10     12 SYMBOL_FUNCTION_CALL     TRUE    x
#> 12     1    9     1    9 12     19                 expr    FALSE     
#> 11     1   10     1   10 11     19                  '('     TRUE    (
#> 13     1   11     1   11 13     14            NUM_CONST     TRUE    1
#> 14     1   11     1   11 14     19                 expr    FALSE     
#> 15     1   12     1   12 15     19                  ')'     TRUE    )
#> 20     1   13     1   13 20      0                  ';'     TRUE    ;
#> 34     1   15     1   22 34      0                 expr    FALSE     
#> 23     1   15     1   15 23     25 SYMBOL_FUNCTION_CALL     TRUE    c
#> 25     1   15     1   15 25     34                 expr    FALSE     
#> 24     1   16     1   16 24     34                  '('     TRUE    (
#> 26     1   17     1   17 26     34           SYMBOL_SUB     TRUE    x
#> 27     1   19     1   19 27     34               EQ_SUB     TRUE    =
#> 28     1   21     1   21 28     29            NUM_CONST     TRUE    1
#> 29     1   21     1   21 29     34                 expr    FALSE     
#> 30     1   22     1   22 30     34                  ')'     TRUE    )

# backticks don't change the way it's parsed
getParseData(parse(text = '`x` <- 1; `x`(1); c(`x` = 1)')) 
#>    line1 col1 line2 col2 id parent                token terminal text
#> 7      1    1     1    8  7      0                 expr    FALSE     
#> 1      1    1     1    3  1      3               SYMBOL     TRUE  `x`
#> 3      1    1     1    3  3      7                 expr    FALSE     
#> 2      1    5     1    6  2      7          LEFT_ASSIGN     TRUE   <-
#> 4      1    8     1    8  4      5            NUM_CONST     TRUE    1
#> 5      1    8     1    8  5      7                 expr    FALSE     
#> 6      1    9     1    9  6      0                  ';'     TRUE    ;
#> 19     1   11     1   16 19      0                 expr    FALSE     
#> 10     1   11     1   13 10     12 SYMBOL_FUNCTION_CALL     TRUE  `x`
#> 12     1   11     1   13 12     19                 expr    FALSE     
#> 11     1   14     1   14 11     19                  '('     TRUE    (
#> 13     1   15     1   15 13     14            NUM_CONST     TRUE    1
#> 14     1   15     1   15 14     19                 expr    FALSE     
#> 15     1   16     1   16 15     19                  ')'     TRUE    )
#> 20     1   17     1   17 20      0                  ';'     TRUE    ;
#> 34     1   19     1   28 34      0                 expr    FALSE     
#> 23     1   19     1   19 23     25 SYMBOL_FUNCTION_CALL     TRUE    c
#> 25     1   19     1   19 25     34                 expr    FALSE     
#> 24     1   20     1   20 24     34                  '('     TRUE    (
#> 26     1   21     1   23 26     34           SYMBOL_SUB     TRUE  `x`
#> 27     1   25     1   25 27     34               EQ_SUB     TRUE    =
#> 28     1   27     1   27 28     29            NUM_CONST     TRUE    1
#> 29     1   27     1   27 29     34                 expr    FALSE     
#> 30     1   28     1   28 30     34                  ')'     TRUE    )

# quotes do, and it can mess with static analysis
getParseData(parse(text = '"x" <- 1; "x"(1); c("x" = 1)'))
#>    line1 col1 line2 col2 id parent                token terminal text
#> 7      1    1     1    8  7      0                 expr    FALSE     
#> 1      1    1     1    3  1      3            STR_CONST     TRUE  "x"
#> 3      1    1     1    3  3      7                 expr    FALSE     
#> 2      1    5     1    6  2      7          LEFT_ASSIGN     TRUE   <-
#> 4      1    8     1    8  4      5            NUM_CONST     TRUE    1
#> 5      1    8     1    8  5      7                 expr    FALSE     
#> 6      1    9     1    9  6      0                  ';'     TRUE    ;
#> 19     1   11     1   16 19      0                 expr    FALSE     
#> 10     1   11     1   13 10     12            STR_CONST     TRUE  "x"
#> 12     1   11     1   13 12     19                 expr    FALSE     
#> 11     1   14     1   14 11     19                  '('     TRUE    (
#> 13     1   15     1   15 13     14            NUM_CONST     TRUE    1
#> 14     1   15     1   15 14     19                 expr    FALSE     
#> 15     1   16     1   16 15     19                  ')'     TRUE    )
#> 20     1   17     1   17 20      0                  ';'     TRUE    ;
#> 34     1   19     1   28 34      0                 expr    FALSE     
#> 23     1   19     1   19 23     25 SYMBOL_FUNCTION_CALL     TRUE    c
#> 25     1   19     1   19 25     34                 expr    FALSE     
#> 24     1   20     1   20 24     34                  '('     TRUE    (
#> 26     1   21     1   23 26     34            STR_CONST     TRUE  "x"
#> 27     1   25     1   25 27     34               EQ_SUB     TRUE    =
#> 28     1   27     1   27 28     29            NUM_CONST     TRUE    1
#> 29     1   27     1   27 29     34                 expr    FALSE     
#> 30     1   28     1   28 30     34                  ')'     TRUE    )

另一方面,双引号/单引号给予了更好的跟踪,在一些旧版本的R中,下面的漂亮版本也更快。

f <- function (...) {
  stop (...)
}
ugly <- function(x, ...) {
  do.call(f, list("!",  "!" ))
}
ugly()
traceback()
# 4: stop(...) at #2
# 3: (function (...) 
# {
#     stop(...)
# })("!", "!")
# 2: do.call(f, list("!", "!")) at #2
# 1: ugly()

pretty <- function(x, ...) {
  do.call("f", list("!",  "!" ))
}
pretty()
traceback()
# 4: stop(...) at #2
# 3: f("!", "!")
# 2: do.call("f", list("!", "!")) at #2
# 1: pretty()

创建于2023-06-03带有reprex v2.0.2

  • 只使用反引号你会更一致
  • 但在我看来,"this that" <- 1实际上看起来更好,因为你可以看到一些时髦的事情正在发生
  • dplyr文档c("x_a" = "y_a", "x_b" = "y_b")用于连接,它更对称
  • 这主要是品味问题

我们在这里讨论过:https://twitter.com/antoine_fabri/status/1579863982294601728

相关问题