Parsecにちょっと触ってみる(2)

の写経の続きです。

sepBy と sepBy1

「パーサによって区切られるパーサの列をパースします」って分かりにくい説明が書かれていますが、動かしてみます。

import Text.ParserCombinators.Parsec

tstSepBy :: String -> [String]
tstSepBy str = case parse strToNumList "" str of
              Left  err -> []
              Right n   -> n

-- "123,456, 789" --=> ["123","456","789"]
strToNumList :: Parser [String]
strToNumList = sepBy1 number separator

-- 数字:'0'-'9' が1個以上
number :: Parser String
number = many1 digit

-- 区切りの定義:スペースか ',' が1個以上
separator   :: Parser ()
separator   = skipMany1 (space <|> char ',')

指定された区切りで分割してリストにして返す、ってことのようです。

 *Main> tstSepBy "123,456,789"    --=> ["123","456","789"]

 *Main> tstSepBy "123,456     ,,,  , 789" --=> ["123","456","789"]

 -- リストになればこっちのもの。やりたい放題。
 *Main> foldr  (\x y->((read x)::Int)+y) 0 $ tstSepBy "123,456,789"
  --=> 1368
sepEndBy と sepEndBy1

区切り文字、終端文字の両方として認識します。

strToNumList :: Parser [String]
strToNumList = sepEndBy1 number separator
 *Main>  tstSepBy "123,456,789"        --=> ["123","456","789"]
 *Main>  tstSepBy "123,456,789,"       --=> ["123","456","789"]
 *Main>  tstSepBy "123,456,789,dddddddddddddddddddddd"
       --=> ["123","456","789"]
 *Main>  tstSepBy "123,456,789, Hello"
       --=> ["123","456","789"]
satisfy

satisfy は 関数 f を引数に取り、f を適用して True となる文字を返す。

satisfy :: (Char -> Bool) -> CharParser st Char

-- isDigit が True を返す文字をパースする。
digit    = satisfy isDigit
-- (\c -> c `elem` cs) が True を返す文字(csの中にに含まれている)
-- をパースする。
oneOf cs = satisfy (\c -> c `elem` cs)