我有一张叫robots的地图。在查找地图时,如果不存在给定键的机器人,我想创建一个。clojure.core/get与not-found arg似乎正是我所需要的。
但是,not-found表达式似乎被急切地计算出来了。我如何才能防止这种评估,以便我创造一个新的机器人,只有当一个不存在?
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"}发布于 2014-04-21 13:17:27
我完全同意Timothy Dean's answer,但是如果映射中有false或nil值,他的实现就不能工作。
以下是更精确的解决方案:
(if (contains? robots 1)
(get robots 1)
(println "Beep boop"))还有奇特的宏:
(defmacro get-lazy [map key not-found]
`(let [map# ~map
key# ~key]
(if (contains? map# key#)
(get map# key#)
~not-found)))我正在使用let消除两次引起某些副作用的可能性,例如:
(get-lazy {1 false}
(do (println "Side effect") 1)
(println "Oops!"))发布于 2014-04-21 13:00:04
问题是,这不是一个分支语句-它实际上希望在not-found槽中有一个值,而不是一个形式或函数。因此,您可以使用分支语句:
(if-let [r (get robots 1)] r (println "Beep boop"))如果打字量太大,你可能会觉得很花哨:
(defmacro get-lazy
[map idx statement]
`(if-let [v# (get ~map ~idx)] v# ~statement))
user=> (get-lazy robots 1 (println "Oops!"))
{:name "R2D2"}下面是A.Webb评论的提示,我们得出以下宏。它使用与上面相同的方法,还可以处理包含false和nil值的映射。它也比在地图上调用contains?和get更快。不要使用假返回值( false /nil)提示您该项不存在,而是使用get的内置功能,并检查“野生”映射中永远不会出现的内容。
(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您可以避开宏,选择功能路线:
(let [sentinel ::nil]
(defn lazyget
[map idx function]
(let [r (get map idx sentinel)]
(if (identical? r sentinel) (function) r))))这在我的机器上慢了一点。
https://stackoverflow.com/questions/23197520
复制相似问题