清理其渠道地图
Cleansing a Map of Its Channels
假设我们有一个具有以下结构的映射 m
:
{:a (go "a")
:b "b"
:c "c"
:d (go "d")}
如图所示,m
有四个键,其中两个包含频道。
问题:如何编写一个通用函数(或宏?)cleanse-map
,它采用像m
并输出其无通道版本(在本例中为 {:a "a" :b "b" :c "c" :d "d"}
)?
这个问题的一个很好的辅助函数可能如下:
(defn chan? [c]
(= (type (chan)) (type c)))
如果 cleanse-map
的 return 值(或任何名称)本身就是一个频道,也没有关系。即:
`(cleanse-map m) ;=> (go {:a "a" :b "b" :c "c" :d "d"})
core.async
的 Limitations 使得 cleanse-map
的实现不是那么简单。但下面的应该有效:
(defn cleanse-map [m]
(let [entry-chs (map
(fn [[k v]]
(a/go
(if (chan? v)
[k (a/<! v)]
[k v])))
m)]
(a/into {} (a/merge entry-chs))))
基本上,这里做了什么:
- 每个地图条目都被转换为一个包含该地图条目的通道。如果映射条目的值是通道,则在映射函数内的
go
-块内提取它。
- 带有地图条目的频道
merge
-d 变成了一个。完成此步骤后,您将拥有一个包含地图条目集合的频道。
- 带有地图条目的频道转换为将包含所需地图的频道(
a/into
步骤)。
(ns foo.bar
(:require
[clojure.core.async :refer [go go-loop <!]]
[clojure.core.async.impl.protocols :as p]))
(def m
{:a (go "a")
:b "b"
:c "c"
:d (go "d")
:e "e"
:f "f"
:g "g"
:h "h"
:i "i"
:j "j"
:k "k"
:l "l"
:m "m"})
(defn readable? [x]
(satisfies? p/ReadPort x))
(defn cleanse-map
"Takes from each channel value in m,
returns a single channel which will supply the fully realized m."
[m]
(go-loop [acc {}
[[k v :as kv] & remaining] (seq m)]
(if kv
(recur (assoc acc k (if (readable? v) (<! v) v)) remaining)
acc)))
(go (prn "***" (<! (cleanse-map m))))
=> "***" {:m "m", :e "e", :l "l", :k "k", :g "g", :c "c", :j "j", :h "h", :b "b", :d "d", :f "f" , :i "i", :a "a"}
假设我们有一个具有以下结构的映射 m
:
{:a (go "a")
:b "b"
:c "c"
:d (go "d")}
如图所示,m
有四个键,其中两个包含频道。
问题:如何编写一个通用函数(或宏?)cleanse-map
,它采用像m
并输出其无通道版本(在本例中为 {:a "a" :b "b" :c "c" :d "d"}
)?
这个问题的一个很好的辅助函数可能如下:
(defn chan? [c]
(= (type (chan)) (type c)))
如果 cleanse-map
的 return 值(或任何名称)本身就是一个频道,也没有关系。即:
`(cleanse-map m) ;=> (go {:a "a" :b "b" :c "c" :d "d"})
core.async
的 Limitations 使得 cleanse-map
的实现不是那么简单。但下面的应该有效:
(defn cleanse-map [m]
(let [entry-chs (map
(fn [[k v]]
(a/go
(if (chan? v)
[k (a/<! v)]
[k v])))
m)]
(a/into {} (a/merge entry-chs))))
基本上,这里做了什么:
- 每个地图条目都被转换为一个包含该地图条目的通道。如果映射条目的值是通道,则在映射函数内的
go
-块内提取它。 - 带有地图条目的频道
merge
-d 变成了一个。完成此步骤后,您将拥有一个包含地图条目集合的频道。 - 带有地图条目的频道转换为将包含所需地图的频道(
a/into
步骤)。
(ns foo.bar
(:require
[clojure.core.async :refer [go go-loop <!]]
[clojure.core.async.impl.protocols :as p]))
(def m
{:a (go "a")
:b "b"
:c "c"
:d (go "d")
:e "e"
:f "f"
:g "g"
:h "h"
:i "i"
:j "j"
:k "k"
:l "l"
:m "m"})
(defn readable? [x]
(satisfies? p/ReadPort x))
(defn cleanse-map
"Takes from each channel value in m,
returns a single channel which will supply the fully realized m."
[m]
(go-loop [acc {}
[[k v :as kv] & remaining] (seq m)]
(if kv
(recur (assoc acc k (if (readable? v) (<! v) v)) remaining)
acc)))
(go (prn "***" (<! (cleanse-map m))))
=> "***" {:m "m", :e "e", :l "l", :k "k", :g "g", :c "c", :j "j", :h "h", :b "b", :d "d", :f "f" , :i "i", :a "a"}