How to use R's ellipsis feature when writing your own function?
R语言有一个很好的功能,用于定义可以采用可变数量参数的函数。例如,函数
1 2 3 4 5 | > 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 |
函数的签名包括省略号,如下所示:
1 2 3 4 5 | function (..., row.names = NULL, check.rows = FALSE, check.names = TRUE, stringsAsFactors = default.stringsAsFactors()) { [FUNCTION DEFINITION HERE] } |
我想编写一个类似的函数,获取多个值并将它们合并为一个返回值(以及进行一些其他处理)。为了做到这一点,我需要弄清楚如何从函数的函数参数中"解包"
那么如何将省略号从函数的签名转换为例如列表呢?
更具体地说,如何在下面的代码中编写
1 2 3 4 5 6 7 | 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) |
编辑
似乎有两种可能的方法来做到这一点。它们是
我阅读了答案和评论,我发现很少有东西没有提到:
1 2 3 | object <- as.list(substitute(list(...)))[-1L] mrn <- is.null(row.names) x <- list(...) |
要使用未评估的
当您在评论结果中写入Dirk时,答案不是列表。是长度为4的列表,哪些元素是
由于Dirk声明
当你调用
1 2 3 4 5 | List of 4 $ : symbol list $ a: language 1:10 $ b: language 11:20 $ c: language 21:30 |
第一个元素没有名称,这在Dirk答案中是
1 2 3 4 5 6 | 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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 |
没关系。让我们看看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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 |
不是我们需要的。您将需要其他技巧来处理这些类型的对象(如
如果你想使用
您可以将省略号转换为带有
1 2 3 4 5 6 7 | > test.func <- function(...) { lapply(list(...), class) } > test.func(a="b", b=1) $a [1]"character" $b [1]"numeric" |
所以你的
一个有效的用例是在你想要传入未知数量的对象进行操作的情况下(如
否则,它对于您希望将参数传递给子函数而不将它们全部暴露在您自己的函数参数中的情况也很有用。这可以在功能文档中注明。
只是为了补充Shane和Dirk的回答:比较有趣
1 2 3 4 5 6 7 8 9 10 11 | get_list_from_ellipsis1 <- function(...) { list(...) } get_list_from_ellipsis1(a = 1:10, b = 2:20) # returns a list of integer vectors $a [1] 1 2 3 4 5 6 7 8 9 10 $b [1] 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
同
1 2 3 4 5 6 7 8 9 10 11 | get_list_from_ellipsis2 <- function(...) { as.list(substitute(list(...)))[-1L] } get_list_from_ellipsis2(a = 1:10, b = 2:20) # returns a list of calls $a 1:10 $b 2:20 |
就目前而言,任何一个版本在
你已经给出了一半答案。考虑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | R> my_ellipsis_function <- function(...) { + input_list <- as.list(substitute(list(...))) + } R> print(my_ellipsis_function(a=1:10, b=2:20)) [[1]] list $a 1:10 $b 11:20 R> |
所以这从调用中获取了两个参数
这按预期工作。
以下是交互式会话:
1 2 3 4 5 6 | > talk <- function(func, msg, ...){ + func(msg, ...); + } > talk(cat, c("this","is","a","message."), sep=":") this:is:a:message. > |
相同,除了默认参数:
1 2 3 4 5 6 7 8 9 | > talk <- function(func, msg=c("Hello","World!"), ...){ + func(msg, ...); + } > talk(cat,sep=":") Hello:World! > talk(cat,sep=",", fill=1) Hello, World! > |
如您所见,如果默认值不是您在特定情况下所需的值,则可以使用此参数将"额外"参数传递给函数中的函数。