【问题标题】:Implement undo and redo functions using a stack. How to edit a stack without having to recreate it in Haskell使用堆栈实现撤消和重做功能。如何编辑堆栈而无需在 Haskell 中重新创建它
【发布时间】:2023-10-24 16:51:01
【问题描述】:

我有一个名为 TextFile 的自定义数据类型,它存储四个字符串,我需要能够在每次编辑文本文件时将它的一个版本存储在堆栈中。这样我就可以实现某种形式的撤消和重做功能。

但是,堆栈将从其他函数中更新,并且每次都不会创建新堆栈,当我向堆栈推送某些内容时,我看不到保存更改的方法吗?

有没有一种方法可以创建一个堆栈并在每次推送或弹出某些内容时更新同一个堆栈?

newtype Stack a = Stack [a] deriving (Eq, Show)
buffer = Stack [] :: Stack TextFile

data TextFile = TextFile String String String String deriving Show
file = TextFile "This is the left element" " This is the right element" "" ""

pop :: Stack a -> (Maybe a, Stack a)
pop (Stack (x:xs)) = (Just x, Stack xs)
pop (Stack []) = (Nothing, Stack [])

push :: a -> Stack a -> Stack a
push x (Stack xs) = Stack (x:xs)

澄清一下,我的主要问题是,如果您不能在 Haskell 中更改变量的值,那么如何在不复制堆栈的情况下将堆栈创建为结构?

【问题讨论】:

  • 这是 State monad 的经典用例,您可以在网上找到很多参考资料。例如。在LYAH 中进行了非常温和的介绍(甚至还有一个使用pushpop 方法对堆栈建模的示例,就像你的那样)。不过,您需要先了解 monad 的基础知识。
  • 创建带有更改的新堆栈和丢弃旧堆栈与更改旧堆栈有什么区别?查找可变数据结构与不可变数据结构。您可能仍然需要这里的帮助,在这种情况下,您应该提供有关您遇到的确切问题的更多详细信息(例如,您无法使其完全按照您的意愿执行的功能)。但总的来说 - 尝试创建一个新的!不会疼的(相信我)~
  • 扩展我的评论并将其链接到@ATayler 的评论 - 您当然可以按照他的方式进行操作,但是手动编写代码以将状态线程化到您编写的每个函数会变得非常乏味。这就是 State monad 的用途,它只是允许您通过将所有样板“隐藏在引擎盖下”(在 State monad 本身的实现中)来编写更清晰的代码。它不会做任何没有它就做不到的“魔法”。
  • Robin 是完全正确的——就我个人而言,我找到了一个了解例如的好方法。 state monad 将自己经历几次编写样板的繁琐程序,然后才想知道,“有没有办法干净地抽象出所有这些?”只有这样,您才能体会到解决方案的美妙之处! (YMMV)。
  • 另外,请注意,you 复制堆栈和 compiler 实际复制内存中的数据结构是有区别的。 因为值是不可变的,编译器可以让Stack xsStack (x:xs)在后台共享对xs的引用。

标签: haskell stack undo redo


【解决方案1】:

如何在不复制堆栈的情况下将堆栈创建为结构?

您提供的代码很好,不会重复太多数据。

假设您的当前堆栈为stack1 = a - b - c - d - e。现在你 pop stack1 使用代码:

pop (Stack (x:xs)) = (Just x, Stack xs)

您将返回一个新堆栈stack2 = b - c - d - e,它只是a 之后的整个结构,并且没有任何内容被复制。如果您保留 stack1,那么您将拥有两个看起来像这样的结构:

 stack1 -> a - b - c - d - e
               ^
               |
             stack2

请记住,您使用的单链表意味着a 不是stack2 的一部分。如果stack1 被垃圾回收,那么您最终会得到stack2 = b - c - d - e,正如您所期望的那样。

现在假设你 push z stack2 产生 stack3 = z - b - c - d - e。如果stack1stack2 仍然存在,那么堆看起来像:

     stack3 -> z
               |
 stack1 -> a - b - c - d - e
               ^
               |
             stack2

【讨论】:

    最近更新 更多