web-dev-qa-db-fra.com

Construire des fonctions de symboles utilisant 'Bquote' (ou alternatives à le faire)

Disons que j'ai un objet de type "symbole" représentant le nom d'une fonction. Par example:

nm <- quote(mean)

Je veux construire une fonction f dont le corps utilise la fonction nommée par le symbole nm. Par example:

f <- function(x, do = c("something", "nothing")) {
  switch(match.arg(do), something = mean(x), nothing = x)
}

Je veux construire cette fonction identique, ce qui implique que je voudrais non Soyez satisfait de l'approche suivante:

factory <- function(name) {
  func <- match.fun(name)
  function(x, do = c("something", "nothing")) {
    switch(match.arg(do), something = func(x), nothing = x)
  }
}
g <- factory(nm)

puisque le corps de g n'est pas body(f) et l'environnement de g n'est pas environment(f).

Une approche que j'ai envisagée est bquote:

h <- eval(bquote({
  function(x, do = c("something", "nothing")) {
    switch(match.arg(do), something = .(nm)(x), nothing = x)
  }
}))

bquote me fait la majeure partie du chemin, mais un problème est que la sortie print de h ne contient pas la valeur substituée de nm par défaut :

h
## function(x, do = c("something", "nothing")) {
##     switch(match.arg(do), something = .(nm)(x), nothing = x)
##   }

print(h, useSource = FALSE)
## function (x, do = c("something", "nothing")) 
## {
##     switch(match.arg(do), something = mean(x), nothing = x)
## }

La cause semble être l'attribut srcrefh:

identical(f, h)
## [1] TRUE
identical(f, h, ignore.srcref = FALSE)
## [1] FALSE

ma question est : Comment peut-on approcher le problème général de la construction f de nm?

Mes conditions sur la fonction construite h sont que identical(f, h) doit être TRUE et que la sortie de print(h) doit contenir la valeur substituée de nm, semblable à print(f).

J'accueillerais les réponses à améliorer mon approche bquote ou réponses suggérant une nouvelle approche, ou des réponses expliquant pourquoi ce que je veux faire n'est pas réellement possible ...

3
Mikael Jagan

Pas particulièrement élégant, mais un parse(deparse( semble fonctionner:

nm <- quote(mean)
f <- function(x, do = c("something", "nothing")) {
  switch(match.arg(do), something = mean(x), nothing = x)
}

eval(parse(text=deparse(bquote(h <- function(x, do = c("something", "nothing")) {
  switch(match.arg(do), something = .(nm)(x), nothing = x)
}))))

identical(f, h)
#> [1] TRUE
print(f)
#> function(x, do = c("something", "nothing")) {
#>   switch(match.arg(do), something = mean(x), nothing = x)
#> }
print(h)
#> function(x, do = c("something", "nothing")) {
#>     switch(match.arg(do), something = mean(x), nothing = x)
#> }

srcref n'est pas identique, comme prévu:

identical(f, h, ignore.srcref = FALSE)
#> [1] FALSE
attributes(attributes(f)$srcref)$srcfile$lines
#> [1] "f <- function(x, do = c(\"something\", \"nothing\")) {"     
#> [2] "  switch(match.arg(do), something = mean(x), nothing = x)"
#> [3] "}"
attributes(attributes(h)$srcref)$srcfile$lines
#> [1] "h <- function(x, do = c(\"something\", \"nothing\")) {"     
#> [2] "    switch(match.arg(do), something = mean(x), nothing = x)"
#> [3] "}"
1
jblood94

Lecture à travers ?srcref, Il semble qu'il existe deux manières idiomatiques d'améliorer l'approche bquote. La première utilisation removeSource Pour nettoyer récursivement une fonction qui conserve son code source:

h <- removeSource(eval(bquote({
  function(x, do = c("something", "nothing")) {
    switch(match.arg(do), something = .(nm)(x), nothing = x)
  }
})))
h
function (x, do = c("something", "nothing")) 
{
    switch(match.arg(do), something = mean(x), nothing = x)
}

La seconde évite de préserver le code source tout à fait:

op <- options(keep.source = FALSE)
h <- eval(bquote({
  function(x, do = c("something", "nothing")) {
    switch(match.arg(do), something = .(nm)(x), nothing = x)
  }
}))
options(op)
h
function (x, do = c("something", "nothing")) 
{
    switch(match.arg(do), something = mean(x), nothing = x)
}

En fait, ?options Dit que la valeur par défaut de keep.source Est interactive(), les deux approches sont donc quelque peu redondantes dans des contextes non interactifs.

0
Mikael Jagan