R: почему предоставление списка для ellipsis (), когда многоточие является последним аргументом, не работает?

Я писал функцию, которая использует многоточие (также известное как ...). Он позволяет вам указать переменное количество дополнительных аргументов. Я хотел предоставить список с аргументами в качестве дополнительного аргумента. Ниже приведен воспроизводимый пример:

f <- function(..., a =1, b = 2){
  l <- list(...)
  print(l)
}
f(list(a = 2))

[[1]]
[[1]]$a
[1] 2

Целью предоставления дополнительных аргументов в списке было избежать конфликта имен (функция внутри f могла принимать аргумент с именем a, и я хотел обеспечить возможность его предоставления).

При изменении реализации я заметил, что перемещение многоточия на последнее место в объявлении функции возвращает другой результат (а именно пустой список):

g <- function(a =1, b = 2, ...){
  l <- list(...)
  print(l)
}
g(list(a = 2))

list()

Из любопытства я добавил к обеим функциям печать аргументов по умолчанию:

f <- function(..., a =1, b = 2){
  l <- list(...)
  print(l)
  print(c(a = a, b = b))
}
g <- function(a =1, b = 2, ...){
  l <- list(...)
  print(l)
  print(c(a = a, b = b))
}

f(list(a = 2)) # results of calling f
[[1]]
[[1]]$a
[1] 2


a b 
1 2

g(list(a = 2)) # results of calling g
list()
$a.a
[1] 2

$b
[1] 2

Итак, первая функция (f) вернула предполагаемый результат, но вторая (g) проигнорировала (?) Аргумент по умолчанию a и каким-то образом изменила список, предоставленный благодаря многоточию.

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


person kaksat    schedule 23.03.2018    source источник


Ответы (3)


Аргументы работают в R так, что, когда вы не называете свой первый аргумент, R просто предполагает, что это входит в первый аргумент функции. Это верно для второго, третьего и т. Д. Аргументов в списке аргументов, ЗА ИСКЛЮЧЕНИЕМ для аргументов, которые идут после ... - потому что R не знает, сколько аргументов вы собираетесь поместить в ..., если вы хотите изменить значение по умолчанию для что бы ни было после этого, вы должны назвать это.

Итак, в вашем случае, когда вы вызываете функцию f(), объект list(a=2) переходит в .... Но в g() тот же объект переходит в. Единственный способ получить что-то в ..., когда это помещено в конец списка аргументов без включения аргументов для a и b, - это назвать это чем-то, кроме a или b, например g (c = список (a = 1)).

person iod    schedule 23.03.2018

Я думаю, что функция работает должным образом, если вы не используете list в вызове функции: f(a=2) и g(a=2) оба вернут list() значение переменной l.

Поскольку вы передаете список в качестве аргумента, он рассматривается как безымянная переменная и присваивается первому формальному параметру, который отличается для f и g.

Все было бы иначе, если бы вы сделали do.call(f, list(a=2)) и do.call(g, list(a=2)). В этом случае значение 2 будет присвоено ожидаемому формальному параметру a.

person Karsten W.    schedule 23.03.2018
comment
Я думаю, что @kaksat хочет, чтобы аргумент НЕ входил в a, а скорее в .... - person iod; 23.03.2018

Краткий ответ на ваш вопрос Означает ли это, что передача списка в качестве дополнительного аргумента возможна только в том случае, если многоточие является первым аргументом в вызове функции? - Нет, если вы следуете правильному названию или положению, когда вы передаете значения во время вызова функции, у вас не будет проблем с некорректным сопоставлением аргументов.

Лучше давать имя аргументу (по крайней мере, из второго аргумента) в вызове функции, чтобы правильное сопоставление имен имело желаемый эффект.

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

Обратите внимание на порядок имен аргументов в выводе на печать. Он соответствует порядку аргументов, определенному в функции. Этот правильный порядок будет иметь место только в том случае, если вы правильно укажете аргументы.

При правильном именовании аргументов не нужно беспокоиться о положении аргументов. Вы можете передать ему что угодно. Это не обязательно должно быть list.

f <- function(..., a =1, b = 2){
  mc <- match.call(expand.dots = TRUE )
  print(names(mc))
}
g <- function(a =1, b = 2, ...){
  mc <- match.call(expand.dots = TRUE )
  print(names(mc))
}

f(c1 = list(z = 2, f = 5), a = 1, b = 2)  # [1] ""   "c1" "a"  "b" 
f(a = 1, c1 = list(z = 2, f = 5), b = 2)  # [1] ""   "c1" "a"  "b"
f(a = 1, c1 = list(z = 2, f = 5), b = 2, c2 = 4) # ""   "c1" "c2" "a"  "b" 
f(c1 = list(z = 2, f = 5), 1, 2, 4) # [1] ""   "c1" ""   ""   ""  


g(c1 = list(z = 2, f = 5), a = 1, b = 2)  # [1] ""   "a"  "b"  "c1"
g(a = 1, c1 = list(z = 2, f = 5), b = 2)  # [1] ""   "a"  "b"  "c1"
g(a = 1, c1 = list(z = 2, f = 5), b = 2, c2 = 4) # [1] ""   "a"  "b"  "c1" "c2"
g(c1 = list(z = 2, f = 5), 1, 2, 4) # [1] ""   "a"  "b"  "c1" ""
person Sathish    schedule 23.03.2018