2011-10-18 3 views
31

¿Cuál es la definición precisa de "posición de la cola" para recurrir en clojure. Yo pensaría que sería el último elemento en una expresión S del bucle, pero en el ejemplo de abajo me parece que la S-Expresión que comienza con (si ...) está en posición de cola, es decir ([PALABRA CLAVE] [ESTATUTOS VINCULANTES] [SI ESTÁ DECLARACIÓN]). códigoClojure: ¿Cuál es exactamente la posición de la cola para recurrencia?

(= __ 
    (loop [x 5 
     result []] 
    (if (> x 0) 
     (recur (dec x) (conj result (+ 2 x))) 
     result))) 

tomado de http://www.4clojure.com/problem/68

cuestión estrechamente relacionada: How can I call recur in an if conditional in Clojure?

Respuesta

56

La posición de la cola es Con una posición, que sería una expresión devolver un valor desde. No hay más formularios evaluados después de evaluar la forma en la posición de la cola.

Considere este ejemplo de The Joy of Clojure

(defn absolute-value [x] 
    (if (pos? x) 
     x  ; "then" clause 
     (- x))) ; "else" clause 

Se necesita un único parámetro y los nombres que x. Si x ya es un número positivo, entonces x es devuelto; de lo contrario, se devuelve el opuesto de x. La forma if está en la posición de la cola de la función porque cualquiera que sea el resultado, la función completa volverá. La x en la cláusula "then" también está en una posición de cola de la función. Pero la x en la cláusula "else" no está en la posición de cola de la función porque el valor de x se pasa a la función -, no se devuelve directamente. La cláusula else en su conjunto (- x) está en una posición de cola.

Del mismo modo en la expresión

(if a 
    b 
    c) 

tanto bc y se encuentran en posiciones de cola, ya que ninguno de ellos pudiera ser devueltos de la sentencia if.

Ahora en su ejemplo

(loop [x 5 
     result []] 
    (if (> x 0) 
    (recur (dec x) (conj result (+ 2 x))) 
    result))) 

la forma (if ...) está en la posición de cola de la forma (loop ...) y tanto la forma (recur ...) y la forma result se encuentran en la posición de la cola de la forma (if ...).

Por otro lado, en la pregunta que se ha vinculado

(fn [coll] (let [tail (rest coll)] 
      (if (empty tail) 
       1 
       (+ 1 (recur tail))))) 

la recur es no en posición de la cola porque el (+ 1 ...) serán evaluados después de la (recur tail). Por lo tanto, el compilador Clojure da un error.

La posición de la cola es importante porque puede usar la forma recur desde la posición de la cola. Los lenguajes de programación funcional usualmente usan recursión para lo que los lenguajes de programación de procedimiento logran por bucles. Pero la recursión es problemática, ya que consume espacio en la pila y la recursión profunda puede provocar problemas de acumulación de stack (además de ser lenta). Este problema generalmente se resuelve con optimización de llamadas de cola (TCO), que elimina la llamada cuando la llamada recursiva ocurre en la posición de cola de una función/formulario.

Dado que Clojure está alojado en la JVM y la JVM no es compatible con la optimización de la cola de llamadas, se necesita un truco para realizar la recursión. La forma recur es ese truco, le permite al compilador Clojure hacer algo similar a la optimización de la cola de cola. Además, verifica que recur está realmente en una posición de cola. El beneficio es que puede asegurarse de que la optimización realmente suceda.

+0

Gracias, gran explicación, mi confusión fue que estaba pensando en la posición de la cola como una posición en la S-Expresión en lugar de en términos de final evaluación. A la luz de su explicación, parece que es posible utilizar múltiples formularios recurrentes en un bucle (por ejemplo, en ambas ramas de un IF s-exp final) ¿verdad? – tjb

+0

Sí, eso es completamente posible. Recuerdo estar bastante confundido por esto, también. Además, seguí leyendo que la recurrencia era tan importante debido a la falta de TCO en la JVM ... No entendí nada sobre esto (lo que TCO significa, por ejemplo ...) y mi cabeza estaba girando ... – Paul

+0

Sí, es bastante confuso, y nunca vi ninguna documentación que lo explicara. Quizás sugeriré que agreguen algo a la documentación recurrente en función de su respuesta. – tjb

24

Simplemente para complementar la excelente respuesta de Paul anterior, The Joy of Clojure (ed1) proporciona una tabla (Tabla 7.1) que muestra exactamente dónde está la posición de la cola en varias formas/expresiones, que he reproducido a continuación. Busque dónde aparece la palabra "cola" en cada forma/expresión:

 
|---------------------+-------------------------------------------+---------------| 
| Form    | Tail Position        | recur target? | 
|---------------------+-------------------------------------------+---------------| 
| fn, defn   | (fn [args] expressions tail)    | Yes   | 
| loop    | (loop [bindings] expressions tail)  | Yes   | 
| let, letfn, binding | (let [bindings] expressions tail)   | No   | 
| do     | (do expressions tail)      | No   | 
| if, if-not   | (if test then tail else tail)    | No   | 
| when, when-not  | (when test expressions tail)    | No   | 
| cond    | (cond test test tail ... :else else tail) | No   | 
| or, and    | (or test test ... tail)     | No   | 
| case    | (case const const tail ... default tail) | No   | 
|---------------------+-------------------------------------------+---------------| 
Cuestiones relacionadas