说我有这样的董事会
(def board {:a 10 :b 12})
Run Code Online (Sandbox Code Playgroud)
以及这样的功能列表
(def funcs [(assoc board :a 2)
(assoc board :b 4)
(assoc board :a (inc (get board :a)))])
Run Code Online (Sandbox Code Playgroud)
我如何将列表中的每个操作应用到我的电路板上,每次都以功能方式更新.
funcs在我的repl中进行评估,在调用每个函数后给出一个返回值列表,但每次都会保持电路板本身不变
user=> funcs
[{:a 2, :b 12} {:a 10, :b 4} {:a 11, :b 12}
Run Code Online (Sandbox Code Playgroud)
理想情况下,我希望每次运行函数时更新电路板的值
所以在运行all命令后,板的最终状态应为:
{:a 3 :b 4}
我知道这可能是使用尾递归但我想避免这样做,因为我几乎可以肯定reduce/ apply/ 的组合map会做的伎俩
clojure的一个定义特征是它的数据结构是不可变的.这意味着board永远不会改变,对数据结构进行操作的函数返回数据结构的修改副本.所以你所做的funcs就是制作一个三个不同电路板的矢量,原始电路板应用了功能.
通常你想要的是将一堆函数应用于某个初始值,其中每个函数都先获取函数的返回值,然后使用返回的值作为某些东西.通常在函数参数中传递它.
;; First of all, there's a function for that
(assoc board :a (inc (get board :a)))
;; The function update takes a map and a key and a function
;; It applies the function to value currently at key,
;; then sets key in the retuned "copy" of the map to be the return value of the function.
;; Equivalent to the above
(update board :a inc)
Run Code Online (Sandbox Code Playgroud)
如果你想要一个应用了这些功能的更新板,你需要通过函数传递返回值,因为原始板永远不会改变,它们都只返回其输入的更新副本.
(def updated-board
;; The innermost forms are evaluated first.
(update (assoc (assoc board :a 2) :b 4) :a inc))
Run Code Online (Sandbox Code Playgroud)
通过使用->或"线程优先"宏可以使这更具可读性.它采用初始值和缺少第一个参数的表单,然后重写代码以"线程化"每个参数的返回值作为下一个参数的第一个参数.
(def updated-board-threaded
(-> board
(assoc :a 2)
(assoc :b 4)
(update :a inc)))
;; You can expand the macro to see for yourself
;; that the two examples are exactly equivalent.
(macroexpand-1 '(-> board
(assoc :a 2)
(assoc :b 4)
(update :a inc)))
;;=> (update (assoc (assoc board :a 2) :b 4) :a inc)
Run Code Online (Sandbox Code Playgroud)
这是"在clojure中思考"的方法,函数通常只接受不可变值并返回其他不可变值.
但有时你需要一些可变的东西,而clojure以原子的形式提供它.原子可以被认为是包含不可变值的可变框.
它使用函数交换!并重置!应用受控突变.并且函数deref获取当前值.
(def board (atom {:a 10, :b 12}))
;; I'll define a function that takes a board and returns an updated version of it.
(defn do-stuff-with-board [b]
(-> b
(assoc :a 2)
(assoc :b 4)
(update :a inc)))
;; Get the current value of board.
(deref board) ;;=> {:a 10, :b 12}
;; Swap takes an atom and a function and
;; sets the value of the atom to be the return value of the function
(swap! board do-stuff-with-board)
;; Now the mutable board atom contains a new immutable value.
(deref board) ;;=> {:a 3, :b 4}
;; derefing an atom is a very usual operation, so there's syntax sugar for it
;; Equivalent to (deref board)
@board ;;=> {:a 3, :b 4}
Run Code Online (Sandbox Code Playgroud)
reset!将board的值设置为另一个值,例如=在"普通"语言中.这样做通常不是惯用的,因为它有点对读者说,原子的新价值与旧的价值无关,但是clojure是务实的,有时它就是你所需要的.
(reset! board "And now for something completely different.")
;; The value is now a string.
@board ;;=> "And now for something completely different."
Run Code Online (Sandbox Code Playgroud)
作为旁白.数据结构实际上并不是彼此的深层副本,幕后有魔力使其几乎与更新数据结构一样有效,但从程序员的角度来看,它们相当于其他语言的深层副本.
| 归档时间: |
|
| 查看次数: |
224 次 |
| 最近记录: |