2012-07-26 8 views
10

Decir que tengo un mapa de esta forma:mejor manera de nido si alquilar en clojure

(def m {:a "A" :b "B"}) 

y quiero hacer algo si :a y :b son ambos no nula, lo que puedo hacer:

(if-let [a (:a m)] 
    (if-let [b (:b m)] 
     ... etc)) 

o

(if (and (:a m) (:b m)) 
    (let [{a :a b :b} m] 
     ... etc)) 

o incluso

(if (every? m [:a :b]) 
    (let [{a :a b :b} m] 
     ... etc)) 

¿Existe una forma más ordenada (es decir, una línea) de lograr esto?

Respuesta

7

creo una macro puede ser necesario aquí para crear el comportamiento que desea. Nunca una (aún) he escrito, pero la siguiente representación me sugiere que esto podría ser bastante sencillo:

(let [{:keys [a b]} m] 
    (when (every? identity [a b]) 
     (println (str "Processing " a " and " b)))) 

El uso de la forma de desestructuración :keys vinculante y every? permite una única especificación de un vector de claves para desestructurar y verificar, y los locales vinculados están disponibles en un siguiente bloque de código.

esto podría ser utilizado para hacer una macro como (when-every? [keys coll] code-with-bindings)

puedo actualizar esta respuesta con el código de macro si puedo tomar el tiempo para encontrar la manera de hacerlo.

1

not-any? es un buen atajo para esto:

user> (not-any? nil? [(m :a) (m :b)]) 
true 
user> (not-any? nil? [(m :a) (m :b) (m :d)]) 
false 
user> 
+2

No estoy seguro que responde a la pregunta ... – noahlz

+0

would not '(every? some? ...)' ser mejor? de esa manera usted podría evitar la doble negación – kosii

+0

not-any? puede dejar de verificar cuando se encuentra uno, por una leve mejora de tiempo en muchos casos –

6

Usted podría utilizar map destructuring - una característica útil de Clojure. Esto también se aprovecha el hecho de que and es un cortocircuito, y cualquier tecla en el primer mapa que no se encuentra en el segundo mapa obtiene nil, un valor Falsy:

(let [{a :a b :b} {:a 1 :b "blah"}] 
    (and a b (op a b))) 

bien, por lo que es dos líneas en lugar de uno .... también esto no distingue entre nil y otros valores falsy.

1

No estoy muy seguro de lo que quiere hacer si las claves tienen valores no nulos o si desea que se devuelvan valores o claves que no sean nulos. Entonces, lo resolví para que las claves no nulas sean devueltas.

Utilizaría lo siguiente como un paso intermedio como parte de una solución final.

Estoy mostrando todos los pasos que he usado, no para ser pedante, sino para proporcionar una respuesta completa. El espacio de nombre es repl-test. Tiene una principal asociada a ella.

repl-test.core=> (def m {:a "A" :b "B" :c nil}) 
#'repl-test.core/m 

repl-test.core=> (keys m) 
(:a :c :b) 

y finalmente:

; Check key's value to determine what is filtered through. 
repl-test.core=> (filter #(if-not (nil? (%1 m)) (%1 m)) (keys m)) 
(:a :b) 
1

Por cierto he encontrado un feo de una sola línea, que funciona porque and devuelve la última cosa en su lista de argumentos si son todas verdaderas:

(if-let [[a b] (and (:a m) (:b m) [(:a m)(:b m)])] 
    (println "neither " a " nor " b " is falsey") 
    (println "at least one of " a " or " b " is falsey")) 
Cuestiones relacionadas