Clojure:defRecord,defProtocol:只进行一次昂贵的计算

时间:2012-05-27 23:07:19

标签: clojure

上下文

考虑以下代码

(defprotocol ICat "Foo"
  (meow [cat]))

(defrecord Cat [a b] "Cat"
  ICat
  (meow [cat] (some-expensive-operation a b)))

问题

有没有办法可以让某个地方进入那里?

在我执行时,我更愿意(一些昂贵的操作a b)被评估一次

(->Cat a b)

所以在(喵猫)时,它只返回预先缓存的值,而不是在运行中重新计算它。例如:

[1] (let [x (->Cat a b)]
[2]   (meow x)
[3]   (meow x)
[4]   (meow x))

我希望(一些昂贵的操作)在[1]处被恰好评估一次,然后对于[2],[3],[4]它只返回旧值。

3 个答案:

答案 0 :(得分:3)

我建议在构造函数中包装逻辑以调用昂贵的操作一次,并将结果作为常规值存储在记录中:

(defprotocol ICat "Foo"
  (meow [cat]))

(defrecord Cat [a b] "Cat"
  ICat
  (meow [cat] (:meow cat)))

(defn make-cat [a b]
  (assoc (->Cat a b) :meow (some-expensive-operation a b)))

当你的代码变得更复杂时,我发现你经常想要在任何情况下定义你自己的构造函数。

请注意,您可能还需要考虑以延迟序列或延迟包装昂贵的操作,以便只在需要时才进行计算。

答案 1 :(得分:1)

如果你的功能是参考透明的,那么你可以将你的功能包裹在memoize中。至少,您可以:

(def memo-some-expensive-function (memoize some-expensive-function))

然后在记录中使用memo-some-expensive-function

答案 2 :(得分:0)

我开始相信最简单的方法可能是

  • 围绕defrecord创建一个包装器

  • 这个包装器允许我指定我想要重新计算的其他字段

  • 并将这些额外字段附加到“真正的”defrecord