2016-12-11 2 views
0

Je veux écrire une fonction dans R qui saisit le nom d'une variable dans le contexte de l'appelant de l'appelant. Je pense que le problème que j'ai est mieux compris en demandant comment composer deparse et substitute. Vous pouvez voir qu'une composition naïve ne fonctionne pas:En R, comment définir une fonction équivalente à `deparse (substitut (x))`?

# a compose operator 
> `%c%` = function(x,y)function(...)x(y(...)) 

# a naive attempt to combine deparse and substitute 
> desub = deparse %c% substitute 
> f=function(foo) { message(desub(foo)) } 
> f(log) 
foo 

# this is how it is supposed to work 
> g=function(foo) { message(deparse(substitute(foo))) } 
> g(log) 
log 

J'ai aussi essayé quelques variations impliquant eval.parent mais pas de chance. Toute aide est appréciée.


Précision: Je ne cherche pas synonyme de deparse(substitute(...)), par exemple match.call()[[2]] - ce que je suis à la recherche est un moyen de définir une fonction

desub = function(foo) { 
    ... 
    # What goes here? 
} 

telle que la définition de f ci-dessus produit la même réponse que g. Il devrait ressembler à ceci:

> f=function(foo) { message(desub(foo)) } 
> f(log) 
log 

Peut-être match.call pourrait être utile dans le corps de desub ci-dessus, mais je voudrais savoir comment. Merci!

+0

Vous pouvez vérifier 'match.call' – akrun

+0

@akrun: Je sais' match.call' mais je ne vois pas comment il va aider. Voulez-vous élaborer un peu? Ou peut-être que ce sera plus facile pour vous de fournir simplement une ligne de code qui fait ce que je demande ... – Metamorphic

+0

Je voulais dire 'g1 <- function (foo) match.appel() [[2]]; g1 (log) # log' – akrun

Répondre

1

Merci à @ egnha et @akrun pour les tentatives courageuses. Après avoir joué un peu, j'ai trouvé une solution qui fonctionne.

Ce fragment:

desub <- function(y) { 
    e1=substitute(y) 
    e2=do.call(substitute,list(e1), env=parent.frame()) 
    deparse(e2) 
} 

donne:

> f <- function(x) message(desub(x)) 
> f(log) 
log 

Mise à jour:

Avec l'aide de Mark Bravington sur la liste de R-devel, j'ai pu généraliser cette à plusieurs images. Je pensais que je devrais poster ici, parce que c'est un peu plus utile que ce qui précède, et parce qu'il y avait une solution de contournement difficile impliquant (éventuellement buggy?) Comportement dans parent.frame().

# desub(v,0)=="v" 
# desub(v,1)==deparse(substitute(v)) 
# desub(v,2)==name of v in grandparent's frame 
# etc. 
desub = function(y,n=1) { 
    env=environment(); 
    for(i in 0:n) { 
    y = do.call(substitute, list(substitute(y)), env=env) 
    env = do.call(my_mvb_parent, list(), env=env) 
    } 
    deparse(y) 
} 

# helper: 
# 
# - using mvb.parent.frame fixes problems with capture.output and 
# weird cycling behavior in the built-in parent.frame 
# 
# - this wrapper makes mvb.parent.frame not throw an error when we get 
# to globalenv() 
my_mvb_parent=function() { 
    library(mvbutils) 
    tryCatch(
    mvb.parent.frame(2), 
    error=function(e) { globalenv()}) 
} 

if(1) { 
    # example code 
    g2=function(t) { 
    for(i in 0:5) { 
     res=desub(t,i); 
     print(res); 
     res1=capture.output(desub(t,i)) 
     stopifnot(capture.output(res)==res1) 
    } 
    } 
    g1=function(z) g2(z) 
    g=function(y) g1(y) 
    g(log) 
    # prints: 
    ## [1] "t" 
    ## [1] "z" 
    ## [1] "y" 
    ## [1] "log" 
    ## [1] "log" 
    ## [1] "log" 
} 
2

Comme vous l'avez supposé, c'est un problème avec les environnements. La raison pour laquelle la fonction f ne donne pas log lorsque vous appelez f(log), est que l'environnement dans lequel substitute est appelé, à savoir l'environnement d'évaluation de desub, ne contient pas de liaison à log.

Le remède est d'évaluer l'appel à substitute dans l'environnement approprié et modifier desub en conséquence:

desub <- function(x, env = parent.frame()) { 
    deparse(eval(substitute(substitute(x)), envir = env)) 
} 

maintenant f fait ce qu'il était censé faire:

f(log) 
#> log 
+0

egnha: Merci - avant de l'accepter, cela vous dérangerait-il d'éditer votre réponse pour fournir le code pour "# Ce qui se passe ici?"? – Metamorphic

+0

En outre, votre réponse dépend de l'argument de 'f_new' étant appelé' x'. Si vous le changez en 'y', votre programme imprime' x' ... – Metamorphic

+0

@Metamorphic Vous avez raison. Ce comportement est conforme au raisonnement que j'ai donné. Néanmoins, votre variation sur ma solution résout le problème d'origine plus fidèlement. Je vais mettre à jour. – egnha