подставить в базу R имена аргументов для вызова функции - PullRequest
6 голосов
/ 08 марта 2020

Конечная цель в вопросе - построить следующий неоцененный вызов с использованием вычислений на языке, где list, a_name и 50L предоставляется из параметров.

list(a_name = 50L)

Что внутренне выглядит как

str(quote(list(a_name = 50L)))
# language list(a_name = 50L)

str(as.list(quote(list(a_name = 50L))))
#List of 2
# $       : symbol list
# $ a_name: int 50

Я помещу свои переменные в список, чтобы следующий код был чище.

params = list(my_fun = as.name("list"), my_name = "a_name", my_value = 50L)

# What I tried so far?

# 1. The first thing that one would try

substitute(my_fun(my_name = my_value),
           params)
#list(my_name = 50L) ## `my_name` was not substituted!

# 2. Workaround get the same output, but only after `setNames` call evaluation, so doesn't really answer the question about constructing specific call

substitute(setNames(my_fun(my_value), my_name), ## alternatively could be `structure(..., names=my_name)`
           params)
#setNames(list(50L), "a_name")

# 3. Another workaround, not really computing on the language but parsing, and integer L suffix is gone!

with(expr = parse(text=paste0(my_fun, "(", my_name, " = ", my_value, ")"))[[1L]],
     data = params)
#list(a_name = 50)

# 4. Working example using rlang

with(expr = rlang::call2(my_fun, !!my_name := my_value),
     data = params)
#list(a_name = 50L)

Есть ли способ в базе для создания требуемого вызова? В основном, чтобы получить точно такой же вывод, как и в rlang, но с использованием базы .

Обратите внимание, что этот Вопрос не является дубликатом этого , который строго запрашивал решение rlang. В этом Вопросе спрашивается, как этого добиться, используя базу . Если нет способа добиться этого, я бы тоже хотел это знать. Спасибо.

1 Ответ

6 голосов
/ 08 марта 2020

Предполагается, что вопрос заключается в том, как создать объект call, функция которого является первым аргументом params, имя единственного аргумента которого является значением второго компонента params, а значением аргумента является третий компонент из params тогда c2 является этим объектом вызова. Мы проверяем, что созданный объект cakl идентичен объекту вызова rlang, показанному в вопросе.

c2 <- as.call(setNames(params, c("", "", params2$my_name))[-2])

# verify that c2 equals the output of the rlang example in question
c1 <- with(expr = rlang::call2(my_fun, !!my_name := my_value), data = params)

identical(c1, c2)
## [1] TRUE

Это обобщает так, что если у вызова есть больше аргументов, так что params имеет длину n + 2 и второй компонент из params - это вектор имен, длина которого равна n, тогда он все еще работает.

Если у вас есть контроль над вводом, я думаю, что было бы более целесообразно определить список ввода как имеющий один компонент для функции name и один компонент для каждого аргумента, а имена компонентов являются именами аргументов (в отличие от отдельного аргумента для хранения имен). В этом случае мы могли бы просто написать следующее. На самом деле то, что сделано выше, это преобразование параметров в эту форму, а затем использование as.call, чтобы мы могли также предоставить эту форму с самого начала, если это возможно.

as.call(params2)  # params2[[1]] is func name, params2[-1] is named args

Примечание

Для выполнения вызов просто использовать:

eval(c2)

или мы могли бы использовать do.call:

do.call(as.character(params[[1]]), setNames(tail(params, -2), params[[2]]))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...