Хаскель Монад Стэйт Пример - PullRequest
0 голосов
/ 09 февраля 2019

Я экспериментирую с Haskell Control.Monad.State, пытаясь перебрать список строк или целых чисел, подсчитывая их и заменяя строковые записи целым числом 0.Мне удалось сделать подсчет части, но не удалось создать замененный список.Вот мой код, который правильно выводит [3,6] на экран.Как я могу заставить его создать нужный список [6,0,3,8,0,2,9,1,0]?

module Main( main ) where

import Control.Monad.State

l = [
    Right 6,
    Left "AAA",
    Right 3,
    Right 8,
    Left "CCC",
    Right 2,
    Right 9,
    Right 1,
    Left "D"]

scanList :: [ Either String Int ] -> State (Int,Int) [ Int ]
scanList [    ] = do
    (ns,ni) <- get
    return (ns:[ni])
scanList (x:xs) = do
    (ns,ni) <- get
    case x of
        Left  _ -> put (ns+1,ni)
        Right _ -> put (ns,ni+1)
    case x of
        Left  _ -> scanList xs -- [0] ++ scanList xs not working ...
        Right i -> scanList xs -- [i] ++ scanList xs not working ...

startState = (0,0)

main = do
    print $ evalState (scanList l) startState

1 Ответ

0 голосов
/ 09 февраля 2019

[0] ++ scanList xs не работает, потому что scanList xs - это не список, а State (Int,Int) [Int].Чтобы это исправить, вам нужно будет использовать fmap / <$>.

. Вам также нужно будет изменить базовый случай, чтобы значение состояния не возвращалось.

scanList :: [Either String Int] -> State (Int, Int) [Int]
scanList []     = return []
scanList (x:xs) = do
    (ns,ni) <- get
    case x of
        Left  _ -> put (ns+1, ni)
        Right _ -> put (ns, ni+1)
    case x of
        Left  _ -> (0 :) <$> scanList xs
        Right i -> (i :) <$> scanList xs

Однако, чтобы еще больше упростить код, было бы хорошо использовать mapM / traverse и state для удаления большей части шаблонной части рекурсии и синтаксиса get / put.

scanList :: [Either String Int] -> State (Int, Int) [Int]
scanList = mapM $ \x -> state $ \(ns, ni) -> case x of
    Left  _ -> (0, (ns+1, ni))
    Right i -> (i, (ns, ni+1))
...