在R语言中,括号后面跟着括号是什么意思?

6

使用scales::label_percent()语法在变异函数中的方式很不寻常,因为它使用了双括号:

label_percent()(an_equation_goes_here)

我以前没有见过R中的()()语法,也不知道如何查找它,因为我不知道它叫什么。我尝试了?`()()`??`()()`,但都没有帮助。双括号语法叫什么?有人可以推荐一个阅读它的地方吗?

以下是一个示例以供参考:

library(tidyverse)
members <- 
  read_csv(
    paste0(
      "https://raw.githubusercontent.com/rfordatascience/tidytuesday/", 
      "master/data/2020/2020-09-22/members.csv"
    ), 
    show_col_types = FALSE)


members %>%
  count(success, died) %>%
  group_by(success) %>%
  # old syntax:
  # mutate(percent = scales::percent(n / sum(n))) 
  # new syntax:
  mutate(percent = scales::label_percent()(n / sum(n))) 
#> # A tibble: 4 × 4
#> # Groups:   success [2]
#>   success died      n percent
#>   <lgl>   <lgl> <int> <chr>  
#> 1 FALSE   FALSE 46452 98%    
#> 2 FALSE   TRUE    868 2%     
#> 3 TRUE    FALSE 28961 99%    
#> 4 TRUE    TRUE    238 1%

使用reprex v2.0.2于2023年1月1日创建


3
除了 @r2evans 的出色回答外,您可能会发现《高级 R》中的“函数工厂”章节(https://adv-r.hadley.nz/function-factories.html)很有趣。 - zephryl
像往常一样,获取一份《R地狱》的副本。 - Carl Witthoft
3个回答

10

大多数函数都返回一个值,无论是原子类型的(数值、整数、字符),类似于列表的(包括data.frame)还是更复杂的东西。对于那些函数,单一的()圆括号集合(正如你所认识到的)是为了一次调用而设计的。

然而,偶尔地,一个函数调用会返回一个函数。例如,如果我们查看?scales::label_percent,我们可以往下滚动到

Value:

     All 'label_()' functions return a "labelling" function, i.e. a
     function that takes a vector 'x' and returns a character vector of
     'length(x)' giving a label for each input value.

让我们来逐步看:

fun <- scales::label_percent()
fun
# function (x) 
# {
#     number(x, accuracy = accuracy, scale = scale, prefix = prefix, 
#         suffix = suffix, big.mark = big.mark, decimal.mark = decimal.mark, 
#         style_positive = style_positive, style_negative = style_negative, 
#         scale_cut = scale_cut, trim = trim, ...)
# }
# <bytecode: 0x00000168ee5440e8>
# <environment: 0x00000168ee5501b8>
fun(0.35)
# [1] "35%"

第一次调用 scales::label_percent() 返回了一个函数,然后我们可以使用该函数并传入任意多的参数。

如果您不想将返回的函数存储在变量中,比如fun,您可以直接在第一组括号后面加上另一组括号来立即使用它。

scales::label_percent()(0.35)
# [1] "35%"

一个相关的问题是,“为什么要让一个函数返回另外一个函数?”有许多风格上的原因,但在 scales::label_* 这个例子中,它们被设计用于需要将选项表达为函数而不是静态值的地方。例如,在 ggplot 代码中可以使用它:轴标记通常通过简单的启发式确定其数量、位置和呈现方式来方便地放置。虽然可以使用 ggplot2::scale_*_manual(values = ...) 手动控制它们的数量、位置和外观,但通常更方便的方法是不预先关注数量或位置,并且在使用分面时,这些标记可能会因不同的分面变量而不同,因此无法轻松地分配给静态变量。在这种情况下,通常最好分配一个函数,该函数给出一些简单的参数(例如轴的最小/最大值),并且该函数返回有意义的东西。

为什么我们不能只传递 scales::label_percent 呢?(好问题。)即使您在这里调用默认值,也可能想要更改任何或所有可控制的事物,例如:

  • suffix= 的默认值为 "%",但也许您想要一个空格,如 " %"
  • decimal.mark= 的默认值为 ".",但也许您的语言环境更喜欢逗号?

虽然有可能为所有这些选项的组合提供多个函数,但通常在长远来看,提供一个“模板函数”用于创建该函数会更容易,例如:

fun <- scales::label_percent(accuracy = 0.01, suffix = " %", decimal.mark = ",")
fun(0.353)
# [1] "35,30 %"
scales::label_percent(accuracy = 0.01, suffix = " %", decimal.mark = ",")(0.353)
# [1] "35,30 %"

3
再次给出了非常棒的答案! - TarJae
2
那是一个非常棒的答案。你写过R语言的书吗?如果没有,请写一本。如果你有机会来迈阿密,晚餐我请。 - itsMeInMiami

1
如果我们运行一个函数并且函数返回的值本身是一个函数,那么我们也可以调用其中一个函数。
例如,我们首先使用f()运行f,将返回值分配给g,但返回值本身是一个函数,因此g是一个函数--它是函数function() 3--我们也可以运行它。
# f is a function which returns a function
f <- function() function() 3  

g <- f()  # this runs f which returns `function() 3`
g()  # thus g is a function so we can call it
## [1] 3

现在将所有内容放在一起,我们可以将其写成一行:

f()()
## [1] 3

如您所见,() 只有一个意义,而当有两个 () 并排在一起时,那只是因为我们在调用函数并返回其结果。


1
一个在圆括号中带有参数列表的表达式被称为 R 中的函数调用
连续两个函数调用不需要有特殊的名称,它们仍然只是函数调用。

谢谢@jörg。我的困惑不在于嵌套的函数调用,而是两组相邻的括号没有中缀运算符。明白吗? - itsMeInMiami
我不明白。这里没有嵌套的函数调用。 - Jörg W Mittag

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接