以不变应万变

    在之前的学习中我们学习了如何定义我们的集合,一个很自然的想法就是修改这个集合。

    我们可以使用 assoc 函数来替换一个集合中的元素

    assoc 函数的第一个参数是你要进行替换的集合,第二个参数是你要替换的元素所在“位置”,第三个参数是替换后的元素。

    返回值是修改过后的集合的值。

    然而,Clojure 里集合类型是不可变的。assoc 函数的替换其实并没有改变原集合元素的内容,它返回的是一个“新的”集合。

    我们可以通过以下代码观察:

    1. => (def my-vec ["第一个元素" "第二个元素"])
    2. => (assoc my-vec 0 "first")
    3. ["first" "第二个元素"]
    4. => (print my-vec)
    5. [第一个元素 第二个元素]
    6. nil

    在这段代码中,我们首先定义了一个 vector,然后我们使用 assoc 函数来对这个 vector 进行“替换”。把 0 号元素替换为 “first"。看起来我们已经改动了这个 my-vec。但是当我们使用 print 函数来观察这个集合的时候却发现,他的值没有发生变化。

    那我们如何保存改变之后的值呢?

    我们可以再给这个改变之后的值取一个名字。

    更常见的做法是把这个改变作为一个值,传递给下一个需要这个值的表达式(如一个函数。我们在之后的定义函数一节里再进行详细介绍)

    我们为什么需要这种不可变的设计呢?

    不再一一叙述每个参数是什么,大家在代码中观察,以及在自己的机器上运行观察结果,然后修改部分参数观察是否符合预期。

    1. => (def my-vec ["第一个元素" "第二个元素"])
    2. ("第零个元素" "第一个元素" "第二个元素")

    对于 vector 来说是末尾,但是对 list 来说是头部:

    1. => (def my-list '("第一个元素" "第二个元素"))
    2. #'my-clojure-study.core/my-list
    3. => (conj my-list "第三个元素")
    4. ("第三个元素" "第一个元素" "第二个元素")
    5. => my-list
    6. ("第一个元素" "第二个元素")

    conj 函数一样,它只保证以最快的速度合并。

    如果你接触过其它“可变的”编程语言,那么你需要提醒自己,使用 定义的不是变量,而是一个不可变值。

    而当你真的需要在两个操作之间交换一些信息,比如在多线程中共享一些资源,那么 Clojure 单独提供了一些类型。我们在以后的多线程部分再做详细讲解。