2010-11-26 14 views
57

Estoy teniendo dificultades para entender la diferencia entre rest y next en Clojure. The official site's page on laziness indica que la preferencia debería ser probablemente usar rest, pero realmente no explica claramente la diferencia entre los dos. ¿Alguien puede dar alguna idea?Clojure: resto vs.

+3

Este punto no vale una respuesta por separado: Lo que las respuestas hasta ahora no han hecho completamente explícito es que '()' es verdadero, mientras que 'nil' es falso. Entonces '(defn keep-going [my-coll] (if (rest my-coll) (keep-going (rest my-coll))" Finalizado.")' continuará por siempre - o más bien, desbordará la pila - mientras '(defn keep-going [my-coll] (si (next my-coll) (keep-going (next my-coll))" Terminado. ")' Terminará. (OP seguramente ya lo ha resuelto, estoy agregando esta observación para otros.) – Mars

Respuesta

55

A medida que la página se ha vinculado descrito, next es más estricta que (el nuevo comportamiento de) rest porque necesita para evaluar la estructura de los contras perezosos para saber si se debe devolver nil o una ss.

rest Por otro lado, siempre devuelve un seq, por lo que no es necesario evaluar nada hasta que realmente utilice el resultado de rest. En otras palabras, rest es más vago que next.

+0

OK, creo que podría entender lo que quieres decir. ¿Estás diciendo que 'next' siempre devuelve un cons cell, mientras que 'rest' simplemente devuelve un ISeq (o el tipo que sea en realidad)? –

+1

@Daniel:' next' devuelve 'nil' o un' seq' no ocupado '.' rest' siempre devuelve un' seq', que podría estar vacío. – sepp2k

+0

Así que considero que, para algunas secuencias, determinar si hay más elementos es una operación costosa. Con una lista normal de células cons, yo pensaría que es trivial determinar si ha llegado al final de la lista. Sin embargo, con una secuencia diferida (de lazy-seq, quizás), esta determinación puede ser costosa. I No entendí completamente que nil era diferente de la secuencia vacía. Parece que las subsecuencias producen con el resto garantías débiles y, por lo tanto, pueden ser más eficientes. –

27

Es fácil si usted tiene esto:

(next '(1)) 
=> nil 

Así next se ve en la siguiente cosa y si la línea está vacía se devuelve nil en lugar de un vacío ss. Esto significa que necesita mirar hacia el futuro (al primer elemento que devolvería) lo que hace que no sea totalmente vago (quizás no necesite el siguiente valor, pero next desperdicia el tiempo de cálculo para mirar hacia adelante).

(rest '(1)) 
=>() 

rest no se ve adelante y sólo devuelve el resto de la SEC.

Quizás piense: ¿Por qué molestarse en usar dos cosas diferentes aquí? La razón es que normalmente quiere saber si no queda nada en el seq y simplemente devuelve nil, pero en algunos casos donde el rendimiento es muy importante y evaluar un elemento más podría significar un gran esfuerzo puede usar rest.

+7

solo para matarlo: (resto nil) =>() – gtrak

19

next es como (seq (rest ...)).

rest devolverá la pieza restante de una secuencia. Si esa parte de la secuencia aún no se ha realizado, rest no la fuerza. Ni siquiera te dirá si quedan más elementos en la secuencia.

next hace lo mismo pero luego obliga a que se realice al menos un elemento de la secuencia. Entonces, si next devuelve nil, sabrá que ya no quedan más elementos en la secuencia.

2

ahora prefieren utilizar next con reursion, como el escape de evaluación es más simple/Limpiador:

(loop [lst a-list] 
    (when lst 
     (recur (next lst)) 

vs

(loop [lst a-list] 
    (when-not (empty? lst) ;; or (when (seq? lst) 
     (recur (rest lst)) 

Un caso para el uso de rest, sin embargo, sería si usa una colección como cola o pila. En ese caso, desea que su función devuelva una colección vacía al abrir o quitar el último elemento.