Text.Parsecのパーサの型
前回 Text.Parsec の記事を書いてから、ひと月以上あいてしまった。なんか途中で書くモチベーションが下がったりもしたんだけど、今日は時間がとれたので書く。前回良くわからなかった、パーサの型についてだ。
ここ↓が参考になった。
cf. Parsec3 におけるパーサーの型 - k16’s note
このページによると、
- パーサの厳密な型シグネチャは
ParsecT s u m a- s は抽象化された入力の型。この抽象化された入力をストリームという
- u はパース時に好きな状態を格納しておく容器の型
- m はモナド変換子にとって基盤となるモナド
- a は出力の型
モナド変換子というのは、ここでは ParsecT のことで、正直なんだかよくわからない。
前回書いたコードにある wave パーサの場合、入力は文字列(String)、出力は少数点数のリスト([Double])で、m がモナドである必要があるから、次のようになる。
wave :: Monad m => ParsecT String u m [Double]
コード全体を載せると:
module Main where
import System.Environment ( getArgs )
import Text.Parsec
--------------------------------------------------------------------------------
main :: IO ()
main = do
args <- getArgs
cs <- readFile (head args)
let wv = parseWave cs
either (\ e -> print e) (\ w -> putStr $ unlines $ map show w) wv
--------------------------------------------------------------------------------
-- Parsers
wave :: Monad m => ParsecT String u m [Double]
wave = do
ls <- many1
waveLine
eof
return $ concat ls
waveLine :: Monad m => ParsecT String u m [Double]
waveLine = do
ws <- many1
waveData
newline
return ws
waveData :: Monad m => ParsecT String u m Double
waveData = do
d <- count 8 dataChar
return $ read d
dataChar :: Monad m => ParsecT String u m Char
dataChar = digit <|> oneOf "-. "
--------------------------------------------------------------------------------
parseWave :: String -> Either ParseError [Double]
parseWave input = parse wave "(unknown)" input
--------------------------------------------------------------------------------
実行例:
takatoh@apostrophe $ runhaskell parsewave0.hs data0.dat > result0.txt
takatoh@apostrophe $ head result0.txt
-5.0e-2
-5.0e-2
-5.0e-2
-5.0e-2
-6.0e-2
-6.0e-2
-6.0e-2
-6.0e-2
-6.0e-2
-7.0e-2
話はさらに続く。
Text.Parsec.String には、次のような定義がある。
type Parsec s u = ParsecT s u Identity
type Parser = Parsec String ()
上は ParsecT のモナドに Identity を使ったもの、下はその Parsec の入力に String、容器に () を使ったものだ。これを使うと、前述の wave パーサの型は次のように簡潔に書ける。
wave :: Parser [Double]
コード全体を載せよう。
module Main where
import System.Environment ( getArgs )
import Text.Parsec
import Text.Parsec.String
--------------------------------------------------------------------------------
main :: IO ()
main = do
args <- getArgs
cs <- readFile (head args)
let wv = parseWave cs
either (\ e -> print e) (\ w -> putStr $ unlines $ map show w) wv
--------------------------------------------------------------------------------
-- Parsers
wave :: Parser [Double]
wave = do
ls <- many1
waveLine
eof
return $ concat ls
waveLine :: Parser [Double]
waveLine = do
ws <- many1
waveData
newline
return ws
waveData :: Parser Double
waveData = do
d <- count 8 dataChar
return $ read d
dataChar :: Parser Char
dataChar = digit <|> oneOf "-. "
--------------------------------------------------------------------------------
parseWave :: String -> Either ParseError [Double]
parseWave input = parse wave "(unknown)" input
--------------------------------------------------------------------------------
実行例:
takatoh@apostrophe $ runhaskell parsewave0a.hs data0.dat > result0a.txt
takatoh@apostrophe $ head result0a.txt
-5.0e-2
-5.0e-2
-5.0e-2
-5.0e-2
-6.0e-2
-6.0e-2
-6.0e-2
-6.0e-2
-6.0e-2
-7.0e-2
ふぅ、今日はここまで。
