2013年10月19日土曜日

HaskellのParsecライブラリのまとめ

インポート

Parsecを使用するには、モジュールParsecをインポートする。
import Text.ParserCombinators.Parsec

パーサ一覧

関数/パーサ runParser
引数 パーサ, 状態の初期値, ファイルパス, パース対象
パーサの戻り値 Either
説明 パーサを実行し、Eitherを返す。パースが成功した場合はRight、失敗した場合はLeftを返す。ファイルパスは、パースが失敗したときのメッセージに使わる。
putStrLn $ show $ runParser (string "test") () "error" "test"
-- > Right "test"
putStrLn $ show $ runParser (string "test") () "error" "abc"
-- > Left "error" (line 1, column 1):
-- > unexpected "a"
-- > expecting "test"
    
関数/パーサ parseTest
引数 パーサ, パーサ対象文字列
パーサの戻り値 IO (), 結果をコンソールに出力
説明 パーサを実行し、結果をコンソールに出力する
parseTest (string "this is test") "this is test"
-- > "this is test"
    
関数/パーサ string
引数 String
パーサの戻り値 String
説明 文字列をパースし、マッチした文字列を返す
parseTest (string "Hello") "Hello World" -- > "Hello"
parseTest (string "Hello") "hello world" -- > Error
      
関数/パーサ char
引数 Char
パーサの戻り値 Char
説明 文字をパースし、マッチした文字を返す
parseTest (char 'a') "a" -- > "a"
parseTest (char 'a') "b" -- > Error
      
関数/パーサ many
引数 パーサ
パーサの戻り値 パーサの戻り値の配列
説明 0回以上与えられたパーサを適用する。
まっちしなくてもパースは成功する。
parseTest (many $ char 'a') "aaa" -- > "aaa"
parseTest (many $ char 'a') "abc" -- > "a"
parseTest (many $ char 'a') "" -- > ""
parseTest (many $ char 'a') "b" -- > ""
      
関数/パーサ many1
引数 パーサ
パーサの戻り値 パーサの戻り値の配列
説明 1回以上与えられたパーサを適用する。
parseTest (many1 $ char 'a') "aaa" -- > "aaa"
parseTest (many1 $ char 'a') "abc" -- > "a"
parseTest (many1 $ char 'a') "" -- > Error
parseTest (many1 $ char 'a') "b" -- > Error
      
関数/パーサ upper
引数 なし
パーサの戻り値 Char
説明 大文字とマッチする
parseTest (many upper) "AAA" -- > "AAA"
parseTest (many upper) "Abc" -- > "A"
parseTest (many upper) "abc" -- > ""
      
関数/パーサ lower
引数 なし
パーサの戻り値 Char
説明 小文字とマッチする
parseTest (many lower) "abc" -- > "abc"
parseTest (many lower) "aBc" -- > "a"
parseTest (many lower) "A" -- > ""
      
関数/パーサ alphaNum
引数 なし
パーサの戻り値 Char
説明 数字とアルファベットにマッチする
parseTest (many alphaNum) "abc" -- > "abc"
parseTest (many alphaNum) "123" -- > "123"
parseTest (many alphaNum) "1b3" -- > "1b3"
parseTest (many alphaNum) "1_3" -- > "1"
parseTest (many alphaNum) "%" -- > ""
      
関数/パーサ letter
引数 なし
パーサの戻り値 Char
説明 アルファベットにマッチする
parseTest (many letter) "abc" -- > "abc"
parseTest (many letter) "123" -- > ""
parseTest (many letter) "%" -- > ""
      
関数/パーサ digit
引数 なし
パーサの戻り値 Char
説明 数字にマッチする
parseTest (many digit) "abc" -- > ""
parseTest (many digit) "123" -- > "123"
parseTest (many digit) "%" -- > ""
      
関数/パーサ anyChar
引数 なし
パーサの戻り値 Char
説明 どんな文字にもマッチする
parseTest (many anyChar) "a|~" -- > "a|~"
parseTest (many anyChar) "1/+" -- > "1/+"
      
関数/パーサ oneOf
引数 [Char]
パーサの戻り値 Char
説明 指定した文字のどれかにマッチする
parseTest (many $ oneOf "xyz") "abc" -- > ""
parseTest (many $ oneOf "xyz") "xxx" -- > "xxx"
parseTest (many $ oneOf "xyz") "xy_" -- > "xy"
      
関数/パーサ noneOf
引数 [Char]
パーサの戻り値 Char
説明 指定した文字以外の文字にマッチする
parseTest (many $ noneOf "xyz") "abc" -- > "abc"
parseTest (many $ noneOf "xyz") "xxx" -- > ""
parseTest (many $ noneOf "xyz") "abz" -- > "ab"
      
関数/パーサ <|>
引数 パーサ <|> パーサ
パーサの戻り値 パーサ
説明 2つのパーサのうち成功した方を返す。
10 
11 
12 
13 
14 
15 
16 
parseTest (string "abc" <|> string "xyz") "abc"
-- > "abc"
parseTest (string "abc" <|> string "xyz") "xyz"
-- > "xyz"
parseTest (string "abc" <|> string "xyz") "123"
-- > Error
-- > "123"は"abc"と"xyz"にはマッチしないので失敗
parseTest ((string "abc") <|> return "failed") "xyz"
-- > "failed"
-- > "abc"は"xyz"とはマッチしないので、
-- > 'return "failed"'が呼ばれた。
parseTest ((string "abc") <|> return "failed") "abx"
-- > Error
-- > "abx"が"abc"の"ab"までマッチしたので全体が失敗。
-- > 途中までマッチして失敗すると全体が失敗する。
      
関数/パーサ try
引数 パーサ
パーサの戻り値 パーサ
説明 指定したパーサが途中失敗しても、入力列を消費せずにパースを続ける
parseTest ((try $ string "abc") <|> return "failed") "abc"
-- > "abc"
parseTest ((try $ string "abc") <|> return "failed") "abx"
-- > "failed"
-- > 途中までマッチして失敗しても、次のパーサが呼ばれた。
      
関数/パーサ choice
引数 パーサの配列
パーサの戻り値 マッチしたパーサの戻り値
説明 指定したパーサのどれかにマッチする
10 
11 
12 
13 
parseTest (choice [string "abc", string "xyz"]) "abc"
-- > "abc"
parseTest (choice [string "abc", string "xyz"]) "xyz"
-- > "xyz"
parseTest (choice [string "abc", string "abd"]) "abd"
-- > Error
-- > "abd"が"abc"の"ab"までマッチしているので
-- > 失敗する。Parsecは、途中まで成功して失敗すると、
-- > 全体が終了する。 tryを使うと良い。
parseTest (choice [try $ string "abc", string "abd"]) "abd"
-- > "abd"
-- > tryを使うことで途中で失敗しても、次のパーサを適用する。      
      
関数/パーサ option
引数 失敗したときの戻り値,
パーサ
パーサの戻り値 パーサの戻り値
説明 マッチしたパーサか指定した戻り値を返す
parseTest (option "abc" $ string "xyz") "xyz" -- > "xyz"
parseTest (option "abc" $ string "xyz") "123" -- > "abc"
      
関数/パーサ sepBy
引数 トークンを表すパーサ, 区切りのパーサ
パーサの戻り値 トークンの配列
説明 同じトークンがある区切りを挟んで連続している列をパースする
parseTest (alphaNum `sepBy` (char ',')) "x,y,z" -- > "xyz"
parseTest (alphaNum `sepBy` (char ',')) "x,y," -- > Error
parseTest (alphaNum `sepBy` (char ',')) ",x,y," -- > ""
      
関数/パーサ endBy
引数 トークンを表すパーサ, 区切りのパーサ
パーサの戻り値 トークンの配列
説明 ある区切りで終わるトークンの連続している列をパースする
parseTest (alphaNum `endBy` (char ';')) "x;y;z" -- > Error
parseTest (alphaNum `endBy` (char ';')) "x;y;z;" -- > "xyz"
parseTest (alphaNum `endBy` (char ';')) ";x;y;" -- > ""
      
関数/パーサ eof
引数 なし
パーサの戻り値 ()
説明 入力列の終わりとマッチする
parseTest (eof >> return "eof") "" -- > "eof"
      
関数/パーサ notFlowedBy
引数 パーサ
パーサの戻り値 ()
説明 指定したパーサの失敗を成功とする。入力列を消費しない。
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
parseTest (do { x <- string "abc"
              ; notFollowedBy letter
              ; return x
              }) "abc"
-- > "abc"
      
parseTest (do { x <- string "abc"
              ; notFollowedBy letter
              ; return x
              }) "abca"
-- > Error
      
parseTest (do { x <- string "abc"
              ; notFollowedBy letter
              ; return x
              }) "abc;"
-- > "abc"
      
parseTest (do { x <- string "abc"
              ; notFollowedBy letter
              ; y <- char ';'
              ; return $ x ++ [y]
              }) "abc;"
-- > "abc;"
-- > notFollowedByで';'が消費されていないことがわかる      
      
関数/パーサ lookAead
引数 パーサ
パーサの戻り値 指定したパーサの戻り値
説明 入力列を消費せずに、指定したパーサの適用する。
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
parseTest (do { x <- string "abc"
              ; y <- lookAhead $ string "123"
              ; return $ x ++ y
              }) "abc123"
-- > "abc123"

parseTest (do { x <- string "abc"
              ; y <- lookAhead $ string "123"
              ; return $ x ++ y
              }) "abc;"
-- > Error
  
parseTest (do { x <- string "abc"
              ; lookAhead $ char ';'
              ; y <- char ';'
              ; return $ x ++ [y]
              }) "abc;"
-- > "abc;"
-- > lookAheadで';'が消費されていないことがわかる
      
関数/パーサ getState
引数 なし
パーサの戻り値 状態
説明 現在の状態を取り出す
10 
runParser (getState >>= \-> return x) "xyz" "error" "abc"
-- > Right "xyz"
  
runParser (do { x <- many anyChar
              ; setState x
              ; y <- getState
              ; return y
              }) "" "error" "abc"
-- > Right "abc"
      
関数/パーサ setState
引数 状態
パーサの戻り値 ()
説明 状態を設定する
10 
runParser (getState >>= \-> return x) "xyz" "error" "abc"
-- > Right "xyz"
  
runParser (do { x <- many anyChar
              ; setState x
              ; y <- getState
              ; return y
              }) "" "error" "abc"
-- > Right "abc"
      
関数/パーサ getPosition
引数 なし
パーサの戻り値 SourcePos
説明 現在のパース位置を取得する
10 
parseTest (do { x <- string "abc\nx"
              ; p <- getPosition
              ; return $ f p
              }) "abc\nxyz"
  where
    lineNum p = show $ sourceLine p
    columnNum p = show $ sourceColumn p
    f p = "line: " ++ (lineNum p) ++ " column: " ++ (columnNum p)
-- > "line: 2 column: 2"
      

2013年10月10日木曜日

Scalaでモナドを作成する

ScalaのEitherモナドが正直言って使い勝ってが悪いので、自分なりに使い勝手がよいモナドを作ってみました。

Eitherモナド

Eitherモナドのどういうところが使い勝手が悪いかと言うと、forとかでEitherモナドをつなげて使う時です。
例えば、次のようにEitherを4つ繋げるときです。

    for {
      a <- Right(1).right
      b <- Right(2).right
      c <- Left("error").right
      d <- Right(4).right
    } yield (a, b, c, d)

この結果は、Left("error")を返します。
ここで面倒くさいのが、Rightで繋げていきたいときは、.rightLeftで繋げていきたいときは、.leftメソッド呼び出さないといけないことです。僕がEitherモナドを使うときは、Rightに成功したときの値でLeftに失敗した時のメッセージなどを入れることがほとんどなので、.leftを使うことは、ほぼ有りません。.rightを省いて使いたいのです。
僕にとっては、次のように使えるモナドがあれば都合がいいです。

    for {
      a <- Good(1)
      b <- Good(2)
      c <- Bad("error")
      d <- Good(4)
    } yield (a, b, c, d)

こんなふうに、Goodのときは、どんどん繋がっていき、途中でBadがあったら、そこで止まるというようなモナドがあったら便利です。
そこで自分で都合の良いモナドを作ってみることにしました。

Resultモナド

自作モナドをResultモナドと呼ぶことにします。何かの「結果」を返す時に使うことが多いからです。

仕様概要

Resultサブクラスには、成功したときの結果を入れるGood、失敗した時の結果を入れる使うBadが必要でしょう。さらに、失敗したけど結果がないときに使えるEmptyもあったら便利です。

forで使うには、foreachfiltermapflatMapを定義する必要があります。

他のモナドにもあるような、getgetOrElseorElseexistsforallなどのメソッドも実装します。

型パラメータ

Resultは、2つの型パラーメータを取ります。それぞれ、GoodBadの保持する型です。Emptyが保持する型は、全ての型のサブクラスとして返す必要があるので、Nothingにします。このため、Resultの2つの型パラメータを共変にしなければなりません。

  abstract class Result[+A, +B]
  case class Good[+A, +B]( a: A ) extends Result[A, B]
  case class Bad[+A, +B]( b: B ) extends Result[A, B]
  case object Empty extends Result[Nothing, Nothing]

flatMapメソッド

flatMapは、Good[A]の保持するオブジェクトを受け取って、新しいResultモナドを返す関数fを引数に取ります。関数fの戻り値Resultの型パラメータは、基本的にどんな型でも返せるようにします。ただし、flatMapは、Bad[B]を返す可能性があるので、Badの型は、型Bに関連する型にしなければなりません。型Bと関数fが返すBadの型を共通にするために、戻り値のBadの型は、型Bの親型のBB >: Bにします。

    def flatMap[C, BB >: B]( f: A => Result[C, BB] ): Result[C, BB] = this match {
      case Good( a ) => f( a )
      case Bad( b ) => Bad( b )
      case _ => Empty
    }

ソースコード

Resultモナドのソースコード次のようになりました。
Haskellのモナドを繋げる>>=>>もついでに追加してみました。

10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53 
54 
55 
56 
57 
58 
59 
60 
61 
62 
63 
64 
65 
66 
67 
68 
69 
70 
71 
72 
73 
74 
75 
76 
77 
78 
79 
80 
81 
82 
83 
84 
85 
86 
87 
  abstract class Result[+A, +B] {

    def getGood: A = this match {
      case Good( a ) => a
      case _ => throw new java.util.NoSuchElementException("this instance is not Good")
    }

    def getBad: B = this match {
      case Bad( b ) => b
      case _ => throw new java.util.NoSuchElementException("this instance is not Bad")
    }

    def get: A = getGood

    def getOrElse[AA >: A]( a: => AA ): AA = this match {
      case Good( a ) => a
      case _ => a
    }

    def orElse[AA >: A, BB >: B]( r: => Result[AA, BB] ): Result[AA, BB] = this match {
      case Good( a ) => Good( a )
      case _ => r
    }

    def exists( f: A => Boolean ): Boolean = this match {
      case Good( a ) => f( a )
      case _ => false
    }

    def forall( f: A => Boolean ): Boolean = this match {
      case Good( a ) => f( a )
      case _ => false
    }

    def foreach( f: A => Unit ): Unit = this match {
      case Good( a ) => f( a )
      case _ => ()
    }

    def filter( f: A => Boolean ): Result[A, B] = this match {
      case Good( a ) => if( f( a ) ) Good( a ) else Empty
      case Bad( b ) => Bad( b )
      case _ => Empty
    }

    def flatMap[C, BB >: B]( f: A => Result[C, BB] ): Result[C, BB] = this match {
      case Good( a ) => f( a )
      case Bad( b ) => Bad( b )
      case _ => Empty
    }

    def map[C]( f: A => C ): Result[C, B] = this match {
      case Good( a ) => Good( f(a) )
      case Bad( b ) => Bad( b )
      case _ => Empty
    }

    def >>= [C, BB >: B] ( f: A => Result[C, BB] ): Result[C, BB] = flatMap( f )

    def >> [C, BB >: B] ( f: => Result[C, BB] ): Result[C, BB] = flatMap( _ => f )

    def >>-[C]( f: A => C ): Result[C, B] = map( f )

    def toOption: Option[A] = this match {
      case Good( a ) => Some( a )
      case _ => None
    }

    def toList: List[A] = this match {
      case Good( a ) => List( a )
      case _ => Nil
    }

    def toBoolean: Boolean = this match {
      case Good( _ ) => true
      case _ => false
    }

  }


  case class Good[+A, +B]( a: A ) extends Result[A, B]

  case class Bad[+A, +B]( b: B ) extends Result[A, B]

  case object Empty extends Result[Nothing, Nothing]