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

vie*_*bel 5 clojure clojurescript core.async

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

(defn foo [x]
  (go
    (<! (timeout 2000))
    (* 2 x)))
Run Code Online (Sandbox Code Playgroud)

(在现实生活中,为了缓存服务器调用的结果可能很有用)

我能够通过编写 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))))))
Run Code Online (Sandbox Code Playgroud)

用法示例:

(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
Run Code Online (Sandbox Code Playgroud)

我想知道是否有更简单的方法来实现相同的结果。

**备注:我需要一个适用于<!. 对于<!!,请参阅此问题:如何记忆使用 core.async 和阻塞通道读取的函数?**

Jes*_*lia 1

您可以使用内置的 memoize 函数来实现此目的。首先定义一个从通道读取并返回值的方法:

 (defn wait-for [ch]
      (<!! ch))
Run Code Online (Sandbox Code Playgroud)

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

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

(def foo-memo (memoize (comp wait-for foo)))
Run Code Online (Sandbox Code Playgroud)

foo返回一个通道,因此wait-for将阻塞直到该通道有一个值(即直到内部操作foo完成)。

foo-memo可以类似于上面的示例使用,但您不需要调用,<!因为wait-for它将为您阻塞:

(go (println (foo-memo 3))
Run Code Online (Sandbox Code Playgroud)

您还可以在 go 块之外调用它,它的行为就像您期望的那样(即阻塞调用线程直到 foo 返回)。