おそらく、State モナドの代わりに、ST モナドと変更可能なベクトルが必要です。
IO モナドを使用して、クラス ファイルからバイトを読み込みます。
bytes <- readFile myClassFile
指定されたバイトでモナド計算runST
を実行するために使用します。ST
let result = runST $ transform bytes
ST モナドは、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
そのため、すべての関数 (ST アクションを返す必要があります) に を渡すだけでmvec
、バイトに対してやりたいことが何でもできるようになります。
わざわざ引数として渡したくない場合は、ReaderT
モナド変換を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
.
たとえば、 のすべてのインスタンスを に置き換える0xDEADBEEF
だけ0xCAFEBABE
でよい場合は、リストを使用するだけで済み、ST モナドは必要ありません。
let newBytes = intsToBytes . map (\i -> if i == 0xDEADBEEF then 0xCAFEBABE else i) $ bytesToInts bytes