web-dev-qa-db-fra.com

Accéder aux variables dans une fonction dans une fonction

Lorsque j'exécute une fonction dans R, j'exécute une autre fonction dans celle-ci. J'ai un code sur les lignes de ceci:

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

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

Cela fonctionne bien. Ce que je voudrais faire est ne pas passer les variables b, c à la fonction f_a. Je voudrais faire quelque chose comme ça (qui jette des erreurs)

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

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

Est-il possible de faire cela en utilisant des environnements, des chemins de recherche ou tout autre moyen?

7
Siddd

Je vous encourage à lire sur la portée lexicale , Mais je pense qu'une bonne approche pour éviter d'écrire beaucoup de variables pourrait être:

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 

Définir inherits = FALSE par défaut garantit que nous n'obtenons que des variables de l'environnement spécifié . Nous pourrions également définir dots = NULL lors de l'appel de get_args_for pour ne pas transmettre toutes les variables, .__, mais laisser les ellipses vides.

Néanmoins, ce n'est pas tout à fait robuste, .__, car dots est simplement ajouté à la fin, Et si certains arguments ne sont pas nommés, , Ils pourraient être mis en correspondance par la position .. les valeurs devraient être NULL dans l'appel, il ne serait pas facile de le détecter.


Je vous déconseille fortement d'utiliser ces éléments dans un paquet R . Non seulement ce sera plutôt moche, vous obtiendrez de nombreuses notes de la vérification CMD de R. concernant des variables globales non définies.

Autres options.

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

Quelque chose comme ce qui précède ne fonctionnerait probablement pas dans un package R, .__, car je pense que les fonctions d’un package ont leurs environnements fermants verrouillés.

Ou:

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

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

> f_e()
[1] 4

Ainsi, vous ne modifiez pas de façon permanente l'environnement englobant de l'autre fonction . Cependant, les deux fonctions partageront un environnement, Un événement comme celui-ci pourrait se produire:

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

Où l'appel de la fonction interne modifie les valeurs de l'environnement externe.

Encore une autre option, un peu plus flexible, .__ puisqu'elle modifie temporairement l'environnement englobant en utilisant eval. Cependant, certaines fonctions R détectent leur environnement d'exécution actuel par le biais de "magie daRk", et ne peut être dupé par eval; voir cette discussion .

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 
9
Alexis

Une option consiste à extraire explicitement a et b de l'environnement appelant:

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

Vous pouvez également utiliser quote pour retarder l'évaluation, puis eval pour évaluer le code dans l'environnement appelant, en effectuant la même chose:

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

Cependant, ce n'est pas vraiment un moyen robuste d'écrire du code car il limite les moyens possibles pour appeler f_a avec succès. Il est beaucoup plus facile de suivre un code qui transmet explicitement des variables.

3
alistaire

Modifier:

La suggestion de @ alistaire d'utiliser quote pour construire les expressions fait apparaître cette autre alternative qui semble encore moins laide:

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)
}

Est-ce que définir la fonction en utilisant local serait une alternative acceptable?

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

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

Une alternative serait de ne renvoyer qu'un arbre d'analyse puis de terminer l'évaluation dans l'environnement "local" de la fonction. Cela me semble "moche":

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)
}
2
42-

Vous pouvez affecter les variables à l'environnement global et utiliser la fonction inside.

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