clojure解构(clojure destructuring)

[b]解构是什么?[/b]
可以认为map、list、struct等是构造出来数据结构。怎么取这些结构里面的数据?那就需要把结构拆开,取到数据。这就是解构(destructuring)。

[b]为什么要解构?[/b]
由于在clojure中如果只靠一些集合到操作函数如first、last、nth等函数,不能够简洁的获取一些嵌套集合中的元素,所以需要destructuring来使我们可以简洁快速的去获取集合中的元素。

[b]clojure中的解构[/b]
clojure支持的结构有:有顺序集合(sequential collection)的解构和map的解构。

一、Sequential destructuring

有序集合包括:
clojure的list,vector以及sequence。
所有实现java.util.List接口的集合
java数组
字符串

1、根据位置对应来解构

(def v [42 "foo" 99.2 [5 12]])
(let [[x y z][x _ _ [y z]] v]
(+ x y z))
;= 59

2、收集剩余的元素(Gathering extra-positional sequential values)

;;使用&来收集
(let [[x & rest] v]
rest)
;= ("foo" 99.2 [5 12])

3、保留解构的值(retaining the destructured value)

;;使用as来保留解构的值
(let [[x _ z :as original-vector] v]
(conj original-vector (+ x z)))
;= [42 "foo" 99.2 [5 12] 141.2]


二、Map destructuring

可以进行map解构的类型有:
clojure hash-map,array-map,record。
所有实现java.util.Map接口的集合
支持get函数获取相应值的对象,如:clojure vector,string,array

1、位置对应来解构

(def m {:a 5 :b 6
:c [7 8 9]
:d {:e 10 :f 11}
"foo" 88
42 false})
;;注意:这里key不单是:key类型,也可以是其他类型
(let [{a :a b :b} m]
(+ a b))
;= 11

;;注意:如果是数组、字符串或vector,解构的key是下标
(let [{x 3 y 8} [12 0 0 -18 44 6 0 0 1]]
(+ x y))
;= -17

;;顺序解构和map解构混合
(let [{[x _ y] :c} m]
(+ x y))
;= 16

(def map-in-vector ["James" {:birthday (java.util.Date. 73 1 6)}])
;= #'user/map-in-vector

(let [[name {bd :birthday}] map-in-vector]
(str name " was born on " bd))
;= "James was born on Thu Feb 06 00:00:00 EST 1973"

2、保留解构的值(retaining the destructured value)
和顺序解构一样用as来保存。

(let [{r1 :x r2 :y :as randoms}
(zipmap [:x :y :z] (repeatedly (partial rand-int 10)))]
(assoc randoms :sum (+ r1 r2)))
;= {:sum 17, :z 3, :y 8, :x 9}

3、默认值(Default values)

(let [k :unkown x :a
:or {k 50}
m]
(+ k x))
;;= 55

4、绑定值到对应的key上(binding values to their key‘s name)

;;这里的可以用:keys、:strs、:syms来指定key的类型
;;但是通常使用:keys类型
;;在很多框架中,经常会看到:keys,:or,:as的组合使用
(def chas {:name "Chas" :age 31 :location "Massachusetts"})
(let [{name :name age :age location :location} chas]
(format "%s is %s years old and lives in %s." name age location))
;= "Chas is 31 years old and lives in Massachusetts."

5、对剩余部分像map一样解构(Destructuring rest sequences as map key/value pairs)

;;这个代码太乱,有洁癖的人受不鸟
(def user-info ["robert8990" 2011 :name "Bob" :city "Boston"])
(let [[username account-year & extra-info] user-info
{:keys [name city]} (apply hash-map extra-info)]
(format "%s is in %s" name city))
;= "Bob is in Boston"

;;如果剩余部分的参数个数是偶数个的话,可以用map解构
;;有洁癖的人现在觉得世界清静多了
(let [[username account-year & {:keys [name city]}] user-info]
(format "%s is in %s" name city))
;= "Bob is in Boston"


[b]最后,虽然以上的例子只是用let,但是解构用在fn、defn等地方都是可以的。[/b]

(defn foo [{:keys [name age ] :as opts}]
(println name ":" age ))
;;异常
(foo :name "hello" :age 20)
;=ArityException Wrong number of args (4) passed to: core$foo clojure.lang.AFn.throwArity (AFn.java:437)
;;正常
(foo {:name "hello" :age 20})
;=hello : 20

(defn foo2 [& {:keys [name age]
:or {name "peter" age "18"}
:as opts}]
(println name ":" age))
;;异常
(foo2 {:name "hello" :age 20})
;;=IllegalArgumentException No value supplied for key: ......
;;正常
(foo2)
;;=peter : 18
(foo2 :name "peter2" :age 20)
;;=peter2 (+ x z))
;= 141.2

;;使用下划线占位
(let [[x _ _ [y z]] v]
(+ x y z))
;= 59

2、收集剩余的元素(Gathering extra-positional sequential values)

;;使用&来收集
(let [[x & rest] v]
rest)
;= ("foo" 99.2 [5 12])

3、保留解构的值(retaining the destructured value)

;;使用as来保留解构的值
(let [[x _ z :as original-vector] v]
(conj original-vector (+ x z)))
;= [42 "foo" 99.2 [5 12] 141.2]


二、Map destructuring

可以进行map解构的类型有:
clojure hash-map,array-map,record。
所有实现java.util.Map接口的集合
支持get函数获取相应值的对象,如:clojure vector,string,array

1、位置对应来解构

(def m {:a 5 :b 6
:c [7 8 9]
:d {:e 10 :f 11}
"foo" 88
42 false})
;;注意:这里key不单是:key类型,也可以是其他类型
(let [{a :a b :b} m]
(+ a b))
;= 11

;;注意:如果是数组、字符串或vector,解构的key是下标
(let [{x 3 y 8} [12 0 0 -18 44 6 0 0 1]]
(+ x y))
;= -17

;;顺序解构和map解构混合
(let [{[x _ y] :c} m]
(+ x y))
;= 16

(def map-in-vector ["James" {:birthday (java.util.Date. 73 1 6)}])
;= #'user/map-in-vector

(let [[name {bd :birthday}] map-in-vector]
(str name " was born on " bd))
;= "James was born on Thu Feb 06 00:00:00 EST 1973"

2、保留解构的值(retaining the destructured value)
和顺序解构一样用as来保存。

(let [{r1 :x r2 :y :as randoms}
(zipmap [:x :y :z] (repeatedly (partial rand-int 10)))]
(assoc randoms :sum (+ r1 r2)))
;= {:sum 17, :z 3, :y 8, :x 9}

3、默认值(Default values)

(let [k :unkown x :a
:or {k 50}
m]
(+ k x))
;;= 55

4、绑定值到对应的key上(binding values to their key‘s name)

;;这里的可以用:keys、:strs、:syms来指定key的类型
;;但是通常使用:keys类型
;;在很多框架中,经常会看到:keys,:or,:as的组合使用
(def chas {:name "Chas" :age 31 :location "Massachusetts"})
(let [{name :name age :age location :location} chas]
(format "%s is %s years old and lives in %s." name age location))
;= "Chas is 31 years old and lives in Massachusetts."

5、对剩余部分像map一样解构(Destructuring rest sequences as map key/value pairs)

;;这个代码太乱,有洁癖的人受不鸟
(def user-info ["robert8990" 2011 :name "Bob" :city "Boston"])
(let [[username account-year & extra-info] user-info
{:keys [name city]} (apply hash-map extra-info)]
(format "%s is in %s" name city))
;= "Bob is in Boston"

;;如果剩余部分的参数个数是偶数个的话,可以用map解构
;;有洁癖的人现在觉得世界清静多了
(let [[username account-year & {:keys [name city]}] user-info]
(format "%s is in %s" name city))
;= "Bob is in Boston"


[b]最后,虽然以上的例子只是用let,但是解构用在fn、defn等地方都是可以的。[/b]

(defn foo [{:keys [name age ] :as opts}]
(println name ":" age ))
;;异常
(foo :name "hello" :age 20)
;=ArityException Wrong number of args (4) passed to: core$foo clojure.lang.AFn.throwArity (AFn.java:437)
;;正常
(foo {:name "hello" :age 20})
;=hello : 20

(defn foo2 [& {:keys [name age]
:or {name "peter" age "18"}
:as opts}]
(println name ":" age))
;;异常
(foo2 {:name "hello" :age 20})
;;=IllegalArgumentException No value supplied for key: ......
;;正常
(foo2)
;;=peter : 18
(foo2 :name "peter2" :age 20)
;;=peter2 : 20

利用这个可以写出的DSL风格的代码可读性会很好。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值