изменить имена аргументов внутри функции r

Я пытаюсь настроить имена аргументов внутри функции. Я хочу создать процедуру, которая берет тело функции, ищет x, заменяет каждый x на x0, а затем восстанавливает функцию. к тому, что было раньше. Чтобы привести пример:

f = function(x, y) -x^2 + x + -y^2 + y
# Take old names
form_old = names(formals(f))
# Make new names
form_new = paste0(form_old, 0)
# Give f new formals
formals(f) = setNames(vector("list", length(form_new)), form_new)
# Copy function body
bod = as.list(body(f))
for (i in 1:length(form_new)) {
  bod = gsub(form_old[i], form_new[i], bod)
}
# return from list to call ?
body(f) = as.call(list(bod))
f(1, 1) # produces an error

Пока что этот код изменит имена всех переменных с x на x0 и с y на y0. Однако окончательный вывод bod — это вектор символов, а не вызов. Как я могу теперь изменить это обратно на звонок?

Заранее спасибо!


person Vandenman    schedule 22.11.2015    source источник
comment
Ctrl+F не режет его для вас?   -  person MichaelChirico    schedule 22.11.2015
comment
@MichaelChirico Я создаю функцию, которая выводит полиномы Тейлора (см. en.wikipedia.org/wiki /Taylor_series#Definition). Итак, я ввожу функцию, то есть exp(x), и она должна выводить функцию вроде exp(x0) + exp(x0) * (x - x0). В промежутках происходит что-то с производными, и где-то приходится переименовывать переменные. Я делаю автоматизированную процедуру (поэтому она работает для любой функции ввода) Ctrl + F не поможет.   -  person Vandenman    schedule 22.11.2015


Ответы (2)


Конечно, есть лучший способ сделать то, что вы пытаетесь сделать, не требующий изменения функций. При этом вы определенно не хотите заменять переменные регулярными выражениями, которые могут иметь всевозможные проблемы. Как правило, попытка манипулировать кодом как строками приводит к проблемам, например, функция типа tricky <- function(x, y) { tst <- "x + y"; -xx*x + yy*y }, где строки и имена переменных перекрываются, приведет к неправильным результатам.

Вот функция, которая использует рекурсивный подход (Recall) для обхода дерева выражений (рекурсии можно избежать, используя структуру типа "стек", но мне это кажется более сложным).

## Function to replace variables in function body
## expr is `body(f)`, keyvals is a lookup table for replacements
rep_vars <- function(expr, keyvals) {
    if (!length(expr)) return()
    for (i in seq_along(expr)) {
        if (is.call(expr[[i]])) expr[[i]][-1L] <- Recall(expr[[i]][-1L], keyvals)
        if (is.name(expr[[i]]) && deparse(expr[[i]]) %in% names(keyvals))
            expr[[i]] <- as.name(keyvals[[deparse(expr[[i]])]])
    }
    return( expr )
}

## Test it
f <- function(x, y) -x^2 + x + -y^2 + y
newvals <- c('x'='x0', 'y'='y0')  # named lookup vector

newbod <- rep_vars(body(f), newvals)
newbod
# -x0^2 + x0 + -y0^2 + y0

## Rename the formals, and update the body
formals(f) <- pairlist(x0=bquote(), y0=bquote())
body(f) <- newbod

## The new 'f'
f
# function (x0, y0) 
# -x0^2 + x0 + -y0^2 + y0

f(2, 2)
# [1] -4

С более сложной функцией, где вы хотите избежать изменения строк или других переменных с именами, например, yy и xx,

tricky <- function(x, y) { tst <- "x + y"; -xx*x + yy*y }
formals(tricky) <- pairlist(x0=bquote(), y0=bquote())
body(tricky) <- rep_vars(body(tricky), newvals)
tricky
# function (x0, y0) 
# {
#     tst <- "x + y"
#     -xx * x0 + yy * y0
# }
# 
person Rorschach    schedule 22.11.2015
comment
Работает идеально! Даже решает другую проблему (о которой я забыл упомянуть), когда некоторые функции, такие как exp(x), превращаются в ex0p(x0)! Большое спасибо! - person Vandenman; 22.11.2015

Здесь есть несколько способов. Следуя вашему коду, я бы сделал что-то вроде этого:

f = function(x, y) -x^2 + x + -y^2 + y
# Take old names
form_old = names(formals(f))
# Make new names
form_new = paste0(form_old, 0)
deparse(body(f)) -> bod
for (i in 1:length(form_new)) {
  bod = gsub(form_old[i], form_new[i], bod, fixed = TRUE)
}
formals(f) = setNames(vector("list", length(form_new)), form_new)
body(f) <- parse(text = bod)
f(1, 1) 
person Chris    schedule 22.11.2015
comment
Это работает для примера, но, к сожалению, не работает для exp(x), о котором я забыл упомянуть в своем примере. Тем не менее, я думаю, что могу использовать deparse и parse для ситуаций редактирования тела функции (т.е. вставки терминов). Спасибо за ответы! - person Vandenman; 22.11.2015
comment
Да, другой ответ намного лучше. Я в основном просто пытался заставить его работать после вашей настройки. - person Chris; 22.11.2015