【问题标题】:Can anyone explain this code?谁能解释这段代码?
【发布时间】:2010-09-03 07:37:15
【问题描述】:
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))。但是为什么会这样呢?

【问题讨论】:

    标签: clojure types


    【解决方案1】:

    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=>
    

    【讨论】:

    • 干净编码的精彩介绍 :)
    【解决方案2】:

    需要注意的是,当更新函数生成新地图时,它们不会复制旧地图它们会生成一个新地图,该新地图与旧地图共享所有未更改的部分并且只更换变更所需的部件。

    这种结构共享对于所有具有[不可变数据结构][1]的函数式语言都非常重要

    [1]:http://clojure.org/functional_programming#Functional 编程--不可变数据结构

    【讨论】:

      【解决方案3】:

      在 Clojure 中,所有数据类型都是不可变的。因此some-account 映射上的assoc 操作不会更改它(与Java 中java.util.Map 上的put 操作不同)并生成一个新映射。这就是countain? 操作返回假的原因。当你这样做时

      (def some-account (assoc some-account :owner :any-owner))
      

      您正在捕获assoc 操作的返回值并将其分配给some-account 变量。从某种意义上说,你正在重新定义它。所以稍后contain? 操作返回true。

      【讨论】:

        【解决方案4】:

        因为 assoc 和 dissoc 返回新的对象并且不更改某些帐户

        【讨论】:

          【解决方案5】:

          假设您使用的是数字而不是地图:

          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..}
          

          这种风格一开始很奇怪,但最终变得自然。这些天,当我想到要编程的东西时,我经常会想到命令式循环之前的递归解决方案。

          它们是看待同一事物的两种不同方式。

          在它们之间进行转换通常很容易,因此,如果您能想到一种方法,那么您已经想到了另一种方法。但这确实需要练习,比如学习说拉丁语。

          在思路清晰和线程安全方面有一些优势,在创建它们之后保持不变。数学家似乎特别喜欢它。

          【讨论】:

            猜你喜欢
            • 2017-04-22
            • 2011-02-11
            • 1970-01-01
            • 1970-01-01
            • 2021-04-24
            • 1970-01-01
            相关资源
            最近更新 更多