Как мне вызвать функцию "функция"?
Я пытаюсь вызвать функцию 'function'
чтобы определить функцию в коде R.
Как мы все знаем ™, 'function'
- это .Primitive
используется внутри R для определения функций, когда пользователь использует обычный синтаксис, т.е.
mean1 = function (x, ...) base::mean(x, ...)
Но ничто не мешает мне назвать этот примитив напрямую. Или я так думал. Я могу напрямую вызывать другие примитивы (и даже переопределять их; например, в момент безумия я отверг Rs, встроенный 'for'
). Так что это в принципе возможно.
И все же я не могу заставить его работать на 'function'
. Вот что я попробовал:
# Works
mean2 = as.function(c(formals(mean), quote(mean(x, ...))))
# Works
mean3 = eval(call('function', formals(mean), quote(mean(x, ...))))
# Error: invalid formal argument list for "function"
mean4 = 'function'(formals(mean), quote(mean(x, ...)))
Тот факт, что mean3
в частности работает, показывает мне, что mean4
должен работать. Но это не так. Зачем?
Я проверил определение примитива 'function'
в источнике R. do_function
определена в eval.c
И я вижу, что он вызывает CheckFormals
, который гарантирует, что каждый аргумент является символом, и это не удается. Но почему это проверяет, и что это значит?
И самое главное: есть ли способ прямого вызова примитива 'function'
?
Просто чтобы уточнить: существуют тривиальные обходные пути (в этом вопросе перечислено два, а по крайней мере третий). Но я хотел бы понять, как это (не) работает.
Ответы
Ответ 1
Это потому, что function
является специальным примитивом:
typeof('function')
#> [1] "special"
Аргументы не оцениваются, поэтому вы фактически передаете quote(formals(mean))
вместо значения formals(mean)
. Я не думаю, что есть способ вызова function
напрямую без оценочных уловок, кроме как с пустым списком формалей, который просто NULL
.
Ответ 2
Для полноты картины Лайонелс намекает на способ вызова 'function'
конце концов. К сожалению, это довольно ограниченно, поскольку мы не можем передать ни одно определение аргумента, кроме NULL
:
mean5 = 'function'(NULL, mean(x, ...))
formals(mean5) = formals(mean)
(Обратите внимание на отсутствие цитирования по всему телу!)
Это, конечно, совершенно непрактично (и formals<-
внутренне вызывает as.function
любом случае.)
Ответ 3
Немного покопавшись в исходном коде, вот несколько замечаний:
-
Фактическое создание функции выполняется mkCLOSXP(). Это то, что вызывается function() {}
, as.function.default()
и .Primitive("function")
(иначе 'function'
)
-
as.function.default()
направляется в do_asfunction(), которая также вызывает CheckFormals(). Тем не менее, он напрямую строит эти формалы на несколько строк выше.
-
Как вы указали, другое место, где CheckFormals()
находится внутри do_function()
. Тем не менее, я не думаю, что do_function()
.Primitive("function")
чем-либо, кроме .Primitive("function")
, поэтому это единственная ситуация, когда CheckFormals()
вызывается для пользовательского ввода.
-
CheckFormals()
действительно правильно проверяет объект pairlist
.
Вы можете проверить последнюю точку самостоятельно, запустив части функции CheckFormals()
с помощью inline::cfunction
inline::cfunction( c(x="ANY"),
'Rprintf("is list?: %d\\nTag1 OK?: %d\\nTag2 OK?: %d\\nTag3 NULL?: %d\\n",
isList(x), TYPEOF(TAG(x)) == SYMSXP, TYPEOF(TAG(CDR(x))) == SYMSXP,
CDR(CDR(x)) == R_NilValue); return R_NilValue;' )( formals(mean) )
# is list?: 1
# Tag1 OK?: 1
# Tag2 OK?: 1
# Tag3 NULL?: 1
Таким образом, где-то между вами, передавая formals(means)
в .Primitive("function")
и он CheckFormals()
в CheckFormals()
помощью do_function()
, аргумент теряет свою действительность. (Я не знаю источника R достаточно хорошо, чтобы рассказать вам, как это происходит.) Однако, поскольку do_function()
вызывается только .Primitive("function")
, вы не сталкиваетесь с этой ситуацией ни с какими другими примерами.