Ceci est un développement de la discussion aquí .
Nos trois pièces doivent être une liste d'arguments, un corps et un environnement.
Pour l'environnement, nous utiliserons simplement env = parent.frame()
par défaut.
Nous ne voulons pas vraiment d'une bonne vieille liste pour les arguments, donc à la place nous utilisons alist
qui a un comportement différent :
"...les valeurs ne sont pas évaluées, et les arguments balisés sans valeur sont autorisés"
args <- alist(a = 1, b = 2)
Pour le corps, nous quote
notre expression pour obtenir un call
:
body <- quote(a + b)
Une option consiste à convertir args
à une liste de paires et ensuite simplement appeler la fonction function
en utilisant eval
:
make_function1 <- function(args, body, env = parent.frame()) {
args <- as.pairlist(args)
eval(call("function", args, body), env)
}
Une autre option consiste à créer une fonction vide, puis à la remplir avec les valeurs souhaitées :
make_function2 <- function(args, body, env = parent.frame()) {
f <- function() {}
formals(f) <- args
body(f) <- body
environment(f) <- env
f
}
Une troisième option consiste à utiliser simplement as.function
:
make_function3 <- function(args, body, env = parent.frame()) {
as.function(c(args, body), env)
}
Et enfin, cela me semble très similaire à la première méthode, sauf que nous utilisons un idiome quelque peu différent pour créer l'appel de fonction, en utilisant substitute
plutôt que call
:
make_function4 <- function(args, body, env = parent.frame()) {
subs <- list(args = as.pairlist(args), body = body)
eval(substitute(`function`(args, body), subs), env)
}
library(microbenchmark)
microbenchmark(
make_function1(args, body),
make_function2(args, body),
make_function3(args, body),
make_function4(args, body),
function(a = 1, b = 2) a + b
)
Unit: nanoseconds
expr min lq median uq max
1 function(a = 1, b = 2) a + b 187 273.5 309.0 363.0 673
2 make_function1(args, body) 4123 4729.5 5236.0 5864.0 13449
3 make_function2(args, body) 50695 52296.0 53423.0 54782.5 147062
4 make_function3(args, body) 8427 8992.0 9618.5 9957.0 14857
5 make_function4(args, body) 5339 6089.5 6867.5 7301.5 55137
1 votes
Je pense que c'est pour cela qu'ils ont inventé les lisps !
0 votes
Aussi étrange que cela puisse paraître, nous avons vu un cas où l'on pourrait vouloir analyser une chaîne de caractères pour réaliser quelque chose de similaire (knitr).