我会这样写,使用my favorite template project & library:
(ns demo.core
(:use tupelo.core))
(defn next-x
[x x-tgt]
(cond
(< x x-tgt) {:x (inc x) :dir "E"}
(> x x-tgt) {:x (dec x) :dir "W"}
:else {:x x :dir ""}))
(defn next-y
[y y-tgt]
(cond
(< y y-tgt) {:y (inc y) :dir "N"}
(> y y-tgt) {:y (dec y) :dir "S"}
:else {:y y :dir ""}))
(defn update-state
[pos pos-goal]
(let [x-info (next-x (:x pos) (:x pos-goal))
y-info (next-y (:y pos) (:y pos-goal))
pos-next {:x (:x x-info) :y (:y y-info)}
dir-str (str (:dir y-info) (:dir x-info))
state-next {:pos-next pos-next :dir-str dir-str}]
state-next))
(defn walk-path [pos-init pos-goal]
(loop [pos pos-init]
(when (not= pos pos-goal)
(let [state-next (update-state pos pos-goal)]
(println (:dir-str state-next))
(recur (:pos-next state-next))))))
以及一些单元测试以显示它的工作原理:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(dotest
(is= (next-x 0 5) {:x 1, :dir "E"})
(is= (next-x 6 5) {:x 5, :dir "W"})
(is= (next-x 5 5) {:x 5, :dir ""})
(is= (next-y 0 5) {:y 1, :dir "N"})
(is= (next-y 6 5) {:y 5, :dir "S"})
(is= (next-y 5 5) {:y 5, :dir ""}))
(dotest
(is= (update-state {:x 0, :y 0} {:x 1, :y 1}) {:pos-next {:x 1, :y 1}, :dir-str "NE"})
(is= (update-state {:x 1, :y 0} {:x 1, :y 1}) {:pos-next {:x 1, :y 1}, :dir-str "N"})
(is= (update-state {:x 2, :y 0} {:x 1, :y 1}) {:pos-next {:x 1, :y 1}, :dir-str "NW"})
(is= (update-state {:x 0, :y 1} {:x 1, :y 1}) {:pos-next {:x 1, :y 1}, :dir-str "E"})
(is= (update-state {:x 1, :y 1} {:x 1, :y 1}) {:pos-next {:x 1, :y 1}, :dir-str ""})
(is= (update-state {:x 2, :y 1} {:x 1, :y 1}) {:pos-next {:x 1, :y 1}, :dir-str "W"})
(is= (update-state {:x 0, :y 2} {:x 1, :y 1}) {:pos-next {:x 1, :y 1}, :dir-str "SE"})
(is= (update-state {:x 1, :y 2} {:x 1, :y 1}) {:pos-next {:x 1, :y 1}, :dir-str "S"})
(is= (update-state {:x 2, :y 2} {:x 1, :y 1}) {:pos-next {:x 1, :y 1}, :dir-str "SW"}))
以及最终结果:
(dotest
(let [pos-init {:x 0 :y 0}
pos-goal {:x 3 :y 5}
str-result (with-out-str
(walk-path pos-init pos-goal))]
; (println str-result) ; uncomment to print result
(is-nonblank= str-result
"NE
NE
NE
N
N")))
函数 next-x 和 next-y 中仍有一些明显的重复可以合并,update-state 可以稍微清理一下,但我想保持简单,无需使用更高级的方式启动功能或辅助功能。
如需参考,请参阅this list of documentation sources,尤其是 Clojure CheatSheet 和《Getting Clojure》一书
关于您的问题:
-
(< y y-tgt) {:y (inc y) :dir "N"} -
Clojure 通常使用“关键字”而不是字符串来命名映射中的字段。在源代码中,它们在前面有一个冒号,而不是一对引号。
-
pos-next {:x (:x x-info) :y (:y y-info)} - 正确。返回值是一个带有键:x和:y的新映射,它们是从变量x-info和y-info中的映射复制而来的
-
将loop 语句视为类似于let 块。每对的第一个符号定义一个新的“循环变量”,每对的第二个符号是该变量的初始值。
由于只有 1 对,因此只有 1 个循环变量,因此 recur 语句中只有 1 个值。
loop/recur 表单可以有零个或多个循环变量。