шаблон для реализации отмены / повтора в clojure - PullRequest
6 голосов
/ 24 марта 2012

Существует ли установленный шаблон для реализации функций отмены / возврата в clojure или в fp в целом?

На языке OO я хотел бы использовать шаблон команды, но поскольку все дело в состоянии, мне интересно, идиоматично ли это делать в ближайшем будущем.

Есть ли какие-нибудь библиотеки, которые могли бы помочь?

Ответы [ 2 ]

5 голосов
/ 24 марта 2012

Как и во многих шаблонах проектирования, вы можете реализовать его как функцию в clojure. Это немного зависит от того, как вы представляете состояние в своей программе (ссылки, атомы, агенты) в процессе очень похожи.

Вы можете просто добавить функцию наблюдения в ваше состояние agent / ref / atom, которая добавляет состояние в список отмены каждый раз, когда происходит обновление. тогда ваша функция отмены просто ищет ее в списке отмен. Это имеет приятный эффект добавления вашего unto в список отмены, позволяя также повторить

У меня первое впечатление, что ref s может быть правильным инструментом для этого, потому что вы сможете восстановить их все скоординированным образом, если, конечно, вы не сможете уменьшить состояние своих программ до единой идентичности Замените смысл слова) тогда вам не понадобится скоординированное обновление и агент сработает.

1 голос
/ 25 марта 2012

Хорошо, я заставил это работать так, как предложил Артур Ульфельдт:

(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)])))

Но я до сих пор не совсем понимаю, почему.С отменой!и повтори!функции обновляют атом, который отслеживается, разве наблюдатель не должен реагировать на это и, таким образом, портить стек команд, возвращая ему отмененное значение?

...