如何记忆使用core.async和阻塞通道读取的函数?

时间:2014-07-16 13:17:47

标签: clojure core.async

我想将memoize用于使用core.async<!!的功能,例如

(defn foo [x]
  (go
    (<!! (timeout 2000))
    (* 2 x)))

(在现实生活中,它可能有助于缓存服务器调用的结果)

我能够通过编写memoize的core.async版本来实现这一点(几乎与memoize相同的代码):

(defn memoize-async [f]
  (let [mem (atom {})]
    (fn [& args]
      (go
        (if-let [e (find @mem args)]
          (val e)
         (let [ret (<! (apply f args))]; this line differs from memoize [ret (apply f args)]
            (swap! mem assoc args ret)
            ret))))))

使用示例:

(def foo-memo (memoize-async foo))
(go (println (<!! (foo-memo 3)))); delay because of (<!! (timeout 2000))

(go (println (<!! (foo-memo 3)))); subsequent calls are memoized => no delay

我想知道是否有更简单的方法可以达到相同的效果。

备注:我需要一个适用于<!!的解决方案。对于<!,请参阅此问题:How to memoize a function that uses core.async and non-blocking channel read?

1 个答案:

答案 0 :(得分:4)

您可以使用内置的memoize功能。首先定义一个从通道读取并返回值的方法:

 (defn wait-for [ch]
      (<!! ch))

请注意,我们会使用<!!而不是<!,因为我们需要此功能块,直到所有情况下都有通道上的数据。 <!仅在go块内的表单中使用时才会出现此行为。

然后,您可以通过将此函数与foo组合来构建您的memoized函数,如下所示:

(def foo-memo (memoize (comp wait-for foo)))

foo会返回一个频道,因此wait-for会阻止,直到该频道有一个值(即直到foo内的操作完成)。

foo-memo可以与您上面的示例类似,但您不需要调用<!!,因为wait-for会阻止您:

(go (println (foo-memo 3))

你也可以在go块之外调用它,它会表现得像你期望的那样(即阻止调用线程直到foo返回)。