Haskell 勉強メモ 1
関数型プログラミングをちゃんとやろうと思って「すごいHaskellたのしく学ぼう!」を買って読み始めた。その勉強メモ。自分のメイン言語はScalaなので、気になったところを比較しながら見ていく。
内包表記
Haskellのリスト内包表記。集合の内包的記法の概念。$latex \{2\cdot x | x \in N, x \le 10 \}$ を書いてみる。
Prelude> [x*2 | x <- [1..10]] [2,4,6,8,10,12,14,16,18,20]
Scalaで似たようなものを書く場合は、for 内包表記を使う。
scala> for (i <- 1 to 10) yield i * 2 val res1: IndexedSeq[Int] = Vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)
もう少し複雑な条件で。
2倍したもののうち、12以上のものからなるリストが欲しい場合
Prelude> [x*2 | x <- [1..10], x*2 >= 12] [12,14,16,18,20]
scala> for (i <- 1 to 10 if i*2 >= 12) yield i*2 val res3: IndexedSeq[Int] = Vector(12, 14, 16, 18, 20)
10以上の全ての奇数を"BANG!"に置き換え、10より小さい全ての奇数を"BOOM!"に置き換える内包表記。
boomBangs xs = [ if x < 10 then "BOOM!" else "BANG!" | x <- xs, odd x] // boomBangs [7..13] // ["BOOM!","BOOM!","BANG!","BANG!"]
def boomBangs(xs: Seq[Int]): Seq[String] = { for (x <- xs if x % 2 == 1) yield { if (x < 10) "BOOM" else "BANG!" } } // boomBangs(7 to 13) // val res1: Seq[String] = Vector(BOOM, BOOM, BANG!, BANG!)
複数の述語を含める場合。Haskellは間まで区切る。and条件
*Main> [ x | x <- [10..20], x /= 13, x /= 15, x /= 19] [10,11,12,14,16,17,18,20]
Scalaは if
の真偽値が想定になるようにしてあげればいい
for (x <- 10 to 20 if x != 13 && x != 15 && x !=19) yield x val res2: IndexedSeq[Int] = Vector(10, 11, 12, 14, 16, 17, 18, 20)
複数のリストから値を取り出す
*Main> [ x+y | x <- [1,2,3], y <- [10,100,1000] ] [11,101,1001,12,102,1002,13,103,1003]
for ( x <- 1 to 3; y <- Seq(10,100,1000) ) yield x + y val res3: IndexedSeq[Int] = Vector(11, 101, 1001, 12, 102, 1002, 13, 103, 1003)
述語論理を追加する
*Main> [ x*y | x <- [2,5,10], y <- [8,10,11], x*y > 50] [55,80,100,110]
for ( x <- Seq(2,5,10); y <- Seq(8,10,11) if (x*y > 50) ) yield x*y
直角三角形を見つける
- 3辺の長さ全て整数
- 各辺の長さは10以下
- 周囲の長さは24に等しい
Haskellで書く
// aは斜辺cを超えないかつ、bはaを超えない triples = [(a, b, c) | c <- [1 .. 10], a <- [1 .. c], b <- [1 .. a], a ^ 2 + b ^ 2 == c ^ 2, a + b + c == 24] // [(8,6,10)]
Scalaで書く。Scalaだと ^
みたなメソッドはないから、implicit class
でちょろっと作って使ってみる
implicit class MyInt(value: Int) { def **(n: Int):Int = { n match { case 0 => 1 case k => value * **(k-1) } } } val x:Int = 2 ** 5 // 32 for ( c <- 1 to 10; a <- 1 to c; b <- 1 to a if a ** 2 + b ** 2 == c ** 2 if a + b + c == 24 ) yield (a,b,c) val res5: IndexedSeq[(Int, Int, Int)] = Vector((8,6,10))
全体的にHaskellの方が数学チックな表現ができるなって印象。Scalaと対比でみることで、Scalaの理解が進みそう。