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