Clojure不变性实践

时间:2017-11-21 12:05:49

标签: clojure

我试图了解Clojure的不变性最佳实践,我有一个简单的例子,我不断重新声明(更新)“新订单”,但我不确定这是否正确。< / p>

(defrecord Order [fplate splate])

(def new-orders clojure.lang.PersistentQueue/EMPTY)

(defn add-order [orders order]
  (conj orders order))

(defn cook [order] ())

(defn cook-order [orders]
(cook (first orders)) (pop orders))

;;order1
(def o1 (->Order "Soup" "Fish&Chips"))
(def new-orders (add-order new-orders o1))

;;order2
(def o2 (->Order "Salad" "Hamburger"))
(def new-orders (add-order new-orders o2))

;;order3
(def o3 (->Order "Rice" "Steak"))
(def new-orders (add-order new-orders o3))

;;cook order
(def new-orders (cook-order new-orders))
(peek new-orders)

谢谢, R上。

2 个答案:

答案 0 :(得分:2)

这绝对不是在Clojure或任何其他FP语言中进行函数式编程的正确方法。尽管您已经使用Clojure作为实现语言,但您的代码仍然是必不可少的。

正确的方法是在需要某个数据结构的新版本时,使用修改后的内容创建新的数据结构。类似的东西:

(let [a []
         b (conj a "order1")
         c (conj b "order2")]
     (println c))

在这种情况下,conj通过向作为conj的参数给出的结构添加新元素来创建新的数据结构。旧结构不会以任何方式进行修改,这意味着它是不可变的。

如果你真的需要某种状态,Clojure就有这样的原语,比如原子。但首先尝试编写没有可修改的中心状态的功能代码。

答案 1 :(得分:1)

以下是执行此示例的更典型方法。它使用spyx-pretty函数from the Tupelo library,但如果您愿意,可以替换println

(ns tst.demo.core
  (:require [tupelo.core :as t] ))

(defrecord Order [fplate splate])

(def orders-queue (atom []))

(defn add-order [order]
  (swap! orders-queue conj order))

(defn cook [order] (println "cooking: " (pr-str order)))

(add-order (->Order "Soup" "Fish&Chips")) ; order1
(t/spyx-pretty orders-queue)

(add-order (->Order "Salad" "Hamburger")) ; order2
(t/spyx-pretty orders-queue)

(add-order (->Order "Rice" "Steak")) ; order3
(t/spyx-pretty orders-queue)

; cook orders
(newline)
(doseq [order @orders-queue]
  (cook order))

结果:

orders-queue => 
#<Atom@4147f771: [{:fplate "Soup", :splate "Fish&Chips"}]>

orders-queue => 
#<Atom@4147f771: 
  [{:fplate "Soup", :splate "Fish&Chips"}
   {:fplate "Salad", :splate "Hamburger"}]>

orders-queue => 
#<Atom@4147f771: 
  [{:fplate "Soup", :splate "Fish&Chips"}
   {:fplate "Salad", :splate "Hamburger"}
   {:fplate "Rice", :splate "Steak"}]>

cooking:  #tst.demo.core.Order{:fplate "Soup", :splate "Fish&Chips"}
cooking:  #tst.demo.core.Order{:fplate "Salad", :splate "Hamburger"}
cooking:  #tst.demo.core.Order{:fplate "Rice", :splate "Steak"}