(def asym-hobbit-body-parts [{:name "head" :size 3}
{:name "left-eye" :size 1}
{:name "left-ear" :size 1}
{:name "mouth" :size 1}
{:name "nose" :size 1}
{:name "neck" :size 2}
{:name "left-shoulder" :size 3}
{:name "left-upper-arm" :size 3}
{:name "chest" :size 10}
{:name "back" :size 10}
{:name "left-forearm" :size 3}
{:name "abdomen" :size 6}
{:name "left-kidney" :size 1}
{:name "left-hand" :size 2}
{:name "left-knee" :size 2}
{:name "left-thigh" :size 4}
{:name "left-lower-leg" :size 3}
{:name "left-achilles" :size 1}
{:name "left-foot" :size 2}])
This is a vector of maps.每个映射具有主体部分的名称和主体部分的相对大小。例如:动画人物眼睛有1/3的他们的头的大小defn has-matching-part?
[part]
(re-find #"^left-" (:name part)))
(defn matching-part
[part]
{:name (clojure.string/replace (:name part) #"^left-" "right-")
:size (:size part)})
(defn symmetrize-body-parts
"Expects a seq of maps which have a :name and :size"
[asym-body-parts]
(loop [remaining-asym-parts asym-body-parts
final-body-parts []]
(if (empty? remaining-asym-parts)
final-body-parts
(let [[part & remaining] remaining-asym-parts
final-body-parts (conj final-body-parts part)]
(if (has-matching-part? part)
(recur remaining (conj final-body-parts (matching-part part)))
(recur remaining final-body-parts))))))
(symmetrize-body-parts asym-hobbit-body-parts)
; => the following is the return value
[{:name "head", :size 3}
{:name "left-eye", :size 1}
{:name "right-eye", :size 1}
{:name "left-ear", :size 1}
{:name "right-ear", :size 1}
{:name "mouth", :size 1}
{:name "nose", :size 1}
{:name "neck", :size 2}
{:name "left-shoulder", :size 3}
{:name "right-shoulder", :size 3}
{:name "left-upper-arm", :size 3}
{:name "right-upper-arm", :size 3}
{:name "chest", :size 10}
{:name "back", :size 10}
{:name "left-forearm", :size 3}
{:name "right-forearm", :size 3}
{:name "abdomen", :size 6}
{:name "left-kidney", :size 1}
{:name "right-kidney", :size 1}
{:name "left-hand", :size 2}
{:name "right-hand", :size 2}
{:name "left-knee", :size 2}
{:name "right-knee", :size 2}
{:name "left-thigh", :size 4}
{:name "right-thigh", :size 4}
{:name "left-lower-leg", :size 3}
{:name "right-lower-leg", :size 3}
{:name "left-achilles", :size 1}
{:name "right-achilles", :size 1}
{:name "left-foot", :size 2}
{:name "right-foot", :size 2}]
1.2. let
上面有这样的话:
(let [[part & remaining] remaining-asym-parts
final-body-parts (conj final-body-parts part)]
some-stuff)
将左边绑定右边一个简单的例子:
(let [x 3]
x)
; => 3
(def dalmatian-list
["Pongo" "Perdita" "Puppy 1" "Puppy 2"]) ; and 97 more...
(let [dalmatians (take 2 dalmatian-list)]
dalmatians)
; => ("Pongo" "Perdita")
let也使用于新范围
(def x 0)
(let [x 1] x)
; => 1
(def x 0)
(let [x (inc x)] x)
; => 1
let的好处
;; Associate "part" with the first element of "remaining-asym-parts"
;; Associate "remaining" with the rest of the elements in "remaining-asym-parts"
;; Associate "final-body-parts" with the result of (conj final-body-parts part)
(let [[part & remaining] remaining-asym-parts
final-body-parts (conj final-body-parts part)]
(if (has-matching-part? part)
(recur remaining (conj final-body-parts (matching-part part)))
(recur remaining final-body-parts)))
注意 part
, remaining
, 和 final-body-parts
在let中
每一个都可以利用多次. 如果不用 part
, remaining
, and final-body-parts
会很复杂,例如:
(if (has-matching-part? (first remaining-asym-parts))
(recur (rest remaining-asym-parts)
(conj (conj (conj final-body-parts part) (first remaining-asym-parts))
(matching-part (first remaining-asym-parts))))
(recur (rest remaining-asym-parts)
(conj (conj final-body-parts part) (first remaining-asym-parts))))
let是一个将name和value联系起来的方式
(loop [iteration 0]
(println (str "Iteration " iteration))
(if (> iteration 3)
(println "Goodbye!")
(recur (inc iteration))))
; =>
Iteration 0
Iteration 1
Iteration 2
Iteration 3
Iteration 4
Goodbye!
第一行 loop [iteration 0]
开始循环并介绍绑定初始值,就像用default value去调用匿名函数 . 第一次遍历, iteration
值为0(inc iteration)
.。 (defn recursive-printer
([]
(recursive-printer 0))
([iteration]
(println iteration)
(if (> iteration 3)
(println "Goodbye!")
(recursive-printer (inc iteration)))))
(recursive-printer)
; =>
Iteration 0
Iteration 1
Iteration 2
Iteration 3
Iteration 4
Goodbye!
正则表达式是用于对文本进行模式匹配工具。我不会进入他们的工作,但这里是他们的文字符号:
;; pound, open quote, close quote
#"regular-expression"
在第一个model处理中,有re-find
返回true or false 根据part's name是否starts with "left-":
(defn has-matching-part?
[part]
(re-find #"^left-" (:name part)))
(has-matching-part? {:name "left-eye"})
; => true
(has-matching-part? {:name "neckbeard"})
; => false
matching-part用正则表达式来用"right-"替换"left-"
(defn matching-part [part] {:name (clojure.string/replace (:name part) #"^left-" "right-") :size (:size part)}) (matching-part {:name "left-eye" :size 1}) ; => {:name "right-eye" :size 1}]
重点分析下 symmetrizer 。
(def asym-hobbit-body-parts [{:name "head" :size 3} {:name "left-eye" :size 1} {:name "left-ear" :size 1} {:name "mouth" :size 1} {:name "nose" :size 1} {:name "neck" :size 2} {:name "left-shoulder" :size 3} {:name "left-upper-arm" :size 3} {:name "chest" :size 10} {:name "back" :size 10} {:name "left-forearm" :size 3} {:name "abdomen" :size 6} {:name "left-kidney" :size 1} {:name "left-hand" :size 2} {:name "left-knee" :size 2} {:name "left-thigh" :size 4} {:name "left-lower-leg" :size 3} {:name "left-achilles" :size 1} {:name "left-foot" :size 2}]) (defn has-matching-part? [part] (re-find #"^left-" (:name part))) (defn matching-part [part] {:name (clojure.string/replace (:name part) #"^left-" "right-") :size (:size part)}) ; ~~~1~~~ (defn symmetrize-body-parts "Expects a seq of maps which have a :name and :size" [asym-body-parts] ; (loop [remaining-asym-parts asym-body-parts ; ~~~2~~~ final-body-parts []] (if (empty? remaining-asym-parts) ; ~~~3~~~ final-body-parts (let [[part & remaining] remaining-asym-parts ; ~~~4~~~ final-body-parts (conj final-body-parts part)] (if (has-matching-part? part) ; ~~~5~~~ (recur remaining (conj final-body-parts (matching-part part))) ; ~~~6~~~ (recur remaining final-body-parts))))))1.该功能采用了是常见于函数式编程的一般的策略。给定序列(a vector of body parts and their sizes ),连续的序列分割成一个“ head”和“ tail”。处理head,将它添加到了一些成绩,然后用递归来继续该过程的tail。
2. 循环开始在body parts。该序列的“tail”将被绑定到remaining-asym-parts。最初,它被绑定到完整序列传递给function,remaining-asym-parts。创建一个结果序列,final-body-parts
;它的初始值是一个空vector。
3. 如果remaining-asym-parts
是空的,这意味着我们处理整个序列,并且可以返回结果 final-body-parts
。
4. 否则,split the list into a head, part
, and tail, remaining
. 另外, 向 final-body-parts
添加 part
,然后重新绑定结果final-body-parts
.
5. 如果是这样,matching-part
加至final-body-parts
and recur(循环),否则,只是recur。
形式"process each element in a sequence and build a result"用 reduce函数。例如:
;; sum with reduce (reduce + [1 2 3 4]) ; => 10
就等于clojure中的
(+ (+ (+ 1 2) 3) 4)
因此, reduce works by doing this:
Reduce 也可选的初始值. 15是这里的初始值:
(reduce + 15 [1 2 3 4])如果提供的初始值,然后通过施加给定的函数的初始值和所述序列的第一个元素开始reduce,而不是该序列的前两个元素。
(defn my-reduce ([f initial coll] (loop [result initial remaining coll] (let [[current & rest] remaining] (if (empty? remaining) result (recur (f result current) rest))))) ([f [head & tail]] (my-reduce f (f head (first tail)) (rest tail))))我们可以重新实现 symmetrize,如下所示:
(defn better-symmetrize-body-parts "Expects a seq of maps which have a :name and :size" [asym-body-parts] (reduce (fn [final-body-parts part] (let [final-body-parts (conj final-body-parts part)] (if (has-matching-part? part) (conj final-body-parts (matching-part part)) final-body-parts))) [] asym-body-parts))
现在,让我们创建一个函数,将决定 which part of the hobbit被击中:
(defn hit [asym-body-parts] (let [sym-parts (better-symmetrize-body-parts asym-body-parts) body-part-size-sum (reduce + 0 (map :size sym-parts)) target (inc (rand body-part-size-sum))] (loop [[part & rest] sym-parts accumulated-size (:size part)] (if (> accumulated-size target) part (recur rest (+ accumulated-size (:size part))))))) (hit asym-hobbit-body-parts) ; => {:name "right-upper-arm", :size 3} (hit asym-hobbit-body-parts) ; => {:name "chest", :size 10} (hit asym-hobbit-body-parts) ; => {:name "left-eye", :size 1}