Ответ 1
Здесь приведен пример того, почему ...()
работает так, как он делает. Я напишу более подробную информацию и ссылки позже, но это касается ключевых моментов.
-
Прежде чем выполнять замену на любой из своих компонентов,
substitute()
сначала анализирует оператор R. -
...()
анализирует объект вызова, тогда как...
анализирует объект имени. -
...
- это специальный объект, предназначенный только для использования в вызовах функций. Как следствие, код C, реализующий подстановку, принимает специальные меры для обработки...
, когда он найден в объекте вызова. Подобные меры предосторожности не принимаются, когда...
встречается как символ. (Соответствующий код находится в функцияхdo_substitute
,substitute
иsubstituteList
(особенно последние два) вR_SRCDIR/src/main/coerce.c
.)
Итак, роль ()
в ...()
состоит в том, чтобы заставить оператор анализироваться как объект вызова (aka language), чтобы подстановка возвращала полностью расширенное значение точек. Может показаться удивительным, что ...
заменяется даже тогда, когда он находится снаружи ()
, но: (a) вызовы хранятся внутри как объекты, подобные спискам, и (b) соответствующий код C, кажется, не делает различий между первым элементом этого списка и последующими.
Просто обратите внимание: для изучения поведения substitute
или классов различных объектов мне полезно настроить небольшую песочницу, например:
f <- function(...) browser()
f(a = 4, 77, B = "char")
## Then play around within the browser
class(quote(...)) ## quote() parses without substituting
class(quote(...()))
substitute({...})
substitute(...(..., X, ...))
substitute(2 <- (makes * list(no - sense))(...))