2010-01-14 24 views
5

Estoy trabajando con clojure y aunque he incursionado con cefaleas antes, tengo problemas para encontrar una forma limpia de anotar sentencias de let en sentencias de cond. Por ejemplo, considere la siguiente función:let inside cond

(defn operate-on-list [xs] 
    (let [[unpack vector] (first xs)] 
    (cond 
     [(empty? xs) 'empty 
     unpack vector 
     :else (operate-on-list (rest xs))]))) 

Es una operación recursiva bastante estándar en una lista, pero hay que hacer un trabajo sobre el primer elemento en la lista antes de que funciona con el contenido. El problema, por supuesto, es que la lista puede estar vacía.

En este ejemplo, no sería difícil cambiar unpack a ((first xs) 0) y vector a ((first xs) 1), pero esto rápidamente se pone fea, si aún queda trabajo por hacer en los primeros (xs).

¿Hay alguna forma de utilizar de manera efectiva una instrucción let en un estado intermedio?

Gracias.

-Nate

+0

Hi - no está claro para mí lo que está tratando de lograr aquí. Puede encontrar este interesante http://www.assembla.com/spaces/clojure/tickets/200 y http://groups.google.com/group/clojure/browse_thread/thread/c1097ce07506fc39 Por favor, considere indicar cuál es la función que se supone hacer en su pregunta, y algunos ejemplos de entradas y salidas. La sintaxis no se ve como una declaración de cond válida ya que el cuerpo del cond se envuelve en un vector. –

Respuesta

11

En casos como estos, estás mejor usando if-let:

(defn operate-on-list [xs] 
    (if-let [[unpack v] (first xs)] 
    (cond 
     unpack v 
     :else (operate-on-list (rest xs))))) 

Este código recorre la lista dada ss-poder (list, vector, array ...) de vectores y devuelve el segundo elemento del primer vector cuyo primer elemento es verdadero (es decir, no false o nil). nil se devuelve si no se encuentra dicho vector.

Tenga en cuenta que vector es una función incorporada, por lo que he elegido v como el nombre de la variable, en caso de que la necesidad de utilizar la función en el cuerpo se presente en el futuro. Lo que es más importante, estás usando demasiados corchetes en tu sintaxis cond; arreglado en esta versión.

ACTUALIZACIÓN: Dos cosas adicionales digno de mención acerca if-let:

  1. La forma if-let obras, si (first xs) pasa a ser nil (false sería lo mismo), la unión desestructuración nunca se lleva a cabo, por lo Clojure no se quejará de no poder enlazar nil a [unpack v].

  2. Además, if-let acepta una cláusula else (en la que no se puede hacer referencia a las variables ligadas en if-let fijaciones del vector - aunque si estás en la cláusula otra cosa, sabes que donde false o nil de todos modos).

+2

Vea también 'when-let', que es más idiomático cuando solo tiene una rama. – kotarak

+0

Una segunda nota: tenga en cuenta que 'if-let' funciona aquí, porque se supone que la lista debe contener vectores. En general, '(when-let [x (first s)] ...)' es * not * un sustituto de '(when-let [s (seq s)] (let [f (first s)] ...)) '. – kotarak

2

Algo así como esto, con un let dentro del alcance de la cond?

(defn operate-on-list [list] 
    (let [ el_first (first list) ] 
    (cond 
     (nil? el_first) (println "Finished") 
     :else (do 
     (let [ list_rest (rest list) ] 
       (println el_first) 
       (operate-on-list list_rest)))))) 

(operate-on-list '(1 2 3)) 

La salida es:

1 
2 
3 
Finished 
+4

Tu 'do' es superfluo. –

+0

@Brian Carper: tienes razón; simplemente puede omitir el do. Esa fue una resaca Elisp de mi parte (en particular, progn). –