【问题标题】:Cleansing a Map of Its Channels清理其频道地图
【发布时间】:2016-03-10 04:38:18
【问题描述】:

假设我们有一个地图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(或其他任何名称)的返回值本身是否是一个通道也无关紧要。即:

`(cleanse-map m) ;=> (go {:a "a" :b "b" :c "c" :d "d"})

【问题讨论】:

    标签: clojure clojurescript core.async


    【解决方案1】:

    Limitationscore.async 使得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))))
    

    基本上,这里做了什么:

    1. 每个映射条目都被转换为包含该映射条目的通道。如果map entry的值为channel,则在映射函数中的go-block内提取。
    2. 带有地图条目的频道是merge-d 为单一频道。完成此步骤后,您将拥有一个包含地图条目集合的频道。
    3. 带有地图条目的频道被转换为将包含所需地图的频道(a/into 步骤)。

    【讨论】:

    • 令人难以置信的直接和优雅的答案。
    【解决方案2】:
    (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"}

    【讨论】:

    • FWIW 我会用go-loop 而不是doseq/swap! 来写它,但这个计划并没有什么特别的问题。
    • 我无法让它在 clojurescript 中工作。那里有ReadPort 吗?
    • 它只是在不同的命名空间下...cljs.core.async.impl.protocolsgithub.com/clojure/core.async/blob/master/src/main/clojure/cljs/…
    • @amalloy 好主意,我把它改成了一个 go-loop 来摆脱那个讨厌的原子
    猜你喜欢
    • 1970-01-01
    • 2015-12-25
    • 1970-01-01
    • 1970-01-01
    • 2014-11-26
    • 2015-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多