轻松“撤消”在函数式数据结构中

发布于 2024-10-26 07:50:57 字数 654 浏览 3 评论 0原文

我听说纯函数式数据结构的好处之一是您可以免费获得撤消/重做操作。有人可以解释为什么吗?我不明白为什么在函数式语言中添加撤消/重做会更容易。

例如,假设我有以下队列实现:

data Queue a = Queue [a] [a]

newQueue :: Queue a
newQueue = Queue [] []

empty :: Queue a -> Bool
empty (Queue [] []) = True
empty _ = False

enqueue :: Queue a -> a -> Queue a
enqueue (Queue xs ys) y = Queue xs (y:ys)

dequeue :: Queue a -> (a, Queue a)
dequeue (Queue [] []) = error "Queue is empty!"
dequeue (Queue [] ys) = dequeue (Queue (reverse ys) [])
dequeue (Queue (x:xs) ys) = (x, Queue xs ys)

如何修改它以获取撤消和重做操作? (我可以想象入队和出队函数也返回两个列表,其中一个列表是队列的所有先前版本,另一个列表是队列的所有未来版本,这些列表充当我们的撤消/重做操作,但我猜这不是人们通常想到的。)

I've heard that one of the benefits of purely functional data structures is that you get undo/redo operations for free. Can someone explain why? I don't see why adding undo/redo is easier in a functional language.

For example, suppose I have the following implementation of a queue:

data Queue a = Queue [a] [a]

newQueue :: Queue a
newQueue = Queue [] []

empty :: Queue a -> Bool
empty (Queue [] []) = True
empty _ = False

enqueue :: Queue a -> a -> Queue a
enqueue (Queue xs ys) y = Queue xs (y:ys)

dequeue :: Queue a -> (a, Queue a)
dequeue (Queue [] []) = error "Queue is empty!"
dequeue (Queue [] ys) = dequeue (Queue (reverse ys) [])
dequeue (Queue (x:xs) ys) = (x, Queue xs ys)

How would I modify this to get undo and redo operations? (I could imagine that the enqueue and dequeue functions also return two lists, one of the lists being all the previous versions of the queue and the other list being all the future versions of the queue, and these lists act as our undo/redo operations, but I'm guessing this isn't what people typically have in mind.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

各空 2024-11-02 07:50:57

示例:

q1 = newQueue
q2 = enque q1 3

q1q2 的撤消,因为所有值都是不可变的。只需保留对先前值的引用即可。

Example:

q1 = newQueue
q2 = enque q1 3

then q1 is the undo of q2, since all values are immutable. Just keep a reference to the prior value.

梦里人 2024-11-02 07:50:57

撤消/重做在纯函数代码中更容易实现的原因是因为有两个保证:

  • 操作没有副作用
  • 数据结构是不可变的

只要您维护对旧数据结构的引用,您就可以随时恢复到旧的数据结构。如果您想存储整个历史记录,可以将其保存在列表中:

trackHistory :: Queue a -> [Queue a] -> [Queue a]
trackHistory currentQueue history = currentQueue : history

显然,在真实的应用程序中,您希望限制历史记录的大小,但您明白了。这是可行的,因为您可以保证列表中的每个队列都没有更改。

The reason undo/redo is easier to implement in purely functional code is because of two guarantees:

  • operations are side-effect free
  • data structures are immutable

You can always revert back to an old data structure as long as you maintain a reference to it. If you want to store the entire history, you could keep it in a list:

trackHistory :: Queue a -> [Queue a] -> [Queue a]
trackHistory currentQueue history = currentQueue : history

Obviously in a real application you would want to cap the history size, but you get the idea. This works because you can guarantee that each of the queues in your list hasn't been changed.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文