Как мне вызвать функцию "функция"? - программирование

Как мне вызвать функцию "функция"?

Я пытаюсь вызвать функцию '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'?


Просто чтобы уточнить: существуют тривиальные обходные пути (в этом вопросе перечислено два, а по крайней мере третий). Но я хотел бы понять, как это (не) работает.

4b9b3361

Ответ 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

Немного покопавшись в исходном коде, вот несколько замечаний:

  1. Фактическое создание функции выполняется mkCLOSXP(). Это то, что вызывается function() {}, as.function.default() и .Primitive("function") (иначе 'function')

  2. as.function.default() направляется в do_asfunction(), которая также вызывает CheckFormals(). Тем не менее, он напрямую строит эти формалы на несколько строк выше.

  3. Как вы указали, другое место, где CheckFormals() находится внутри do_function(). Тем не менее, я не думаю, что do_function() .Primitive("function") чем-либо, кроме .Primitive("function"), поэтому это единственная ситуация, когда CheckFormals() вызывается для пользовательского ввода.

  4. 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"), вы не сталкиваетесь с этой ситуацией ни с какими другими примерами.