Доступ к переменным в функции внутри функции - PullRequest
0 голосов
/ 23 июня 2018

При запуске функции в R я запускаю другую функцию внутри нее. У меня есть код в строках этого:

f_a <- function(b, c){
    return(b + c)
}

f_e <- function(){
    b = 2
    c = 2 
    d = f_a(b, c)
    print(d)
}

Это отлично работает. Я бы хотел, чтобы не передавал переменные b, c в функцию f_a. Я хотел бы сделать что-то вроде этого (который выдает ошибки)

f_a <- function(){
    return(b + c)
}

f_e <- function(){
    b = 2
    c = 2
    d = f_a()
    print(d)
}

Есть ли способ сделать это с помощью окружения или путей поиска или любым другим способом?

Ответы [ 4 ]

0 голосов
/ 23 июня 2018

Вы можете назначить переменные глобальному окружению и использовать их внутри функции.

f_a <- function(){
    return(b + c)
}

f_e <- function(){
    assign("b", 2, envir = .GlobalEnv)
    assign("c", 2, envir = .GlobalEnv)
    d = f_a()
    print(d)
}

# > f_e()
# [1] 4
0 голосов
/ 23 июня 2018

Одним из вариантов является явный захват a и b из вызывающей среды:

f_a <- function(){
    get('b', envir = parent.frame()) + get('c', envir = parent.frame())
}

f_e <- function(){
    b = 2
    c = 2
    d = f_a()
    d
}

f_e()
#> [1] 4

В качестве альтернативы, вы можете использовать quote, чтобы отложить оценку, а затем eval, чтобы оценить кодв вызывающей среде, фактически делая то же самое:

f_a <- function(){
    eval(quote(b + c), parent.frame())
}

Это не совсем надежный способ написания кода, поскольку он ограничивает возможные способы успешного вызова f_a.Намного проще следовать коду, который явно передает переменные.

0 голосов
/ 23 июня 2018

Edit:

Предложение

@ alistaire использовать quote для построения выражений поднимает эту дополнительную альтернативу, которая кажется еще менее уродливой:

expr_env <- new.env()
   expr_env$f_a <- quote(b+c)
   expr_env$f_z <- quote(x+y)

f_e<-function(){
    b=2
    c=2
    d=eval( expr_env$f_a)
    print(d)
}

Является ли определение функции с помощью local приемлемой альтернативой?

 f_e<-function(){
     b=2
     c=2
     d<-local({
          b+c
              })

     print(d)
 }
 f_e()
[1] 4

Альтернативой может быть только возврат дерева разбора и затем завершение вычисления в среде, "локальной" для функции. Мне это кажется "некрасивым":

expr_list<-function(){  f_a <- quote(b+c)
                        f_z <- quote(x+y)
list(f_a=f_a,f_z=f_z) }

f_e<-function(){
    b=2
    c=2
    d=eval( (expr_list()$f_a))
    print(d)
}
0 голосов
/ 23 июня 2018

Я рекомендую вам прочитать о лексическом определении объема , но я думаю, что хороший способ избежать написания большого количества переменных может быть:

get_args_for <- function(fun, env = parent.frame(), inherits = FALSE, ..., dots) {
    potential <- names(formals(fun))

    if ("..." %in% potential) {
        if (missing(dots)) {
            # return everything from parent frame
            return(as.list(env))
        }
        else if (!is.list(dots)) {
            stop("If provided, 'dots' should be a list.")
        }

        potential <- setdiff(potential, "...")
    }

    # get all formal arguments that can be found in parent frame
    args <- mget(potential, env, ..., ifnotfound = list(NULL), inherits = inherits)
    # remove not found
    args <- args[sapply(args, Negate(is.null))]
    # return found args and dots
    c(args, dots)
}

f_a <- function(b, c = 0, ..., d = 1) {
    b <- b + 1
    c(b = b, c = c, d = d, ...)
}

f_e <- function() {
    b <- 2
    c <- 2
    arg_list <- get_args_for(f_a, dots = list(5))
    do.call(f_a, arg_list)
}

> f_e()
b c d   
3 2 1 5 

Установка inherits = FALSE по умолчанию гарантирует, что мы получаем переменные только из указанной среды. Мы также можем установить dots = NULL при вызове get_args_for, чтобы мы не передавали все переменные, но оставьте многоточие пустым.

Тем не менее, он не совсем устойчив, потому что dots просто добавляется в конце, и если некоторые аргументы не названы, они могут в конечном итоге соответствовать позиции. Кроме того, если некоторые значения должны быть NULL в вызове, это было бы нелегко обнаружить.


Я бы настоятельно рекомендовал не использовать их ниже в пакете R. Мало того, что это будет довольно некрасиво, вы получите кучу заметок из проверки CMD R относительно неопределенных глобальных переменных.

Другие опции.

f_a <- function() {
    return(b + c)
}

f_e <- function() {
    b <- 2
    c <- 2
    # replace f_a's enclosing environment with the current evaluation's environment
    environment(f_a) <- environment()
    d <- f_a()
    d
}

> f_e()
[1] 4

Нечто подобное, описанное выше, вероятно, не будет работать внутри пакета R, так как я думаю, что функции пакета имеют закрытые окружающие среды.

Или:

f_a <- function() {
    with(parent.frame(), {
        b + c
    })
}

f_e <- function() {
    b <- 2
    c <- 2
    f_a()
}

> f_e()
[1] 4

Таким образом, вы не можете навсегда изменить окружение другой функции. Тем не менее, обе функции будут совместно использовать среду, так что-то подобное может произойти:

f_a <- function() {
    with(parent.frame(), {
        b <- b + 1
        b + c
    })
}

f_e <- function() {
    b <- 2
    c <- 2
    d <- f_a()
    c(b,d)
}

> f_e()
[1] 3 5

Где вызов внутренней функции изменяет значения во внешней среде.

Еще один вариант, который немного более гибкий, поскольку он только временно изменяет окружающую среду, используя eval. Тем не менее, есть определенные R-функции, которые обнаруживают свою текущую среду выполнения с помощью "daRk magic", и не может быть одурачен eval; см. это обсуждение .

f_a <- function() {
    b <- b + 1
    b + c
}

f_e <- function() {
    b <- 2
    c <- 2
    # use current environment as enclosing environment for f_a's evaluation
    d <- eval(body(f_a), list(), enclos=environment())
    c(b=b, d=d)
}

> f_e()
b d 
2 5 
...