Как использовать функцию замещающего знака 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(...). Однако эти два не делают точно того же самого. (Для различий посмотрите примеры в ответах.) Кто-либо может сказать мне, что практическое различие между ними, и какой я должен использовать?

175
задан steamer25 14 February 2013 в 16:38
поделиться

4 ответа

Я читал ответы и комментарии и вижу, что некоторые вещи не были упомянуты:

  1. data.frame использует версию list (...) . Фрагмент кода:

     объект <- as.list (replace (list (...))) [- 1L]
    mrn <- is.null (имена строк)
    x <- список (...)
    

    объект используется для волшебства с именами столбцов, но x используется для создания окончательного data.frame .
    Для использования неоцененного аргумента ... посмотрите код write.csv , где используется match.call .

  2. Как вы пишете в комментарии, результат в ответе Дирка не является списком списков. Список длиной 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)
    
  3. Как и выше, мы можем использовать 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 (...) .

109
ответ дан 23 November 2019 в 20:27
поделиться

Просто добавлю к ответам Шейна и Дирка: интересно сравнить

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 , хотя первый явно проще.

31
ответ дан 23 November 2019 в 20:27
поделиться

Вы можете преобразовать многоточие в список с помощью 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 () ). Однако не рекомендуется использовать ... , когда вы знаете каждый параметр заранее, поскольку он добавляет некоторую двусмысленность и дальнейшее усложнение строки аргумента (и делает сигнатуру функции неясной для любого другого пользователя. ). Список аргументов - важная часть документации для пользователей функций.

В противном случае это также полезно в тех случаях, когда вы хотите передать параметры в подфункцию, не раскрывая их все в своих собственных аргументах функции. Это можно отметить в документации по функциям.

37
ответ дан 23 November 2019 в 20:27
поделиться

Вы уже дали половину ответа. Рассмотрим

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 из вызова и преобразовал их в список. Разве вы не об этом просили?

14
ответ дан 23 November 2019 в 20:27
поделиться
Другие вопросы по тегам:

Похожие вопросы: