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]

Scalaif の真偽値が想定になるようにしてあげればいい

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の理解が進みそう。