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.frame函数定义中的相关行是object <- 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)
Edit
似乎有两种方法可以做到这一点。他们as.list(替代(列表(…)))[1 l]和列表(…)。然而,这两者做的事情并不完全相同。(关于差异,请参见答案中的例子。)谁能告诉我它们之间的实际区别是什么,我应该用哪一个?
我看了回答和评论,发现有几件事没有被提到:
data.frame uses list(...) version. Fragment of the code:
object <- as.list(substitute(list(...)))[-1L]
mrn <- is.null(row.names)
x <- list(...)
object is used to do some magic with column names, but x is used to create final data.frame.
For use of unevaluated ... argument look at write.csv code where match.call is used.
As you write in comment result in Dirk answer is not a list of lists. Is a list of length 4, which elements are language type. First object is a symbol - list, second is expression 1:10 and so on. That explain why [-1L] is needed: it removes expected symbol from provided arguments in ... (cause it is always a list).
As Dirk states substitute returns "parse tree the unevaluated expression".
When you call my_ellipsis_function(a=1:10,b=11:20,c=21:30) then ... "creates" a list of arguments: list(a=1:10,b=11:20,c=21:30) and substitute make it a list of four elements:
List of 4
$ : symbol list
$ a: language 1:10
$ b: language 11:20
$ c: language 21:30
First element doesn't have a name and this is [[1]] in Dirk answer. I achieve this results using:
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)
As above we can use str to check what objects are in a function.
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
It's ok. Lets see substitute version:
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
Isn't what we needed. You will need additional tricks to deal with these kind of objects (as in write.csv).
如果你想用…那么你应该像在Shane answer中那样使用它,by list(…)
这与预期的一样。
以下为互动环节:
> talk <- function(func, msg, ...){
+ func(msg, ...);
+ }
> talk(cat, c("this", "is", "a","message."), sep=":")
this:is:a:message.
>
相同,除了有一个默认参数:
> talk <- function(func, msg=c("Hello","World!"), ...){
+ func(msg, ...);
+ }
> talk(cat,sep=":")
Hello:World!
> talk(cat,sep=",", fill=1)
Hello,
World!
>
正如你所看到的,如果在特定情况下默认值不是你想要的,你可以使用这个来在你的函数中传递'额外'参数。