【问题标题】:Simple Clojure clarification re: hashes / values简单的 Clojure 澄清:哈希/值
【发布时间】:2014-08-13 20:30:37
【问题描述】:

我刚刚开始使用 Clojure,试图将我的大脑包裹在函数式/不可变编程上。

我的简单问题是 - 我有一个包含两个值的地图,我想将它们从一个转移到另一个(在同一个地图内)。这可以通过一个简单的功能来完成吗?还是我必须进入refs 和atoms?

例如

(def bucket {:volume 100 :rate 10 :poured 0})

如何将volume 移动到poured rate

(defn pour
  [bucket]
  ?
)

; -> {:volume 90 :rate 10 :poured 10}

【问题讨论】:

    标签: hash map clojure


    【解决方案1】:

    两者兼而有之。存储桶是一个值,一个函数将获取该值并生成一个基于原始值(和共享未更改值)的新值:

    user> (def bucket {:volume 100 :rate 10 :poured 0})
    #'user/bucket
    user> (assoc bucket 
            :volume (- (:volume bucket) (:rate bucket)) 
            :poured (+ (:poured bucket) (:rate bucket)))
    {:rate 10, :volume 90, :poured 10}
    

    它将原始存储桶留在名为bucket的变量中

    user> bucket
    {:rate 10, :volume 100, :poured 0}
    

    所以我们可以在桶值上定义一个函数:

    user> (defn pour [bucket]
            (assoc bucket
              :volume (- (:volume bucket) (:rate bucket))
              :poured (+ (:poured bucket) (:rate bucket))))
    #'user/pour
    
    user> (pour bucket)
    {:rate 10, :volume 90, :poured 10}
    user> bucket
    {:rate 10, :volume 100, :poured 0}
    

    当我们想要表达桶“身份”的概念时,这将非常有用。 identity carries values from state to state 非常有用。我将使用原子,因为我希望一次同步更新单个身份/事物。

    user> (def bucket (atom {:volume 100 :rate 10 :poured 0}))
    #'user/bucket
    
    user> (swap! bucket pour)
    {:rate 10, :volume 90, :poured 10}
    
    user> (swap! bucket pour)
    {:rate 10, :volume 80, :poured 20}
    
    user> (swap! bucket pour)
    {:rate 10, :volume 70, :poured 30}
    

    现在我们的存储桶随着时间的推移而变化,这些变化对每个人都是可行的。值得注意的是,产生新值的函数完全独立于它被用来改变原子并且可以在其他地方重用的事实。一些 Clojurians 将此称为“simple

    您通常会想要计算诸如“将桶倒三次的结果是什么,这可以通过将每个对 pour 的调用嵌套在下一个中很好地表达,如下所示:

    user> (pour (pour (pour bucket)))
    {:rate 10, :volume 70, :poured 30}
    

    这种模式很常见,有一个两个字符的宏来表达它:->

    user> (-> bucket pour pour pour)
    {:rate 10, :volume 70, :poured 30}
    

    在实践中,我通过嵌套和线程更多看到 clojure compose 函数,而不是通过可变状态系统(原子、引用、代理、变量)

    【讨论】:

    • 哇非常感谢您的详细回复!在第一个功能中(当您assocd 两个键时)是否有一小段时间值不同步?还是那些“同时”被应用?此外,如果ratepoured 的初始值的函数,这是否仍然有效(通过let)?或者我们是否必须进入dosync 以确保在我们进行计算时不会将倒出的值换出?
    • @Brandon 在assoc 函数中的值永远不会不同步。一个哈希图,因此传入的原始存储桶保证始终相同。可能令人困惑的是使用atom 的示例。请记住 atom 包含一个值,在这种情况下是一个不可变的哈希图。函数swap! bucket pour 将原子的值与(apply pour current-value-of-atom) 交换,所以传递给pour 的实际上是bucket 的值,一个不可变的hashmap。 pour 返回的新 hashmap 然后作为 bucket 的值换入。
    • 如果另一个线程同时改变了bucket的值,swap再次尝试,将函数应用到bucket的新值并交换结果。这是在自旋循环中完成的,因为函数,在这种情况下 pour 可能会被多次调用,它必须是无副作用的。
    • 感谢@dacamo76 的澄清 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-01
    • 1970-01-01
    • 2013-01-02
    • 2012-03-09
    • 2012-01-03
    • 2020-12-08
    相关资源
    最近更新 更多