變數可變性問題

寒凝雪發表於2017-05-02

從FP的角度, Clojure中變數是不可變的, 改變一個變數實際是建立一個新的變數

所以所有的change都需要通過引數的不斷傳遞…

如下面的例子,

=> (defrecord Employee [name room])
backtype.storm.util.Employee
=> (def emp (Employee. "John Smith" 304))
#`backtype.storm.util/emp
=> (:name emp)
"John Smith"
=> (assoc emp :room 309)
#backtype.storm.util.Employee{:name "John Smith", :room 309}
=> (println emp)
#backtype.storm.util.Employee{:name John Smith, :room 304}

Clojure是一個妥協的語言, 
不單純的從FP的角度思考, 也需要從OO的角度思考, 你可以認為這是靈活的體現 
所以有時候, 單純的依賴引數的傳遞很麻煩…雖然很pure FP

希望有變數的可變性, 將結果暫存下來, 這樣無疑帶來了副作用(side effects), 但提供了些便利, 尤其對習慣於oo思維的工程師

 

兩種方法 ,

1. 使用java物件 
雖然說clojure變數是不可變的, 但是如果在clojure裡面直接使用Java物件, 相當於跳過了clojure這層, 如下面的例子,

=> (import `java.awt.Point)
java.awt.Point
=> (def pt (Point. 5 10))
#`backtype.storm.util/pt
=> (.x pt)
5
=> (set! (.x pt) -42)
-42
=> (.x pt)
-42 

可以看到在storm裡面, 仍然有大量的程式碼是用java實現的, 尤其是類的封裝, 為什麼不全用clojure? 可以思考

 

2. 當然clojure並不是沒有考慮到這個問題, 他提倡的是管理可變變數

通過ref, atom, 其實是定義reference, 變數本身是不變的, 可以通過swap!將ref或atom切換到不同的變數上

=> (def test-ref (atom {}))
#`backtype.storm.util/test-ref
=> (swap! test-ref assoc :a 1)
{:a 1}
=> @test-ref
{:a 1}
本文章摘自部落格園,原文釋出日期:2013-08-16


相關文章