我逐行解析文件,并希望根据每行包含的内容执行某些操作。其中一些操作会更改对象的状态,而某些操作则需要打印当前状态。
--code
data Adder = Adder {
cur :: Int,
} deriving (Show)
initAdder :: Adder
initAdder = Adder 0
main = do
[fname] <- getArgs
input <- readFile fname
mapM_ process (lines input)
-- file to read-------------------------
+1
-4
print
*45
+6
-5
print
-----------------------------------------
问题是我应该如何编写函数进程才能传递Adder类型的相同对象,有时执行IO操作(打印)或只是改变状态并继续?感谢。
答案 0 :(得分:2)
要理解的一点是IO
并不意味着国家。为此,State
包中有mtl
monad。如果您需要同时管理状态和IO
,则表示您希望将两种不同类型的效果组合在一起,这会提示StateT
monad变换器。你可以使用类似
import Text.Read (readMaybe)
import Control.Monad.State
-- The same state data type
data Adder = Adder { cur :: Int } deriving (Eq, Show)
-- Some helper functions for doing math on Adders
add :: Int -> Adder -> Adder
add x (Adder cur) = Adder (cur + x)
-- Note that sub x y === y - x
sub :: Int -> Adder -> Adder
sub x (Adder cur) = Adder (cur - x)
mul :: Int -> Adder -> Adder
mul x (Adder cur) = Adder (cur * x)
-- Just an alias to reduce typing and errors
type App a = StateT Adder IO a
process :: String -> App ()
-- If the line starts with an arithmetic operator, use the appropriate function to modify the current state
-- Here, I'm using readMaybe to safely convert the value to an Int, doing nothing if the value can't be parsed
process ('+':val) = maybe (return ()) (\x -> modify (add x)) $ readMaybe val
process ('-':val) = maybe (return ()) (\x -> modify (sub x)) $ readMaybe val
process ('*':val) = maybe (return ()) (\x -> modify (mul x)) $ readMaybe val
-- If the line is "print", then just print the current state
process "print" = do
-- Get the current state
Adder cur <- get
-- Since we're using the StateT transformer, have to use liftIO to perform IO actions
liftIO $ print cur
-- A simple app that executes a sequence of statements with state
runApp :: [String] -> IO Adder
runApp fileLines = execStateT (mapM_ process fileLines) (Adder 0)
main :: IO ()
main = do
(fname:_) <- getArgs
input <- readFile fname
runApp $ lines input
当然,如果您仅使用Int
作为州而不是Adder
,则此代码可能会短得多。您不需要辅助函数,process
可以写为
process :: String -> App ()
process ('+':val) = maybe (return ()) (\x -> modify (+x)) $ readMaybe val
process ('-':val) = maybe (return ()) (\x -> modify (subtract x)) $ readMaybe val
process ('*':val) = maybe (return ()) (\x -> modify (*x)) $ readMaybe val
process "print" = get >>= liftIO . print -- Shortened this to a one-liner too
答案 1 :(得分:0)
我在不使用State monad和变形金刚的情况下写了一个简化到这个程序的地面版本,因为我还不熟悉它们。也许它会对像我这样的Haskell新手有用。
main = do
[fname] <- getArgs
input <- readFile fname
process 0 $ lines input
process :: Int -> [String] -> IO()
process _ [] = return ()
process n (x:xs)
| head x == '+' || head x == '-' = do
process n' xs
| head x == 'p' = do
print n
process n xs
| otherwise = return ()
where
n'
| head x == '+' = n + (read (tail x) :: Int)
| otherwise = n - (read (tail x) :: Int)