Clojure:如何避免评估map get中未找到的表达式
我有一张叫做机器人的地图。在查找地图时,如果给定密钥的机器人不存在,我想创建一个。使用未找到的Clojure:如何避免评估map get中未找到的表达式,clojure,Clojure,我有一张叫做机器人的地图。在查找地图时,如果给定密钥的机器人不存在,我想创建一个。使用未找到的arg似乎正是我所需要的 然而,notfound表达式似乎得到了热切的求值。如何防止这种评估,以便仅在不存在机器人的情况下创建新机器人 robot> (def robots {1 {:name "R2D2"}}) #'robot/robots robot> (get robots 1) {:name "R2D2"} robot> (get robots 1 (println "d
arg似乎正是我所需要的
然而,notfound
表达式似乎得到了热切的求值。如何防止这种评估,以便仅在不存在机器人的情况下创建新机器人
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"}
问题在于,这不是一个分支语句——它实际需要的是
notfound
槽中的值,而不是形式或函数。因此可以使用分支语句:
(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
的内置功能,检查“野生”映射中永远不会出现的内容
您可以避开宏,按功能路线操作:
(let [sentinel ::nil]
(defn lazyget
[map idx function]
(let [r (get map idx sentinel)]
(if (identical? r sentinel) (function) r))))
这在我的机器上稍微慢一点。我完全同意,但如果地图中的值为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!"))
谢谢对于我的用例,这将很好地工作。一般来说,这将是一个更好的选择。对于后代,我将选择它作为“正确”的一个。我更喜欢这个,因为它避免了两次遍历地图(包含?然后获取)。您可以创建不能在地图中作为未找到的哨兵的值:
(let[sentinel(Object.)](let[r(get robots 1 sentinel)](if(=r sentinel)(println“doh!”)r)))
但您通常也可以使用命名空间关键字::not found
来创建不在合法映射中的未找到值。在我的测试中,包含?
调用实际上不会比我的原始公式增加太多(如果有)开销。文档声明包含?
为常数/对数。虽然我期望第二个公式的速度和Leonid的一样快,但我发现它的速度是Leonid解决方案的两倍。当成功和失败的数量相等时,我测试bar
比foo
快,其中bar
是修补过的第二个公式(let[sentinel(Object.))(defn bar[m k f](let[r(获取MK哨兵)(如果(相同的?r哨兵)(f)r))
和foo
是(定义foo[MK f](如果(包含?MK)(获取MK)(f))
。
(get-lazy {1 false}
(do (println "Side effect") 1)
(println "Oops!"))