Как применить вычисление с сохранением состояния к списку?

Давайте представим фиктивное подмножество Brainf*ck:

+ увеличивает счетчик

- уменьшает счетчик

Простая программа:

program = "++++--" -- should evaluate to 2

И функция оценки состояния:

eval :: Char -> State Int Char
eval '+' = do x <- get
              put (x + 1)
              return 'I'
eval '-' = do x <- get
              put (x - 1)
              return 'D'

Как бы вы оценили программу? (Похоже на fold для меня, но я не могу понять это, и мне кажется, что это не способ сделать это правильно...)


person Raphaël Mor    schedule 02.10.2015    source источник


Ответы (3)


Вы можете использовать traverse_ из Data.Foldable:

import Data.Foldable (traverse_)
execState (traverse_ eval "++++--") 0
person Lee    schedule 02.10.2015
comment
работает отлично ! Благодарность ! Еще нужно обдумать многие концепции. - person Raphaël Mor; 09.10.2015

Функция, которую вы ищете, это sequence, которая имеет сигнатуру sequence :: Monad m => [m a] -> m [a] и является очень распространенным шаблоном при работе с такими монадами, как State.

Для вашего кода вы ожидаете, что оценщик будет выглядеть так:

evalBF :: String -> State Int String
evalBF = sequence . map eval

Который вы затем полностью оцените чем-то вроде:

main :: IO ()
main = do
       src <- getLine
       print $ runState (evalBF src) 0
person C. Quilley    schedule 02.10.2015
comment
sequence . map f такой же, как traverse f, но последний имеет более общий тип. - person dfeuer; 02.10.2015

Уродливое решение, но с использованием сгиба, который, как вы правильно подозревали, применим.

import Control.Monad.Trans.State

program = "++++--"

eval :: Char -> State Int Char
eval '+' = do
    x <- get
    put (x + 1)
    return 'I'
eval '-' = do
    x <- get
    put (x - 1)
    return 'D'

evalList :: [Char] -> State Int Char
evalList = foldl (\s c -> (s >> eval c)) (return ' ')

main = putStrLn $ show $ runState (evalList program) 0
person jakubdaniel    schedule 02.10.2015