Как бессрочно закрепить состояние в одной и той же монаде State в Haskell?

Начав с Haskell и остановившись на State Monad ...

Итак, я пытаюсь разобраться с Монадой состояний в Haskell, и чтобы понять ее, я пишу код для генерации последовательностей PRBS. Для заинтересованных людей это описано в статье «Псевдослучайные последовательности и массивы», бесплатную копию которой можно получить по адресу doi: 10.1109 / PROC.1976.10411.

Суть сотрудничества очень проста. У вас есть сдвиговый регистр и генераторный полином. Генераторный полином сообщает вам, какие биты сдвигового регистра нужно взять и суммировать (по модулю 2), чтобы получить старший бит сдвигового регистра. На следующей итерации вы берете этот вычисленный MSB и прикрепляете его к MSB регистра сдвига после выполнения операции сдвига вправо.

Код для этого без монад действительно прост:

import Data.List

m = (4::Int)              -- This is the degree of the polynomial
p = tail $ [4, 1, 0]      -- This is a actual polynomial
s = 1 : replicate (m-1) 0 -- This is the initial state. Note that the head is the MSB

chSt poly s = msb poly s : init s -- changes the shift register 's'
    where
        msb poly s = let tot = sum $ map ( (reverse s) !! ) poly
                     in  tot `mod` 2

word p s = take n $ bits p s
    where
        bits p s = last s : bits p (chSt p s)
        n        = 2 ^ (length s) - 1

Результат выглядит следующим образом:

[ghci] word p s      --> [0,0,0,1,0,0,1,1,0,1,0,1,1,1,1]

чего я и хочу.

Теперь, конечно, поскольку регистр сдвига можно рассматривать как состояние, которое мы модифицируем, мы можем использовать для этой цели монаду состояния. Возможно, это слишком простая проблема, чтобы узнать о монаде состояний, но я просто думаю, что это может быть идеальный пример, который можно реализовать с помощью монады состояний. Итак, вот что я сделал:

getVal :: [Int] -> State [Int] Int
getVal poly = do
    curState <- get
    let lsb = last curState
    modify $ chSt poly
    return lsb

bitM :: State [Int] Int 
bitM = getVal p

Это просто добавление к предыдущему сегменту кода вместе с import Control.Monad.State в первом подобии программы. Когда я импортирую это в GHCI и проверяю вычисления состояний, я могу получить отдельные значения, как показано ниже:

[ghci] runState ( bitM  ) s                                   --> (0,[0,1,0,0])
[ghci] runState ( bitM >> bitM  ) s                           --> (0,[0,0,1,0])
[ghci] runState ( bitM >> bitM >> bitM ) s                    --> (0,[1,0,0,1])
[ghci] runState ( bitM >> bitM >> bitM >> bitM) s             --> (1,[1,1,0,0])
[ghci] runState ( bitM >> bitM >> bitM >> bitM >> bitM) s     --> (0,[0,1,1,0])
[ghci] 

Таким образом, как состояние обновляется правильно, так и возвращаемое значение также является правильным. Теперь я хочу создать функцию, подобную функции word в предыдущей реализации, которая будет применять оператор >> в bitMs n раз, чтобы мы могли создать массив word p s. Я совершенно не понимаю, как это сделать. Обратите внимание, что я не хочу добавлять такие функции, как:

f1 = bitM
f2 = bitM >> bitM
f3 = bitM >> bitM >> bitM 
...

Мне нужна одна функция, которой я передам n, и она будет выполнять bitM оценки последовательно n раз, автоматически передавая состояние внутри последовательных вычислений, собирая результирующие значения и создавая в результате массив. Как мне это сделать? Я не могу понять, как это сделать. Любая помощь будет оценена!


person ssm    schedule 26.02.2014    source источник


Ответы (1)


Если вы немного присмотритесь к нему, bitM >> bitM >> ... > bitM можно рассматривать как список действий, поэтому мы ищем Int -> m a -> [m a] или попроще Int -> a -> [a], который является просто сигнатурой replicate. У нас получится что-то типа

[State [Int] Int]

Теперь мы ищем [State [Int] Int] -> State [Int] [Int] или попроще: [m a] -> m [a], что оказывается sequence. Следовательно, вы ищете

sequence . replicate n $ bitM

который оказался replicateM, имеющим тип Int -> m a -> m [a].

person Zeta    schedule 26.02.2014
comment
Вау, это именно то, что я хочу! Спасибо! - person ssm; 26.02.2014
comment
@Yuuri: Спасибо. Кажется, мне всегда нужно напоминать о Control.Monad функциях, и я все время забываю мои собственные ответы ._. - person Zeta; 26.02.2014
comment
Чтобы дополнить этот ответ, вам может потребоваться использовать runState (fmap last $ replicateM bitM) s, поскольку вам нужен результат последнего действия, а не все результаты. - person tofcoder; 26.02.2014
comment
@Teetoo: Мне нужна одна функция, которой я передам n, и она будет выполнять оценки bitM последовательно n раз, автоматически передавая состояние внутри последовательных вычислений, собирая результирующие значения и создавая массив как результат. OP действительно интересует все результаты, поэтому fmap last на самом деле не требуется. - person Zeta; 26.02.2014