1: user=> (def some-account {:number :any-number :balance :any-balance :bank :any-bank})
2: #'user/some-account
3: user=> (contains? some-account :bank)
4: true
5: user=> (assoc some-account :owner :any-owner)
6: {:owner :any-owner, :number :any-number, :balance :any-balance, :bank :any-bank}
7: user=> (contains? some-account :owner)
8: false
9: user=> (def some-account (assoc some-account :owner :any-owner))
10: #'user/some-account
11: user=> (contains? some-account :owner)
12: true
13: user=> (dissoc some-account :owner)
14: {:number :any-number, :balance :any-balance, :bank :any-bank}
15: user=> (contains? some-account :owner)
16: true
任何人都可以解释这段代码吗?
为什么在(assoc some-account :owner :any-owner)
之后,(contains? some-account :owner)
会返回false
?
为什么,只有在(def some-account (assoc some-account :owner :any-owner))
后(contains? some-account :owner)
返回true
?
为什么在(dissoc some-account :owner)
之后,(contains? some-account :owner)
会返回true
?
我本能地尝试说(def some-account (assoc some-account :owner :any-owner))
。但为什么这样做呢?
答案 0 :(得分:6)
Clojure中的地图是不可变的。
也就是说,更新功能而不是修改原始地图,返回一个新的更新地图。
尝试以下方法:
user=> (def some-account {:number :any-number :balance :any-balance :bank :any-bank})
#'user/some-account
user=> (contains? some-account :bank)
true
user=> (def updated-map (assoc some-account :owner :any-owner))
#'user/updated-map
user=> (contains? updated-map :owner)
true
user=>
答案 1 :(得分:3)
值得注意的是,当更新功能生成新地图时,它们不会复制旧地图它们会生成一个新的地图,它共享所有未更改旧部分的部分并且只替换更改所需的部件。
这种结构共享对于所有具有[不可变数据结构] [1]
的函数式语言非常重要[1]:http://clojure.org/functional_programming#Functional编程 - 不可变数据结构
答案 2 :(得分:2)
在Clojure中,所有数据类型都是不可变的。因此,assoc
地图上的some-account
操作不会对其进行更改(与Java中put
上的java.util.Map
操作不同)并生成新地图。这就是countain?
操作返回false的原因。当你这样做
(def some-account (assoc some-account :owner :any-owner))
您正在捕获assoc
操作的返回值并将其分配给some-account
变量。从某种意义上说,你正在重新定义它。所以稍后contain?
操作返回true。
答案 3 :(得分:1)
因为assoc和dissoc返回新的objekts并且不会更改某个帐户
答案 4 :(得分:1)
想象一下,您使用的是数字而不是地图:
user> (def s 3)
#'user/s
user> (= s 3)
true
user> (+ 1 s)
4
user> (= s 4)
false
user> (def s (+ 1 s)) ;;don't do this! changing definitions shouldn't be used in a program, only at the REPL for trying things out!
#'user/s
user> (= s 4)
true
user> (- s 1)
3
user> (= s 4)
在Clojure中,大多数值的行为与数字相似。有一些可变的东西,但它们隐藏在神秘的界面背后。如果没有它们,可以完成大量的编程。
但是我们如何在不改变变量的情况下进行编程?您可能已经看过阶乘函数
(defn factorial [n]
(if (< n 2) 1
(* n (factorial (dec n)))))
user> (factorial 5)
120
这是一个类似的功能,以相同的方式构建地图
(defn char-map [n]
(if (< n 0) {}
(assoc (char-map (dec n)) n (char n))))
user> (char-map 10)
{various control characters..}
这种风格起初很奇怪,但最终变得自然。当我想到这些天要编程的东西时,我经常会想到在命令循环之前的递归解决方案。
他们是两种看待同一事物的不同方式。
通常很容易在它们之间进行转换,因此如果您能想到一种方法,那么您已经想到了另一种方法。但这确实需要练习,比如学习说拉丁语。
在创建它们之后,如果事情没有改变,思路清晰度和线程安全性有一些优点。数学家似乎更喜欢它。