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

时间:2010-06-16 21:31:42

标签: r function parameters ellipsis variadic

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(...)。但是,这两者并没有完全相同。 (有关差异,请参阅答案中的示例。)任何人都能告诉我它们之间的实际区别是什么,我应该使用哪一个?

5 个答案:

答案 0 :(得分:108)

我阅读了答案和评论,我发现很少有事情没有提到:

  1. data.frame使用list(...)版本。代码片段:

    object <- as.list(substitute(list(...)))[-1L]
    mrn <- is.null(row.names)
    x <- list(...)
    

    object用于对列名称进行一些魔术,但x用于创建最终data.frame
    要使用未评估的...参数,请查看使用write.csv的{​​{1}}代码。

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

    substitute

    第一个元素没有名称,这在Dirk答案中是List of 4 $ : symbol list $ a: language 1:10 $ b: language 11:20 $ c: language 21:30 。我使用以下方法实现了这个结果:

    [[1]]
  3. 如上所述,我们可以使用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 )。

  4. 如果您想使用write.csv,那么您应该在{Shane的回答中...使用它。

答案 1 :(得分:36)

您可以将省略号转换为包含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()的示例所示)。但是,当您事先知道每个参数时,使用...并不是一个好主意,因为它会给参数字符串增加一些歧义和进一步的复杂性(并使任何其他用户都不清楚函数签名)。参数列表是功能用户的重要文档。

否则,它对于您希望将参数传递给子函数而不将它们全部暴露在您自己的函数参数中的情况也很有用。这可以在功能文档中注明。

答案 2 :(得分:30)

只是添加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中都适合您的用途,但第一个版本显然更简单。

答案 3 :(得分:13)

你已经给出了一半答案。考虑

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> 

因此,从调用中获取了两个参数ab并将其转换为列表。那不是你要的吗?

答案 4 :(得分:5)

这可以按预期工作。 以下是交互式会话:

> 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!
>

正如您所看到的,如果在特定情况下默认值不是您想要的,则可以使用此函数将“额外”参数传递给函数中的函数。