【问题标题】:Functional equivalent to the concurrent multimap功能等同于并发多图
【发布时间】:2012-03-22 13:09:08
【问题描述】:

another question 中,我询问了Java 的并发多图。

是否可以在 Scala 或 Clojure 程序中使用一些函数式编程(不可变)模式?我认为 Scala 解决方案可能会涉及演员和 clojure 一个 atomrefagent,但可能有更好的方法。由于这两种语言都允许“回退”到 Java 互操作并仅使用 Java 解决方案,因此我可以使用第一个问题得到的任何答案,但这不符合函数式编程范式。 Haskell 程序员如何解决这个问题?

【问题讨论】:

  • Haskell 不允许突变是一个常见的误解。确实如此。问题是必须在类型中适当地注明所有突变;因此,将变异操作组合在一起的典型方法是使用 monad 接口。 Haskell 可以像 Scala 和 Clojure 一样“退回”到可变性,除了在 Haskell 中你不能作弊并让它看起来像一个函数是纯的,而实际上它不是。但是,如果您可以将突变完全封装在您的函数中,那么它仍然可以暴露一个纯接口。

标签: scala clojure functional-programming


【解决方案1】:

标准 Clojure 映射和集合是不可变的(并且是持久的)[1],因此它们在并发程序中同样有效。您可能希望根据您的要求将它们存储在 ref/agent/var/atom 中,并且您可以像以往一样更新 ref/agent/var/atom。

如果值实际上是 refs,你可以有一个更可变的映射,像这样:

{:a (ref #{1 2 3})
 :b (ref #{4 5 6})}

在这种情况下,您将能够实际向已经存在的键添加值(当然是在事务中)。添加和删​​除键仍然会返回新的地图,这些地图将与原始地图共享相同的 refs,因此对其中一个的更改将对其他人可见:

user=> (def mmap {:a (ref #{1 2 3}) :b (ref #{4 5 6})})
#'user/mmap
user=> mmap
{:a #<Ref@be0446: #{1 2 3}>, :b #<Ref@10aa282: #{4 5 6}>}
user=> (def mmap2 (assoc mmap :c (ref #{7 8 9})))
#'user/mmap2
user=> mmap2
{:c #<Ref@405f6: #{7 8 9}>, :a #<Ref@be0446: #{1 2 3}>, :b #<Ref@10aa282: #{4 5 6}>}
user=> mmap
{:a #<Ref@be0446: #{1 2 3}>, :b #<Ref@10aa282: #{4 5 6}>}
user=> (dosync (alter (:a mmap2) conj 0))
#{0 1 2 3}
user=> mmap
{:a #<Ref@be0446: #{0 1 2 3}>, :b #<Ref@10aa282: #{4 5 6}>}
user=> mmap2
{:c #<Ref@405f6: #{7 8 9}>, :a #<Ref@be0446: #{0 1 2 3}>, :b #<Ref@10aa282: #{4 5 6}>}

[1] 即添加/删除/修改键,值实际上返回一个新的映射,而不改变原来的。

【讨论】:

    【解决方案2】:

    函数式数据结构是不可变的数据结构。不可变数据结构不存在并发问题,因为它们无法修改。

    【讨论】:

    • 是一个 Actor(或类似 Actor 的类)管理此数据结构的唯一线程安全方式。
    • @Ralph 一点也不。 Actor 很有用,但它们并不是任何并发问题的包罗万象。正如 Daniel saya,您不需要对不可变结构进行任何形式的管理。那么问题就变成了您是否需要可变性,如果需要,需要以何种方式。更多细节会在这里有所帮助。
    【解决方案3】:

    【讨论】:

      猜你喜欢
      • 2010-09-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多