是否存在在clojure或fp中实现撤消/重做功能的既定模式?
在OO语言中,我会使用命令模式,但因为它完全是关于状态我不知道它是否在clojure中是惯用的。
是否有任何图书馆可以提供帮助?
答案 0 :(得分:5)
与许多设计模式一样,您可以将其作为clojure中的函数实现。它取决于你如何在程序中代表状态(refs,atoms,agents),这个过程非常相似。
你只需向你的州添加一个观察者功能 agent / ref / atom,每次有更新时都会将状态添加到撤消列表中。然后你的撤销功能只是在撤消列表中查找。这样可以很好地将你的too添加到撤消列表中,同时允许重做
我的第一印象是ref
可能是正确的工具,因为您将能够以协调的方式恢复它们,除非您当然可以将程序状态缩减为单一身份(在Clojure意义上的话)然后你不需要协调更新,代理就可以了。
答案 1 :(得分:1)
好的,我让它像Arthur Ulfeldt建议的那样工作:
(defn cmd-stack [state-ref]
(let [stack (atom ['() '()])]
(add-watch state-ref :cmdhistory
(fn [key ref old new]
(let [redo-stack '()
undo-stack (conj (second @stack) old)]
(reset! stack [redo-stack undo-stack]))))
stack))
(defn can-redo? [stack]
(not (empty? (first @stack))))
(defn can-undo? [stack]
(not (empty? (second @stack))))
(defn undo! [stack state-ref]
(let [redo-stack (first @stack)
undo-stack (second @stack)
current-state @state-ref
last-state (first undo-stack)]
(assert (can-undo? stack) "cannot undo")
(reset! state-ref last-state)
(reset! stack [(conj redo-stack current-state) (drop 1 undo-stack)])))
(defn redo! [stack state-ref]
(let [redo-stack (first @stack)
undo-stack (second @stack)
current-state @state-ref
last-state (first redo-stack)]
(assert (can-redo? stack) "cannot redo")
(reset! state-ref last-state)
(reset! stack [(drop 1 redo-stack) (conj undo-stack current-state)])))
但我仍然不太明白为什么。自撤消!和重做!函数更新正在监视的原子,观察者是否应该对此作出反应,从而通过将撤消的值重新置于其上来搞乱命令堆栈?