如何在编写自己的函数时使用R的省略号(ellipsis)功能?
如何在编写自己的函数时使用R的省略号(ellipsis)功能?
R语言有一个很棒的特性,可以定义可以接受可变数量参数的函数。例如,函数data.frame
可以接受任意数量的参数,每个参数都成为结果数据表中的一列数据。示例用法:\n
> 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
\n该函数的签名中包含省略号,如下所示:\n
function (..., row.names = NULL, check.rows = FALSE, check.names = TRUE, stringsAsFactors = default.stringsAsFactors()) { [函数定义在此] }
\n我想编写一个类似的函数,将多个值合并为单个返回值(还要进行一些其他处理)。为了做到这一点,我需要弄清楚如何在函数内部“展开”...
的函数参数。我不知道如何做到这一点。在data.frame
函数定义中相关的一行是object <- as.list(substitute(list(...)))[-1L]
,我无法理解它的意思。\n那么我如何将函数签名中的省略号转换为一个列表呢?\n更具体地说,我如何在下面的代码中编写get_list_from_ellipsis
函数?\n
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)
\n
\n
编辑
\n似乎有两种可能的方法来做到这一点。它们是as.list(substitute(list(...)))[-1L]
和list(...)
。然而,这两种方法并不完全相同。(有关区别,请参见答案中的示例。)有人能告诉我它们之间的实际区别是什么,并且我应该使用哪个?
在编写自己的函数时,使用R的省略号功能的原因是为了能够接受任意数量的参数,并根据需要进行处理。省略号允许函数接受任意数量的参数,而不需要提前指定参数的个数。
解决方法之一是使用...
作为函数的参数,在函数内部使用...
来表示传递给函数的参数。这样,函数就可以接受任意数量的参数,并在需要时进行处理。
下面是使用省略号功能的示例代码:
get_list_from_ellipsis1 <- function(...) { list(...) } get_list_from_ellipsis1(a = 1:10, b = 2:20) # 返回一个整数向量的列表 $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
另一种方法是使用substitute()
函数来获取传递给函数的参数的表达式,并将其转换为列表。这样,函数将返回一个包含参数表达式的列表。
下面是使用substitute()
函数的示例代码:
get_list_from_ellipsis2 <- function(...) { as.list(substitute(list(...)))[-1L] } get_list_from_ellipsis2(a = 1:10, b = 2:20) # 返回一个调用列表 $a 1:10 $b 2:20
以上两种方法在my_ellipsis_function
中都可以使用,但第一种方法更简单。
在R中,有一个特殊的特性叫做ellipsis(省略号),它允许函数接受未知数量的参数。然而,如何正确地使用这个特性却是一个需要注意的问题。
你可以使用list()
将ellipsis转换为一个列表,然后对它进行操作。例如,可以定义一个函数test.func
,它将ellipsis转换为列表,并返回每个参数的类型:
test.func <- function(...) { lapply(list(...), class) } test.func(a="b", b=1)
上述代码将返回一个列表,其中包含参数a和b的类型。这说明get_list_from_ellipsis
函数其实就是list()
函数的别名。
这种使用ellipsis的情况通常出现在需要接受未知数量参数的操作中,比如c()
或data.frame()
函数。然而,如果你已经知道每个参数的数量,使用ellipsis会使参数字符串变得模糊和复杂,同时也会让函数的签名对其他用户不清晰。函数的参数列表对于用户来说是重要的文档。
另外,当你希望将参数传递给子函数而又不想在自己的函数参数中暴露它们时,使用ellipsis也非常有用。这可以在函数的文档中进行说明。
虽然我知道如何使用ellipsis将参数传递给子函数,但R的原始函数也经常使用我所描述的方式。事实上,list()
和c()
函数都是这样工作的,但它们都是原始函数,所以我无法轻松地查看它们的源代码以理解它们的工作原理。
rbind.data.frame
函数也是使用这种方式。
如果list(...)
已经足够使用,为什么R内置函数如data.frame
却使用更长的形式as.list(substitute(list(...)))[-1L]
?
作为一个外部用户,我并不清楚这个问题的答案(不过我相信肯定有一个很好的原因)。在我的自定义包中,我使用list()
来实现这个目的,并且还没有遇到问题。
在R中,当我们编写自己的函数时,可以使用R的省略号功能(ellipsis feature)。省略号允许我们在函数中接受任意数量的参数,并将它们作为列表进行处理。然而,在使用省略号时,有一些需要注意的问题和解决方法。
首先,我们需要注意的是,在使用`data.frame`函数时,它使用了`list(...)`版本的省略号。代码片段如下:
object <- as.list(substitute(list(...)))[-1L] mrn <- is.null(row.names) x <- list(...)
其中,`object`用于处理列名的一些操作,而`x`则用于创建最终的`data.frame`。
其次,我们需要注意的是,在Dirk的回答中提到的结果并不是一个列表的列表,而是一个长度为4的列表,其元素为`language`类型。第一个对象是一个`symbol` - `list`,第二个是表达式`1:10`,依此类推。这解释了为什么需要使用`[-1L]`:它从提供的`...`参数中删除了预期的`symbol`(因为它始终是一个列表)。我们可以通过以下代码来实现这个结果:
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)
这样做是可以的。但是,如果我们使用`substitute`版本,就会出现问题。我们需要额外的技巧来处理这些对象(就像在`write.csv`中一样)。
当我们想要使用省略号时,应该像Shane的回答中那样使用`list(...)`。