人は失敗からしか学ばん

数日前、テニスの草トーに出て負けた。

とまぁ、いつも負けてるわけだけど笑、その日は3試合して2勝1敗で。

2勝も、特に1つはギリギリ勝った感じで、反省点が多々あるだろうに、どうしても思い出して反省するのは、その1敗の試合ばっかり。。。

内容が良い負け試合よりも、内容の悪い勝ち試合を反省する、という風になることもあるのかな?まぁ上手い人はそういうこともあろう、昔の記憶だと強豪校は、勝ってもスパルタコーチに怒られてたなぁ…(こっちは負けてもヘラヘラしてた)

結局、自分の中での「失敗」によるっつう話か。。。

・・・とか、ここまで考えてて、 「失敗」すると、反省するよなぁ、失敗するの嫌だから変えなきゃって思うよなぁ。

人によって「失敗」って様々だろう、その人にとっての「失敗」ってなんなんだろう?って考えるのって意外と自分の中では新しい視点だなと思った。。 人を変えたい、そう思った時にそれが役立つなんてことはないかね。(意地悪!)

対人関係の基本姿勢は、人は変えられず、自分が変わるしかなく、けど自分もまぁなかなか変えられない、時が経つのを待つ、let it beなんだけど。笑

ちなみに、テニスの反省は

  • 前に出るんだったら、ボレーするぞ!みたいに意識を変える。球速い相手だと、気合い入ってないとボレーのタイミング合わせられない
  • リターンは無理しない。速いサーブにブロックリターン、とか心がけてたけど攻めのブロックリターンしか意識できてなかった😅

プログラミングHaskell 8.10 練習問題

1.

def nat: Parser[Int] = for {
    xs <- many1(digit)
  } yield xs.mkString.toInt

  def neg: Parser[Int] = for {
    _ <- symbol("-")
    n <- nat
  } yield -n

  def int: Parser[Int] = neg +++ nat

2.

def comment: Parser[Unit] = for {
    _ <- symbol("--")
    _ <- many(sat(_ != '\n'))
    _ <- char('\n')
  } yield ()

3, 4は構文木を書きづらいから省略

5.

うーむ、たとえば「5」って数字を解析するとき、 - expr '+' expr | termの解析で、exprで解析して、natまでたどり着く => '+'がない => termの解析 ..... - term('+' expr | ε)の解析だと、termで解析して、natまでたどり着く => '+'がない => ε(おわり) って感じで、二度目の解析がなくなる、って感じなのかなあ・・・・

6-1.

注釈に書いてあるけど、右結合なのでこのままだと間違いになっちゃう!

// 1 - 2 - 3 - 4 = 1 - 2 - (3 - 4) = 1 - (2 - (-1)) = 1 - 3 = -2
  def exprBad: Parser[Int] = for {
    t <- term
    e <- (for {
      op <- symbol("+") +++ symbol("-")
      ee <- exprBad
    } yield op match {
      case "+" => t + ee
      case "-" => t - ee
    }) +++ succeed(t)

  } yield e

なので、いったんListにして、foldLeftを使って左結合にする。

  // 1 - 2 - 3 - 4 => 1 List(2, 3, 4) => (1 - 2) List(3, 4) => ((-1) - 3) List(4) => (-4) - 4 = -8 
  def expr: Parser[Int] = for {
    t <- term
    e <- (for {
      _ <- symbol("+")
      ee <- expr
    } yield t + ee) +++ many(for {
      _ <- symbol("-")
      ee <- term
    } yield ee).map(_.foldLeft(t)(_ - _)) +++ succeed(t)
  } yield e

6-2

こちらも同様にfoldLeftで左結合。

def term: Parser[Int] = for {
    f <- factor
    e <- (for {
      _ <- symbol("*")
      ee <- term
    } yield f * ee) +++ many(for {
      _ <- symbol("/")
      ee <- factor
    } yield ee).map(_.foldLeft(f)(_ / _)) +++ succeed(f)
  } yield e

7

BNFはこうなるはず。

/**
    * expr ::= term('+' expr | '-' expr | ε)
    * term ::= pow('*' term  | '/' term | ε)
    * pow ::= factor('^' pow | ε)
    * factor ::= '(' expr ')' | nat
    * nat ::= '0' | '1' | '2' | ...
    */

powはこうかける。ちなみに、termにも修正を入れる必要あり。

def pow: Parser[Int] = for {
   f <- factor
    e <- (for {
      _ <- symbol("^")
      ee <- term
    } yield scala.math.pow(f, ee).toInt) +++ succeed(f)
  } yield e
  println("sample35: " + eval("100 * 2 ^ (2 + 1) ^ 2"))
sample35: 51200

すげえ。

8-a.

左結合の減算だと、BNF

espr ::= expr - nat
nat ::= '0' | '1' | '2' | ...

8-b, 8-c

おかしいけど。無限ループしちゃうし てかこれが問題点?

def wrongSub: Parser[Int] = for {
    e <- wrongSub
    _ <- symbol("-")
    n <- nat
    } yield e - n

8-d

6-1で書いたやつ。。。

プログラミングHaskell 8.10 練習問題

def nat: Parser[Int] = for {
    xs <- many1(digit)
  } yield xs.mkString.toInt

  def neg: Parser[Int] = for {
    _ <- symbol("-")
    n <- nat
  } yield -n

  def int: Parser[Int] = neg +++ nat
def comment: Parser[Unit] = for {
    _ <- symbol("--")
    _ <- many(sat(_ != '\n'))
    _ <- char('\n')
  } yield ()

3, 4は構文木を書きづらいから省略

  1. うーむ、たとえば「5」って数字を解析するとき、
  2. expr '+' expr | termの解析で、exprで解析して、natまでたどり着く => '+'がない => termの解析 .....
  3. term('+' expr | ε)の解析だと、termで解析して、natまでたどり着く => '+'がない => ε(おわり) って感じで、二度目の解析がなくなる、って感じなのかなあ・・・・

6-1. 注釈に書いてあるけど、右結合なのでこのままだと間違いになっちゃう!

// 1 - 2 - 3 - 4 = 1 - 2 - (3 - 4) = 1 - (2 - (-1)) = 1 - 3 = -2
  def exprBad: Parser[Int] = for {
    t <- term
    e <- (for {
      op <- symbol("+") +++ symbol("-")
      ee <- exprBad
    } yield op match {
      case "+" => t + ee
      case "-" => t - ee
    }) +++ succeed(t)

  } yield e

なので、いったんListにして、foldLeftを使って左結合にする。

  // 1 - 2 - 3 - 4 => 1 List(2, 3, 4) => (1 - 2) List(3, 4) => ((-1) - 3) List(4) => (-4) - 4 = -8 
  def expr: Parser[Int] = for {
    t <- term
    e <- (for {
      _ <- symbol("+")
      ee <- expr
    } yield t + ee) +++ many(for {
      _ <- symbol("-")
      ee <- term
    } yield ee).map(_.foldLeft(t)(_ - _)) +++ succeed(t)
  } yield e

6-2 こちらも同様にfoldLeftで左結合。

def term: Parser[Int] = for {
    f <- factor
    e <- (for {
      _ <- symbol("*")
      ee <- term
    } yield f * ee) +++ many(for {
      _ <- symbol("/")
      ee <- factor
    } yield ee).map(_.foldLeft(f)(_ / _)) +++ succeed(f)
  } yield e

7 BNFはこうなるはず。

/**
    * expr ::= term('+' expr | '-' expr | ε)
    * term ::= pow('*' term  | '/' term | ε)
    * pow ::= factor('^' pow | ε)
    * factor ::= '(' expr ')' | nat
    * nat ::= '0' | '1' | '2' | ...
    */

powはこうかける。ちなみに、termにも修正を入れる必要あり。

def pow: Parser[Int] = for {
   f <- factor
    e <- (for {
      _ <- symbol("^")
      ee <- term
    } yield scala.math.pow(f, ee).toInt) +++ succeed(f)
  } yield e
  println("sample35: " + eval("100 * 2 ^ (2 + 1) ^ 2"))
sample35: 51200

すげえ。

8-a. 左結合の減算だと、BNF

espr ::= expr - nat
nat ::= '0' | '1' | '2' | ...

8-b, 8-c おかしいけど。無限ループしちゃうし てかこれが問題点?

def wrongSub: Parser[Int] = for {
    e <- wrongSub
    _ <- symbol("-")
    n <- nat
    } yield e - n

8-d 6-1で書いたやつ。。。

プログラミングHaskell 8.8 数式

自然数と加算演算子、乗算演算子、および括弧からなる数式を考える。 加算演算子と乗算子は右結合とし、乗算演算子は加算演算子よりも高い結合順序を持つとする。 「2 + 3 + 4」 は「2 + (3 + 4)」 「2 * 3 + 4」は 「(2 * 3) + 4」 を意味する。

BNFで表していきたい。

expr ::= expr '+' expr | expr '*' expr | '(' expr ')' | nat
nat ::= '0' | '1' | '2' | ...

これでは、 +*の結合順位についての条件が含まれないので、 以下のように結合順位ごとに規則を用意して書く。

expr ::= expr '+' expr | term
term ::= term '*' term | factor
factor ::= '(' expr ')' | nat
nat ::= '0' | '1' | '2' | ...

さらに、右結合であることを表現するために、以下のように規則を変更する。 これで曖昧さはなくなる。

expr ::= term '+' expr | term
term ::= factor '*' term | factor

簡略化すると、

expr ::= term('+' expr | ε)
term ::= factor('*' term  | ε)
factor ::= '(' expr ')' | nat
nat ::= '0' | '1' | '2' | ...

これを書いていく。 Haskellでは、

expr :: Parser Int
expr = do t <- term
         do symbol "+"
            e <- expr
            return (t + e)
         +++ return t

と書けるようだけど、 Scalaだと以下のようになるのかな・・?

def expr: Parser[Int] = for {
    t <- term
    e <- (for {
      _ <- symbol("+")
      ee <- expr
    } yield t + ee) +++ succeed(t)
  } yield e

同様に、termとfactorについても式を書く。

def term: Parser[Int] = for {
    f <- factor
    e <- (for {
      _ <- symbol("*")
      ee <- term
    } yield f * ee) +++ succeed(f)
  } yield e

  def factor: Parser[Int] = +++(for {
    _ <- symbol("(")
    e <- expr
    _ <- symbol(")")
  } yield e)(nat)

なんとこれだけでできる!!

eval関数も加えて実行。ソースコードはあとで全部まとめて。

  println("sample23: " + eval("2 * 3 + 4"))
  println("sample24: " + eval("2 * (3 + 4)"))
  println("sample25: " + eval("2 * (3+4)"))
  println("sample26: " + eval("4"))
  println("sample27: " + eval("2*3-4"))
sample23: 10
sample24: 14
sample25: 14
sample26: 4
Exception in thread "main" java.lang.Exception: unused expression: -4
....

完成した!シンプル!! なんとなく、ちょっとだけパーサーについて理解が深まった気がした。。。

別話だけど、ParserOpsのimplicit conversionをExpressionオブジェクトの中かから使いたかったけど、最初うまく使えなかった。 ExpressionオブジェクトでParsersオブジェクトの中にあるimplicit conversion使いたかったら、ParsersオブジェクトをExpressionオブジェクトよりも上に書かなきゃいけないと。ふむ

プログラミングHaskell 8.7 空白の扱い

実用的なパーサーは、入力文字列中の「トークン」の前後に任意の空白を許す。...(中略)...あるトークンのパーサーを適用する際、前後の空白を無視する部品を定義する。

  def token[A](p: Parser[A]): Parser[A] = for {
    _ <- space
    v <- p
    _ <- space
  } yield v

tokenを利用すると、前後の空白を無視するパーサーを定義するのが簡単になる。

  def identifire: Parser[String] = token(ident) // 前後の空白を無視する識別子のパーサー
  def natural: Parser[Int] = token(nat) // 前後の空白を無視する自然数パーサー
  def symbol(xs: String): Parser[String] = token(string(xs)) // 前後の空白を無視する特定の文字列パーサー

自然数のリストを解析するパーサー。

def naturalList: Parser[List[Int]] = for {
    _ <- symbol("[")
    n <- natural
    ns <- many(for {
      _ <- symbol(",")
      nn <- natural
    } yield nn)
    _ <- symbol("]")
  } yield (n :: ns)

実行。

  println("sample21: " + parse(naturalList)(" [1, 2, 3] "))
  println("sample22: " + parse(naturalList)("[1, 2,]"))
sample21: List((List(1, 2, 3),))
sample22: List()

ほう!!

プログラミングHaskell 8.6 パーサーの部品

述語pを満足する一文字用のパーサー sat p を定義する。

def sat(p: Char => Boolean): Parser[Char] = {
    item.flatMap(x => p(x) match {
      case true => succeed(x)
      case false => failure
    })
  }

これを使えば、数字・アルファベットとかのパーサーも。

  def digit: Parser[Char] = sat(_.isDigit)
  def lower: Parser[Char] = sat(_.isLower)
  def upper: Parser[Char] = sat(_.isUpper)
  def letter: Parser[Char] = sat(_.isLetter)
  def alphanum: Parser[Char] = sat(_.isLetterOrDigit)
  def char(x: Char): Parser[Char] = sat(_ == x)

実行。

  println("sample9: " + parse(digit)("123"))
  println("sample10: " + parse(digit)("abc"))
  println("sample11: " + parse(char('a'))("abc"))
  println("sample12: " + parse(char('a'))("123"))
sample9: List((1,23))
sample10: List()
sample11: List((a,bc))
sample12: List()

パーサーcharを使って string xsを定義。

 def string(s: String): Parser[String] = s.toList match {
    case Nil => succeed("")
    case x :: xs => for {
      _ <- char(x)
      _ <- string(xs.mkString)
    } yield (x :: xs).mkString
  }

実行。

  println("sample13: " + parse(string("abc"))("abcdef"))
  println("sample14: " + parse(string("abc"))("ab1234"))
sample13: List((abc,def))
sample14: List()

次の二つのパーサー many pmany1 p は、パーサーpを失敗するまでできるだけ多く適用し、適用が成功した結果をリストにして返す。

相互再帰になってる。自分で発想できなそう・・・・

  def many1[A](p: Parser[A]): Parser[List[A]] = for {
    v <- p
    vs <- many(p)
  } yield (v :: vs)
  def many[A](p: Parser[A]): Parser[List[A]] = many1(p) +++ succeed(Nil)

実行。

  println("sample15: " + parse(many(digit))("123abc"))
  println("sample16: " + parse(many(digit))("abcdef"))
  println("sample17: " + parse(many1(digit))("abcdef"))
sample15: List((List(1, 2, 3),abc))
sample16: List((List(),abcdef))
sample17: List()

パーサー manymany1を使うと、「識別子(変数名)」のパーサーを定義できる。識別子は、小文字で始まり、0個以上のアルファベット文字か数字文字が続く。数字文字が一つ以上繰り返される「自然数」のパーサーや、空白文字やタブ文字、あるいは改行文字が一つ以上繰り返される「空白」のパーサーも定義できる。

本文では readって関数が使われてるけど、これはキャストする関数っぽい。この(本文の)場合だと、型推論からIntってことがわかるからキャスト先は省略されてるのかな。。Scalaではxsは List[Char]だから、 mkStringでStringにして、 toIntでキャスト。

  def ident: Parser[String] = for {
    x <- lower
    xs <- many(alphanum)
  } yield (x :: xs).mkString

  def nat: Parser[Int] = for {
    xs <- many1(digit)
  } yield xs.mkString.toInt

  def space: Parser[Unit] = for {
    _ <- many(sat(_.isSpaceChar))
  } yield(())

実行。

  println("sample18: " + parse(ident)("abc def"))
  println("sample19: " + parse(nat)("123 abc"))
  println("sample20: " + parse(space)(" abc"))
sample18: List((abc, def))
sample19: List((123, abc))
sample20: List(((),abc))

だんだんわかってきた!きがする!!!many, many1すごい。

プログラミングHaskell 8.5 選択

だいぶ間があいたけど、引き続き。

パーサーを結合させるもう一つの自然な方法は、一番目のパーサーを入力文字列に適用し、もし失敗したら、代わりに二番目のパーサーを適用することである。

+++(「または」と読む)を定義する。

def +++[A](p: Parser[A])(q: Parser[A]): Parser[A] = (inp: String) =>
    parse(p)(inp) match {
      case List() => parse(q)(inp)
      case List((v, out)) => List((v, out))
    }

実行。

object Main extends App {
  ...
  println("sample7: "  + parse(+++(item)(succeed('d')))("abc"))
  println("sample8: "  + parse(+++(failure)(succeed('d')))("abc"))
}
sample7: List((a,bc))
sample8: List((d,abc))