2012-04-19 5 views
1

Я пишу функцию в Haskell, которая принимает файл класса Java и записывает другой файл класса, который идентичен, но содержит некоторые изменения. Для этого я чувствую, что мне определенно нужна государственная монада, чтобы хотя бы удерживать [Word8], который содержит все байты файла класса. Однако, после всех моих исследований по государственным монадам в Хаскелле, мне все еще трудно понять, как это сделать. Может кто-то указать мне верное направление? Я хотел бы иметь возможность [Word8] (или вы знаете, любой тип данных), который доступен для всех функций и который я могу изменить из функций. Я понимаю, это связано, используя нечто вроде состояния < -get ... положить NewStateState Monads in Haskell

, но я действительно не знаю, с чего начать определение монады и этажерку.

Большое спасибо!

+0

http: // stackoverflow.com/questions/10230562/confusion-over-the-state-monad-code-on-learn-you-a-haskell –

+0

Какие преобразования вы собираетесь делать с байтами? Вы вставляете и удаляете куски? Вам нужен произвольный доступ? Прежде чем беспокоиться о том, какая монада вам нужна, сначала выясните, какая структура данных вам нужна. Это гораздо важнее. – rampion

+0

Кроме того, я предполагаю, что ответы на [этот вопрос] (http://stackoverflow.com/questions/10253474/inject-a-function-into-a-java-class-file-using-haskell) будут использования вам. – rampion

ответ

2

Что вы, вероятно, хотите, а не государственная Монада, является монадой ST и изменяемыми векторами.

Используйте IO monad для чтения байтов из файла класса.

bytes <- readFile myClassFile 

использование runST запустить расчет ST монады на данных байтах:

let result = runST $ transform bytes 

ЗБ Монада дает вам доступ к mutable vectors, которые много, как 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), и вы будете в состоянии сделать все, что вы хотите байтов.

Если вы не хотите беспокоиться о передаче его в качестве аргумента, подумайте об использовании преобразования монады 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 
+3

Взаимозаменяемые векторы получают O (1) чтение и запись, но вставка и удаление O (n), поэтому я не уверен, что это лучший выбор, если вы не уверены, что не изменяете количество байтов в файле classfile , –

+0

@DanBurton: Знаешь, я даже не думал, что Pha3drus может это сделать. Хороший звонок! – rampion

7

Я не уверен, что вы сделать хотят a State монада. В зависимости от того, какие модификации вы хотите сделать, вы : разрешено просто передавать данные, которые вы хотите изменить, для каждой функции, которая хочет ее изменить. State обычно используется в ситуациях, когда вы производите значение дополнительно, чтобы изменить состояние, то есть когда вы пишете много функций, которые выглядят как s -> (s,a).

Сначала попробуйте только подход с нормальной функцией. State не является магии, он просто позволяет некоторым типам кода писать быстрее, лаконично и правильно. Все, что вы можете сделать с ним, вы можете обойтись и без него, это просто немного утомительно.

+0

Спасибо, я закончил тем, что использовал только множество нормальных функций, которые менялись в виде эмулируемых состояний. В случае, если кому-то было любопытно, проект включал чтение в файле класса, разделение его на части и добавление кода к методам для печати имени метода при его вводе. Еще раз спасибо! – Pha3drus