Clojure 的身份与状态思想

基本概念

Clojure 语言中没有简单的变量。但 Clojure 却声称最擅长管理状态,为什么?靠的是仔细设计的状态类型们,其中最引人注目和最常用的是 Atom 和 Ref(ClojureScript 中不可用)。

粗看起来,这两个东西区别很大:生成函数不同,操作函数也不同:

类型
生成函数
操作函数

Atom
atom

reset!, swap!

Ref
ref

ref-set, alter, commute

Ref 还要将操作包装在dosync中。

但在实际上,它们其实遵循同样的设计思路:所谓状态变量,指的是身份。例如:

(def person-data {:age 30, :salary 5000})
(def a-xiaoming (atom person-data))
(def r-xiaoming (ref person-data))
定义了两种不同的状态身份。它们实际上拥有同一个值:{:age 30},这个值是它的状态数据。在 clojure 语言中,状态身份是不会变的(可以理解为类似于 c 的指针),状态数据也不会变化,变化的是两边的联系。例如, 小明的个人数据是 person-data。这句话中,小明这个主体状态身份是不会变的,person-data 这个状态数据也不会变化,但小明年龄可能会关联到不同的状态数据,例如,过了年后可能关联为另一个状态数据 {:age 31, :weight 5500}。

函数推动状态数据的关联变化

推动状态的身份和数据的联系的变化是发生了某动作,也就是函数。因此,驱动状态是这样发生的:

(defn new-year [person-data]
(-> person-data
(update :age inc)
(update :salary # (* % 2))))

(swap! a-xiaoming # (update % :age inc :weight new-year)
注意,一个事件发生的函数应该是统一在一起的,它们是一个事务。clojure 的优秀数据结构保证这个事务的原子性。

下面这种写法呢?

(-> a-xiaoming
(swap! # (update % :age inc))
(swap! # (update :salary (fn [n] (* n 2)))))
事实上,对一个状态数据的连续操作一定是不正确的,它打破了事务的边界。ref 的dosync建立的边界也是一样。

关键字:clojure, ref, 函数, age

版权声明

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处。如若内容有涉嫌抄袭侵权/违法违规/事实不符,请点击 举报 进行投诉反馈!

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部