小编典典

编写自己的函数时如何使用 R 的省略号功能?

all

R
语言有一个很好的特性来定义可以接受可变数量参数的函数。例如,该函数data.frame采用任意数量的参数,每个参数成为结果数据表中一列的数据。示例用法:

> data.frame(letters=c("a", "b", "c"), numbers=c(1,2,3), notes=c("do", "re", "mi"))
  letters numbers notes
1       a       1    do
2       b       2    re
3       c       3    mi

该函数的签名包括一个省略号,如下所示:

function (..., row.names = NULL, check.rows = FALSE, check.names = TRUE, 
    stringsAsFactors = default.stringsAsFactors()) 
{
    [FUNCTION DEFINITION HERE]
}

我想编写一个执行类似操作的函数,获取多个值并将它们合并为一个返回值(以及进行一些其他处理)。为了做到这一点,我需要弄清楚如何...从函数内的函数参数中“解包”。我不知道该怎么做。函数定义中的相关行data.frameobject <- as.list(substitute(list(...)))[-1L],我无法理解。

那么如何将函数签名中的省略号转换为例如列表?

更具体地说,我如何get_list_from_ellipsis在下面的代码中编写?

my_ellipsis_function(...) {
    input_list <- get_list_from_ellipsis(...)
    output_list <- lapply(X=input_list, FUN=do_something_interesting)
    return(output_list)
}

my_ellipsis_function(a=1:10,b=11:20,c=21:30)

编辑

似乎有两种可能的方法可以做到这一点。它们是as.list(substitute(list(...)))[-1L]list(...)。但是,这两个做的事情并不完全相同。(有关差异,请参见答案中的示例。)谁能告诉我它们之间的实际区别是什么,我应该使用哪一个?


阅读 83

收藏
2022-07-02

共1个答案

小编典典

我阅读了答案和评论,发现没有提到几件事:

  1. data.frame使用list(...)版本。代码片段:
    object <- as.list(substitute(list(...)))[-1L]
    

    mrn <- is.null(row.names)
    x <- list(…)

object用于对列名做一些魔术,但x用于创建 final data.frame
要使用未评估的...参数,请查看使用的write.csv代码match.call

  1. 正如您在评论结果中写的那样,Dirk 的答案不是列表。是一个长度为 4 的列表,其中元素是language类型。第一个对象是symbol- list,第二个是表达式1:10,依此类推。这解释了为什么[-1L]需要:它symbol从提供的参数中删除了预期...(因为它始终是一个列表)。
    正如 Dirk 所说substitute,返回“解析树未评估的表达式”。
    当您调用my_ellipsis_function(a=1:10,b=11:20,c=21:30)then
    ...“创建”参数列表时:list(a=1:10,b=11:20,c=21:30)substitute使其成为四个元素的列表:
    List of 4
    

    $ : symbol list
    $ a: language 1:10
    $ b: language 11:20
    $ c: language 21:30

第一个元素没有名称,这是[[1]]Dirk 的答案。我使用以下方法实现了这个结果:

    my_ellipsis_function <- function(...) {
  input_list <- as.list(substitute(list(...)))
  str(input_list)
  NULL
}
my_ellipsis_function(a=1:10,b=11:20,c=21:30)
  1. 如上所述,我们可以str用来检查函数中的对象。
    my_ellipsis_function <- function(...) {
    input_list <- list(...)
    output_list <- lapply(X=input_list, function(x) {str(x);summary(x)})
    return(output_list)
    

    }
    my_ellipsis_function(a=1:10,b=11:20,c=21:30)
    int [1:10] 1 2 3 4 5 6 7 8 9 10
    int [1:10] 11 12 13 14 15 16 17 18 19 20
    int [1:10] 21 22 23 24 25 26 27 28 29 30
    $a
    Min. 1st Qu. Median Mean 3rd Qu. Max.
    1.00 3.25 5.50 5.50 7.75 10.00
    $b
    Min. 1st Qu. Median Mean 3rd Qu. Max.
    11.0 13.2 15.5 15.5 17.8 20.0
    $c
    Min. 1st Qu. Median Mean 3rd Qu. Max.
    21.0 23.2 25.5 25.5 27.8 30.0

没关系。来看substitute版本:

       my_ellipsis_function <- function(...) {
       input_list <- as.list(substitute(list(...)))
       output_list <- lapply(X=input_list, function(x) {str(x);summary(x)})
       return(output_list)
   }
   my_ellipsis_function(a=1:10,b=11:20,c=21:30)
    symbol list
    language 1:10
    language 11:20
    language 21:30
   [[1]]
   Length  Class   Mode 
        1   name   name 
   $a
   Length  Class   Mode 
        3   call   call 
   $b
   Length  Class   Mode 
        3   call   call 
   $c
   Length  Class   Mode 
        3   call   call

不是我们需要的。您将需要额外的技巧来处理这些类型的对象(如 中write.csv)。

如果你想使用...,那么你应该像 Shane 的回答那样使用它,by list(...)

2022-07-02