Mám zavolanú mapu robots
. Pri vyhľadávaní na mape, ak robot podľa daného kľúča neexistuje, chcem ho vytvoriť. clojure.core/get
s not-found
arg sa javí ako presne to, čo potrebujem.
Zdá sa však, že not-found
výraz sa horlivo hodnotí. Ako môžem zabrániť tomuto vyhodnoteniu, aby som vytvoril nového robota, iba ak žiadny neexistuje?
robot> (def robots {1 {:name "R2D2"}})
#"robot/robots
robot> (get robots 1)
{:name "R2D2"}
robot> (get robots 1 (println "damn it"))
damn it
{:name "R2D2"}
odpovede:
3 pre odpoveď č. 1Plne s tym suhlasim Odpoveď Timothy Dean, ale jeho implementácia nebude fungovať, ak máte na svojej mape nepravdivé alebo nulové hodnoty.
Tu je presnejšie riešenie:
(if (contains? robots 1)
(get robots 1)
(println "Beep boop"))
A efektné makro:
(defmacro get-lazy [map key not-found]
`(let [map# ~map
key# ~key]
(if (contains? map# key#)
(get map# key#)
~not-found)))
Používam let
aby sa vylúčila možnosť spôsobiť dvakrát vedľajší účinok, napr .:
(get-lazy {1 false}
(do (println "Side effect") 1)
(println "Oops!"))
4 pre odpoveď č. 2
Problém je v tom, že nejde o rozvetvujúci príkaz - v skutočnosti očakáva hodnotu, nie formu alebo funkciu, v not-found
slot. Môžete teda použiť vetviaci príkaz:
(if-let [r (get robots 1)] r (println "Beep boop"))
Ak je to príliš veľa na písanie, môžete získať chuť:
(defmacro get-lazy
[map idx statement]
`(if-let [v# (get ~map ~idx)] v# ~statement))
user=> (get-lazy robots 1 (println "Oops!"))
{:name "R2D2"}
Po tipoch z komentárov A.Webba sa dostávame k nasledujúcemu makru. Používa rovnakú metódu ako vyššie a dokáže spracovať aj mapy obsahujúce false
a nil
hodnoty. Je to tiež rýchlejšie ako volanie oboch contains?
a get
na mape. Namiesto použitia falošnej návratovej hodnoty (false / nil), ktorá vás upozorní na to, že položka nie je prítomná, použite vstavanú funkcionalitu get
a vyhľadať niečo, čo by sa na „divokej“ mape nikdy nestalo.
(defmacro get-lazy
[map idx statement]
`(let [r# (get ~map ~idx ~::nil)]
(if (identical? r# ~::nil) ~statement r#)))
user=> (get-lazy {:idx 8} :idx (println "Yo."))
8
user=> (get-lazy {:coconuts :migration} :swallow (println "Who goes there?"))
Who goes there?
user=> (ns another.ns)
another.ns=> (user/get-lazy {:LUE ::nil} :LUE (println "42."))
:another.ns/nil
Môžete sa vyhnúť makro a ísť funkčnou cestou:
(let [sentinel ::nil]
(defn lazyget
[map idx function]
(let [r (get map idx sentinel)]
(if (identical? r sentinel) (function) r))))
Na mojom stroji je to o niečo pomalšie.