在clojure中实现undo / redo的模式

时间:2012-03-23 22:23:56

标签: design-patterns clojure functional-programming

是否存在在clojure或fp中实现撤消/重做功能的既定模式?

在OO语言中,我会使用命令模式,但因为它完全是关于状态我不知道它是否在clojure中是惯用的。

是否有任何图书馆可以提供帮助?

2 个答案:

答案 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)])))

但我仍然不太明白为什么。自撤消!和重做!函数更新正在监视的原子,观察者是否应该对此作出反应,从而通过将撤消的值重新置于其上来搞乱命令堆栈?