動機
Java, C++ → Scalaとやってきて、気づいたことはJavaからScalaに受け継がれたのはJVMとしての便利さだけであり、関数型言語的要素はむしろHaskellから受け継がれているということだ。さっそく調査班はYodobashiで以下を購入した。
- 作者: Miran Lipovaca
- 出版社/メーカー: オーム社
- 発売日: 2012/09/21
- メディア: Kindle版
- 購入: 4人 クリック: 9回
- この商品を含むブログを見る
プログラミング言語における学習では新しい概念を知ることが大事だと思っているので(…もちろんアルゴリズムも重要だが、アルゴリズムは概念によりかなり変化する)、新しい考え方だと感じたことを中心にメモする。
読み進めるときには、以下を見ながらだと面白い
Scalaコレクションメソッドメモ(Hishidama's Scala collection method Memo)
第1章 はじめの第一歩
まずはhaskellによる四則演算、否定、リストとか。
1.3 リスト入門
ここの部分、まるっきりScalaのリスト操作です。
head, tail, init, last
1~5までの整数を含んだリストを作った時、上記のメソッドは以下のような動きをします。数値だと実用性が感じられないかもしれないですが、例えばリストの中身がテキストの1行を表すStringだとしたら便利な気がしてきます。
scala> val list = List(1,2,3,4,5) list: List[Int] = List(1, 2, 3, 4, 5) scala> list.head res0: Int = 1 scala> list.tail res1: List[Int] = List(2, 3, 4, 5) scala> list.init res2: List[Int] = List(1, 2, 3, 4) scala> list.last res3: Int = 5
1.4 レンジでチン!
・リストを [0..20] のような表記で作成できるレンジの話
・無限リストの話
無限リストは知らないけど、Scalaでもそのようなことはできるし、D言語でも出来た気がする。
Scala Seqメモ(Hishidama's Scala Seq Memo)
1.5 リスト内包表記
・Pythonista(Python使い)からネット上で百万回聞かされたリスト内包表記です、何が嬉しいんでしょうね???
1.6 タプル
・タプルです。タプルはパターンマッチングと組み合わせると条件分岐をかなり美しく書けることを最近教わりました。
// Javaなら if (isHoge && isFuga) { // うぐぐ… } else if (isHoge && !isFuga) // バグる、絶対にバグる! } else { // ここはこれでいいんだっけ }
// Scalaで (isHoge, isFuga) match { case (true, true) => // いえい case (true, false) => // おー case (_, _) => // 後は知らん! }
2要素、3要素と増えていく内にありがたみが倍になる(当社比)。返り値にもタプルとか使える気がする。
第2章 型を信じろ!
タイトルそのままです
第3章 関数の構文
Haskell特有の構文について
3.1 パターンマッチ
死ぬほど実用的なパターンマッチが出てきます。
Scala matchメモ(Hishidama's Scala match Memo)
3.4 let It Be
letも同じようなもんらしいですが、こちらは式らしい
第4章 Hello 再帰!
再帰関数を実装しまくる気持ち悪い章です
4.4 再帰的に考える
再帰関数の書き方についての指南があります。
別に再帰関数自体はがんばらなくても書ける気がするので、がんばって再帰関数にしたい時にこれに従えば良いのでしょうか。つまり、末尾再帰にしたい場合のお話だと思うのです。ちなみにScalaだと末尾再帰であるかどうかを判別するアノテーションがあるらしいですよ… Scala @tailrecメモ(Hishidama's Scala @tailrec Memo)
再帰するメソッド(自分自身を呼び出すメソッド)は、呼び出す回数が多くなるとその分スタックフレームを消費してしまう(最悪はOutOfMemoryErrorが発生する)が、末尾再帰だと効率の良いループに変換してくれる。
第5章 高階関数
ネット上で難しそうに書かれている概念群が、優しい筆致で書かれた解説により脳内のイメージと結びつく章です。
高階関数
Haskellの関数は、引数として関数を取ったり返り値として関数を返したりできます。
5.1 カリー化関数
カリー化関数は、複数の引数を取る代わりに、常にちょうど一つの引数を取る関数です。
5.3 関数プログラマの道具箱
いよいよあのScalaと同じコレクション操作関数群が現れます。ソースコードの例は、以下を見たほうが楽しいでしょう。
Scalaコレクションメソッドメモ(Hishidama's Scala collection method Memo)
だいたいこのへんのコレクション操作関数は関数を引数に取るので、高階関数というわけです。
すごいですね、もうみんな関数型プログラマですよ!*1
関数 | 引数 |
---|---|
map | 関数とリスト |
filter | 述語*2とリスト |
5.4 ラムダ式
はい、ラムダ式です。Java8でも実装されたので、もはやあまり目新しい概念でもないですね。
今一度昔の日記でC++などでどう書くか振り返ると、nantonaku-shiawase.hatenablog.com
そう、こう書いていたのでした
// C++
[] (type arguments) -> type { 式 }
これが、Haskellだとこう書ける
ghci> map (\x -> x + 3) [1, 6, 3, 2] [4, 9, 6, 5]
Scalaだと
scala> List(1, 6, 3, 2).map { x => x + 3 } res0: List[Int] = List(4, 9, 6, 5)
5.5 畳み込み、見込みアリ!
foldl, foldr、ScalaにおけるfoldLeft, foldRight。
これらは単純なデータ処理の場合あまり役に立たない気もするが、アキュムレータという名前で後で再度紹介されてる。
5.6 $を使った関数適用
Haskellでの省略表現。
5.7 関数合成
この辺の話に相当scalamanual.blog.fc2.com
ポイントフリースタイル
関数合成をすることで、関数の書き方が変わる。
// これが fn x = ceiling (negate (tan (cos (max 50 x))) // こうなる fn = ceiling . negate . tan . cos . max 50
5章まではこれでおしまい!