我刚刚开始使用Clojure,试图将我的大脑包裹在功能/不可变编程中.
我的一个简单的问题是 - 我有一个带有两个值的地图,我想从一个转移到另一个(在同一个地图内).这可以通过简单的功能完成吗?或者我必须进入ref
s和atom
s?
例如
(def bucket {:volume 100 :rate 10 :poured 0})
Run Code Online (Sandbox Code Playgroud)
如何移动volume
到poured
的rate
?
(defn pour
[bucket]
?
)
; -> {:volume 90 :rate 10 :poured 10}
Run Code Online (Sandbox Code Playgroud)
这两者都有.存储桶是一个值,函数将获取该值并生成一个基于原始值(和共享的未更改值)的新值:
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}
Run Code Online (Sandbox Code Playgroud)
它将原始存储桶保存在名为var的var中 bucket
user> bucket
{:rate 10, :volume 100, :poured 0}
Run Code Online (Sandbox Code Playgroud)
所以我们可以在桶值上定义一个函数:
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}
Run Code Online (Sandbox Code Playgroud)
当我们想要表达桶"身份"的想法时,这将非常有用.的身份进行的州值在一个非常有用的方式.我将使用一个原子,因为我希望一次同步更新一个单一的标识/事物.
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}
Run Code Online (Sandbox Code Playgroud)
现在我们的桶正在随着时间的推移而变化,这些变化对每个人都是可行的.值得注意的是,产生新值的函数完全独立于它用于改变原子并可在其他地方重复使用的事实.一些Clojurians将此称为" 简单 "
通常你会想要计算一些值,比如"倒桶三次的结果是什么,通过将每个调用嵌套到pour
下一个内部很好地表达,如下所示:
user> (pour (pour (pour bucket)))
{:rate 10, :volume 70, :poured 30}
Run Code Online (Sandbox Code Playgroud)
这种模式很常见,有两个字符宏来表达它: ->
user> (-> bucket pour pour pour)
{:rate 10, :volume 70, :poured 30}
Run Code Online (Sandbox Code Playgroud)
在实践中通过我嵌套和threadding看到的Clojure撰写功能多往往比通过可变状态sytems(原子,参考文献,代理,乏)