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!"))