2009-12-01 21 views
8

Quiero generar un fn totalmente en tiempo de ejecución (es decir, el nombre y el arg símbolos se deciden en tiempo de ejecución, no en código) Cuál es la mejor manera de lograr esto?Clojure: Cómo crear una función en tiempo de ejecución

Por ejemplo, ¿cómo puedo implementar la siguiente función?

(defn gen-fn [name arg-symbols body] 
... 
... 

que se utilizaría como esto:

(gen-fn "my-func-name" (symbol "x") (symbol "y") (println "this is body. x=" x)) 

Tenga en cuenta que Nombre de función, los argumentos y el cuerpo no están codificados, pero se puede decidir en tiempo de ejecución

Respuesta

12
(defn gen-fn 
    [n as b] 
    (let [n  (symbol n) 
     as  (vec (map symbol as)) 
     fn-value (eval `(fn ~n ~as ~b))] 
    (intern *ns* n fn-value)))

Y algún uso :

user=> (gen-fn "foo" ["x"] '(do (println x) (println (inc x)))) 
#'user/foo 
user=> (foo 5) 
5 
6 
nil

Sin embargo, Realmente no me gusta este enfoque. Huele muy duro: eval. ¿Por qué quieres generar globales en tiempo de ejecución? Veo varios problemas con espacios de nombres incorrectos y otros hots desagradables que se levantan en el horizonte ...

+0

Sí. Esto es feo. La razón por la que necesito esto es que estoy experimentando con GeneticProgramming bajo Clojure. Clojure parece muy natural para esto. – GabiMe

+0

Ah. De acuerdo. GP puede ser un uso legal de 'eval'. Pero aún mantente alerta para efectos extraños con 'eval'. – kotarak

+0

¿por qué eval? ¿No podrías hacer esto con una macro? –

0

No estoy del todo seguro, pero creo que podrías hacer esto con una macro que sería superior a eval.

(defmacro gen-fn 
    [n as b] 
    (let [n (symbol n) 
     as (vec (map symbol as))] 
    `(intern *ns* n (fn ~n ~as [email protected])))) 
+1

Esto no funciona en tiempo de ejecución. El punto es: necesitas saber 'n',' as' y 'b' de antemano. Y luego puede hacer: (defmacro gen-fn [n as b] '(defn ~(symbol n) ~(vec (map symbol as)) [email protected])). Con el enfoque 'eval', los argumentos para' gen-fn' pueden ser resultados de cálculos arbitrarios. – kotarak

1

Otra forma es utilizar "eval" y "leer-string":

user => (def f1 (eval (cadena lectura "(fn [xy] (* xy))")))

# 'usuario/f1

user => (f1 3 5)

Cuestiones relacionadas