1

我正在用 Haskell 编写一个函数,该函数接受一个 Java 类文件,并编写另一个相同但包含一些修改的类文件。为此,我觉得我绝对需要一个状态单子来至少保存包含类文件所有字节的 [Word8]。然而,在我对 Haskell 中的 State Monads 进行了所有研究之后,我仍然无法弄清楚如何做到这一点。谁能指出我正确的方向?我希望能够有一个 [Word8](或者你知道,任何数据类型),它在所有函数的范围内,并且我可以从函数中修改。我知道这涉及使用类似 state<-get ... put newstate

但我真的不知道从哪里开始定义单子和诸如此类的东西。

提前非常感谢!

4

2 回答 2

7

我不确定你是否想要一个State单子。根据您要进行的修改类型,您可以只将要修改的数据传递给要修改它的每个函数。State通常用于除了修改状态之外还生成值的情况,即当您编写很多看起来像s -> (s,a).

首先尝试一种正常功能的方法。State不是魔术,它只是让某些类型的代码更容易快速、简洁和正确地编写。你可以用它做的一切你也可以不用它,只是有点乏味。

于 2012-04-20T00:10:25.077 回答
2

您可能想要的是 ST monad 和可变向量,而不是 State Monad。

使用 IO monad 从类文件中读取字节。

bytes <- readFile myClassFile

用于对给定字节runST运行STmonad 计算:

let result = runST $ transform bytes

ST monad 使您可以访问可变向量,这很像 C 或 Java 数组。它们由整数索引,并具有 O(1) 查找和修改。

transform :: [Char] -> ST s [Char]
transform bytes = do
   mvec <- thaw $ fromList bytes
   -- you can read a value at an index
   val <- read mvec 0
   -- and set a value at an index
   write mvec 0 (val `xor` 0xff)
   -- ...
   -- turn it back into a list of bytes
   vec <- freeze mvec
   return $ toList vec

因此,只需将 传递mvec给您的所有函数(必须返回 ST 操作),您就可以对字节执行任何操作。

如果您不想费心将其作为参数传递,请考虑使用ReaderTmonad 转换以使mvec所有代码都隐式可用。

transform bytes = do
   -- ...
   runReaderT other mvec
   --- ... 

other :: ReaderT (MVector s Char) (ST s) String 
other = do
   -- ...
   -- grab the mvec when you need it
   mvec <- ask
   val <- lift $ read mvec 77
   lift $ write mvec 77 (val * 363 - 28)
   -- ...
   return "Hi!"

当然,这一切都假设您需要随机访问字节。如果你不......那么你可能不需要一个MVector.

例如,如果您需要做的只是替换0xDEADBEEFwith的每个实例0xCAFEBABE,您可以只使用列表,不需要 ST monad:

 let newBytes = intsToBytes . map (\i -> if i == 0xDEADBEEF then 0xCAFEBABE else i) $ bytesToInts bytes
于 2012-04-19T22:58:37.140 回答