问题
I'd like to use memoize
for a function that uses core.async
and <!!
e.g
(defn foo [x]
(go
(<!! (timeout 2000))
(* 2 x)))
(In the real-life, it could be useful in order to cache the results of server calls)
I was able to achieve that by writing a core.async version of memoize (almost the same code as 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))))))
Example of usage:
(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
I am wondering if there are simpler ways to achieve the same result.
Remark: I need a solution that works with <!!
. For <!
, see this question: How to memoize a function that uses core.async and non-blocking channel read?
回答1:
You can use the built in memoize function for this. Start by defining a method that reads from a channel and returns the value:
(defn wait-for [ch]
(<!! ch))
Note that we'll use <!!
and not <!
because we want this function block until there is data on the channel in all cases. <!
only exhibits this behavior when used in a form inside of a go block.
You can then construct your memoized function by composing this function with foo
, like such:
(def foo-memo (memoize (comp wait-for foo)))
foo
returns a channel, so wait-for
will block until that channel has a value (i.e. until the operation inside foo
finished).
foo-memo
can be used similar to your example above, except you do not need the call to <!!
because wait-for
will block for you:
(go (println (foo-memo 3))
You can also call this outside of a go block, and it will behave like you expect (i.e. block the calling thread until foo returns).
来源:https://stackoverflow.com/questions/24781858/how-to-memoize-a-function-that-uses-core-async-and-blocking-channel-read