Язык 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 (...)
. Фрагмент кода:
объект <- as.list (replace (list (...))) [- 1L]
mrn <- is.null (имена строк)
x <- список (...)
объект
используется для волшебства с именами столбцов, но x
используется для создания окончательного data.frame
.
Для использования неоцененного аргумента ...
посмотрите код write.csv
, где используется match.call
.
Как вы пишете в комментарии, результат в ответе Дирка не является списком списков. Список длиной 4 элемента, элементы которого относятся к типу язык
. Первый объект - это символ
- список
, второй - выражение 1:10
и так далее. Это объясняет, почему необходим [- 1L]
: он удаляет ожидаемый символ
из предоставленных аргументов в ...
(потому что это всегда список).
Как заявляет Дирк , замена
возвращает «дерево синтаксического анализа неоцененного выражения».
Когда вы вызываете my_ellipsis_function (a = 1: 10, b = 11: 20, c = 21: 30)
, тогда ...
«создает» список аргументов: list (a = 1: 10, b = 11: 20, c = 21: 30)
и replace
делают его списком из четырех элементов:
Список из 4
$: список символов
$ a: язык 1:10
$ b: язык 11:20
$ c: язык 21:30
У первого элемента нет имени, это [[1]]
в ответе Дирка. Я добиваюсь этого результата с помощью:
my_ellipsis_function <- function (...) {
input_list <- as.list (replace (list (...)))
str (список_входов)
НУЛЕВОЙ
}
my_ellipsis_function (a = 1:10, b = 11:20, c = 21:30)
Как и выше, мы можем использовать str
, чтобы проверить, какие объекты находятся в функции.
my_ellipsis_function <- function (...) {
input_list <- список (...)
output_list <- lapply (X = input_list, function (x) {str (x); summary (x)})
возврат (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
интервал [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
$ а
Мин. 1st Qu. Среднее значение 3-го кв. Максимум.
1,00 3,25 5,50 5,50 7,75 10,00
$ млрд
Мин. 1st Qu. Среднее значение 3-го кв. Максимум.
11,0 13,2 15,5 15,5 17,8 20,0
$ c
Мин. 1st Qu. Среднее значение 3-го кв. Максимум.
21,0 23,2 25,5 25,5 27,8 30,0
Это нормально. Давайте посмотрим, что заменит
версию:
my_ellipsis_function <- function (...) {
input_list <- as.list (replace (list (...)))
output_list <- lapply (X = input_list, function (x) {str (x); summary (x)})
возврат (output_list)
}
my_ellipsis_function (a = 1:10, b = 11:20, c = 21:30)
список символов
язык 1:10
язык 11:20
язык 21:30
[[1]]
Режим класса длины
1 имя имя
$ а
Режим класса длины
3 звонок звонок
$ млрд
Режим класса длины
3 звонок звонок
$ c
Режим класса длины
3 звонок звонок
Это не то, что нам нужно. Для работы с такими объектами вам потребуются дополнительные приемы (например, write.csv
).
Если вы хотите использовать ...
, вы должны использовать его, как в ответе Шейна, в list (...)
.
Просто добавлю к ответам Шейна и Дирка: интересно сравнить
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
, хотя первый явно проще.
Вы можете преобразовать многоточие в список с помощью list ()
, а затем выполнять с ним операции:
> test.func <- function(...) { lapply(list(...), class) }
> test.func(a="b", b=1)
$a
[1] "character"
$b
[1] "numeric"
Итак, ваша функция get_list_from_ellipsis
не что иное, как список
.
Допустимый вариант использования для этого - случаи, когда вы хотите передать неизвестное количество объектов для операции (как в вашем примере c ()
или data.frame ()
). Однако не рекомендуется использовать ...
, когда вы знаете каждый параметр заранее, поскольку он добавляет некоторую двусмысленность и дальнейшее усложнение строки аргумента (и делает сигнатуру функции неясной для любого другого пользователя. ). Список аргументов - важная часть документации для пользователей функций.
В противном случае это также полезно в тех случаях, когда вы хотите передать параметры в подфункцию, не раскрывая их все в своих собственных аргументах функции. Это можно отметить в документации по функциям.
Вы уже дали половину ответа. Рассмотрим
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
из вызова и преобразовал их в список. Разве вы не об этом просили?