在编写自己的函数时如何使用R的省略号功能?
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)
编辑
看来有两种可能的方法来做到这一点。 它们是as.list(substitute(list(...)))[-1L]
和list(...)
。 但是,这两者并不完全一样。 (有关差异,请参阅答案中的示例。)有谁能告诉我他们之间的实际区别是什么,以及我应该使用哪一个?
我读了答案和评论,我看到有几件事没有提到:
-
data.frame
使用list(...)
版本。 代码片段:object <- as.list(substitute(list(...)))[-1L] mrn <- is.null(row.names) x <- list(...)
object
被用来做一些列名称的魔法,但是x
被用来创建最终的data.frame
。
要使用未write.csv
参数,请查看使用match.call
write.csv
代码。 -
当你在评论结果中写Dirk答案不是列表的列表。 是长度为4的列表,哪些元素是
language
类型。 第一个对象是一个symbol
list
,第二个是表达式1:10
等等。 这就解释了为什么需要[-1L]
:它从…中提供的参数中删除预期的symbol
(因为它总是一个列表)。
如德克州所substitute
回报“解析树的未评价的表达”。
当你调用my_ellipsis_function(a=1:10,b=11:20,c=21:30)
那么...
“创建”一列参数: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
第一个元素没有名字,这是Dirk答案中的
[[1]]
。 我使用以下方法实现了这一结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)
-
如上所述,我们可以使用
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
)。
如果你想使用...
那么你应该使用它在沙恩的答案,按list(...)
。
您可以使用list()
将省略号转换为列表,然后对其执行操作:
> test.func <- function(...) { lapply(list(...), class) } > test.func(a="b", b=1) $a [1] "character" $b [1] "numeric"
所以你的get_list_from_ellipsis
函数不过是list
。
这是一个有效的用例,在这种情况下,你想传入未知数量的对象进行操作(如你的c()
或data.frame()
例子)。 但是,当你事先知道每个参数时,使用...
不是一个好主意,因为它会给参数字符串增加一些模糊性和进一步的复杂性(并且使函数签名对其他用户不清楚)。 参数列表是功能用户的重要文档。
否则,对于想要将参数传递给子函数而不将它们全部暴露在自己的函数参数中的情况,这也是有用的。 这可以在功能文档中注明。
只是为了增加Shane和Dirk的回应:比较有趣
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
同
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
就目前而言,在my_ellipsis_function
,哪个版本都适合您的目的,尽管第一个版本显然更简单。
你已经给出了一半的答案。 考虑
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>
所以这个从电话里拿了两个参数a
和b
并把它转换成一个列表。 那不是你要求的吗?
这按预期工作。 以下是互动会议:
> 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! >
正如你所看到的,如果在特定情况下默认值不是你想要的,你可以使用它来将'额外'参数传递给函数中的函数。
我想这是你所期待的(下图)。 a1,a2,a3,a4是一些任意的向量,'catt'是取任意数量的参数并返回输入参数的连接向量的函数。