2012-07-26 10 views
10

Say mam mapę tej postaci:lepszy sposób na gnieździe jeśli-let w Clojure

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

i chcę coś zrobić, jeśli :a i :b są zarówno nie zerowa, co mogę zrobić:

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

lub

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

lub nawet

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

Czy jest to lepszy sposób (tj. Jedna linia), aby to osiągnąć?

Odpowiedz

7

myślę makro może być konieczne, aby stworzyć żądane zachowanie. Nigdy nie napisałem jednego (jeszcze), ale po reprezentacja sugeruje mi, że może to być dość prosta:

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

Korzystanie formularz :keys z demontażu struktury wiążące i every? umożliwia pojedynczą specyfikację wektorze kluczy do destructure i sprawdź, a powiązane locale są dostępne w następującym bloku kodu.

Może to zostać wykorzystane do makro, takie jak (when-every? [keys coll] code-with-bindings)

może zaktualizować tę odpowiedź za pomocą kodu makra czy mogę trochę czasu, aby wypracować jak to zrobić.

1

not-any? jest ładny skrót do tego:

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

Nie wiem, to jest odpowiedź na pytanie ... – noahlz

+0

nie "(każdy? jakiś? ...)" być lepszy? w ten sposób unikniesz podwójnej negacji – kosii

+0

nie? może przestać sprawdzać, kiedy zostanie znaleziony, na niewielką poprawę czasu w wielu przypadkach –

6

Można użyć map destructuring - przydatna cecha Clojure. Ten wykorzystuje również fakty, które and zwarcie i dowolny klawisz w pierwszej mapie Nie znaleziono w drugiej mapie dostaje nil wartość falsy:

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

Ok, więc to dwa linie zamiast jednego .... to także nie rozróżnia wartości nil i innych wartości falsy.

1

Nie jestem do końca pewien, co chcesz zrobić, jeśli klucze mają wartości inne niż zero lub czy chcesz zwracać niezalogowane klucze lub wartości. Tak, właśnie rozwiązałem to dla zwracanych kluczy nie-zerowych.

Użyłbyś następujących rzeczy jako pośredniego kroku jako część ostatecznego rozwiązania.

Pokazuję wszystkie kroki, których użyłem, a nie pedantycznie, ale aby udzielić kompletnej odpowiedzi. Przestrzeń nazw to repl-test. Ma główną rolę z tym związaną.

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

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

i wreszcie:

; 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

Przy okazji znalazłem brzydki jedną wkładkę, która działa bo and zwraca ostatnią rzeczą na liście argumentów, jeśli wszystkie są prawdziwe:

(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")) 
Powiązane problemy