2010-09-08 13 views
14

No estoy muy familiarizado con las macros Clojure/Lisp. Me gustaría escribir macro apply-recur que tendría el mismo significado que (apply recur ...)Aplicar macro recurrente en Clojure

Supongo que no hay una necesidad real de ese macro, pero creo que es un buen ejercicio. Entonces estoy pidiendo tu solución.

Respuesta

15

Bueno, realmente no hay necesidad de eso, si solo porque recur no puede tomar varargs (un recur en la parte superior de la función toma un único argumento seqable final agrupando todos los argumentos pasan el último argumento requerido). Esto no afecta la validez del ejercicio, por supuesto.

Sin embargo, existe el problema de que un "buen" apply-recur presumiblemente debe manejar SEQs argumento devueltos por expresiones arbitrarias y no sólo literales:

;; this should work... 
(apply-recur [1 2 3]) 

;; ...and this should have the same effect... 
(apply-recur (vector 1 2 3)) 

;; ...as should this, if (foo) returns [1 2 3] 
(apply-recur (foo)) 

Sin embargo, el valor de una expresión arbitraria como (foo) es simplemente no disponible, en general, en el tiempo de expansión de macro. (Quizás se asuma que (vector 1 2 3) siempre arroja el mismo valor, pero foo puede significar cosas diferentes en momentos diferentes (una razón eval no funcionaría), sea un let - local de destino en lugar de un Var (otra razón eval no funcionaría), etc.)

Así, para escribir totalmente en general apply-recur, tendríamos que ser capaces de determinar cuántos argumentos esperaría una forma regular y tienen recur(apply-recur some-expression) expanda a algo así como

(let [seval# some-expression] 
    (recur (nth seval# 0) 
     (nth seval# 1) 
     ... 
     (nth seval# n-1))) ; n-1 being the number of the final parameter 

(La final nth podría necesitar ser nthnext si se trata de varargs, que presenta un problema similar al descrito en el párrafo siguiente. Además, sería una buena idea añadir una afirmación para comprobar la longitud de la seqable devuelto por some-expression.)

No estoy al tanto de cualquier método para determinar la aridad apropiado de un recur en un punto particular en el código en el tiempo de macro-expansión. Eso no significa que uno no esté disponible; eso es algo que el compilador necesita saber de todos modos, entonces quizás haya una forma de extraer esa información de sus partes internas. Aun así, cualquier método para hacer eso seguramente necesitaría confiar en los detalles de implementación que podrían cambiar en el futuro.

Por lo tanto, la conclusión es esta: incluso si es posible escribir tal macro (lo que podría no ser el caso), es probable que cualquier implementación sea muy frágil.


Como comentario final, escribiendo un apply-recur que sólo sería capaz de hacer frente a los literales (en realidad tendría que ser dada como un literal de la estructura general de la SEC arg; los argumentos propios - no necesariamente, entonces esto podría funcionar: (apply-recur [foo bar baz]) =>(recur foo bar baz)) sería bastante simple. No estoy arruinando el ejercicio regalando la solución, pero, como sugerencia, considere usar [email protected].

3

apply es una función que toma otra función como argumento. recur es una forma especial, no una función, por lo que no se puede pasar al apply.

+1

Claro, aunque tomé la pregunta en el sentido de "dado que uno no puede simplemente escribir" (aplicar recurrencia ...) ', ¿cómo se puede escribir una macro que capture el sentido intencionado de eso?" –

+0

Sí Michał, lo dije en serio. Gracias a los dos. – JoeCamel

Cuestiones relacionadas