前回から少し時間が空いてしまいましたが、吉祥寺.pm9を開催します。
ついに2周年ということで、皆様の参加をお待ちしております!
今回テーマを「Perl!」と書いてみましたが、まぁ精神的というか、象徴としてのPerlというか、そんな解釈で自由に参加してみて下さい。3周年に向けた第一歩です!
少し間が空いてしまいましたが、第二回を開催します。
今回も、「Scala関数型デザイン&プログラミング ―Scalazコントリビューターによる関数型徹底ガイド」の読書会という形式でやりますが、LTや環境構築とか、基本的な文法の話とか、気軽に聞いてみて下さい。
あらかじめ、こんなことが聞きたい・話したいことが有る、という人はイベントページのコメント欄に書いておいて頂けると準備します!
「Scala関数型デザイン&プログラミング」のexerciseを解き進めるための環境準備をだいぶ書き換えて、読み進める上での準備作業を全部網羅してみたので、改めて「Scala関数型デザイン&プログラミング」を最初から読み進めるためのガイドっぽいことを書いてみます。
今回は第1章と、第2章までです。
Scala関数型デザイン&プログラミング ―Scalazコントリビューターによる関数型徹底ガイド (impress top gear)
第1章はおもに「副作用の排除」について書かれています。
冒頭のコードが実際に実行できない(外部のAPIに依存している、という設定)なのと、コード事例自体があまり副作用の排除によるメリットが見えづらい(テスタビリティが上がったことを実感しづらい)コードになっているので、理解しづらいところがあります。
本当は、「コーヒーの代金を課金する」という本質的な機能はそのままで、その後の色々な機能拡張(まとめて払うとか)のときに重複が上手く排除できたことを示せると良いのでしょう。しかし、そこまでやると紙面も使いすぎてしまうので、かなり急ぎ足の解説になっています。なので、まずはざっと読んで、先に進める方が良さそうです。
実際に、外部のAPIや、再現性の低い機能(時刻とか、乱数だとか)などを対象にテストで苦労したり、随時に行った機能追加でまったく重複したコードを書いた経験が無いと、メリットが理解しづらいかもしれません。
以降、本を読んだだけでは分からなさそうな箇所をポイントを絞って解説していきます。
Scalaという言語の解説が始まります。わりとコンパクトに、分かりやすくまとまっていますが、object
キーワードで作られるシングルトンの解説がポイントです。
Javaではデザインパターンの一つとして使われているシングルトンがScalaでは言語仕様として用意されています。機能としては書かれていますが、あまり存在理由というか、そのメリットについては書かれていないので、デザインパターンなどの解説を(実装方法は別として)読んだ方が理解し易いでしょう。
scalac
とscala
コマンドを使ってコードを実行する方法が説明されています。sbt
からの実行方法については、下記の記事を参考にしてみてください。
「Scala関数型デザイン&プログラミング」のexerciseを解き進めるための環境準備
高階関数という用語が難解なイメージを抱かせますが、Java8ではラムダ式、LL系言語やJavaでも無名関数と、実際には割とよく使われている概念です。
自分がいままで使ってきた言語でどう実現されているか、振り返ってみると分かりやすいでしょう。
再帰も同様に、まずは自分が使ったことのある言語で試してみると良いでしょう。
アルゴリズム系では定番の、フィボナッチ数列を求める関数の作成です。
関数のひな形は、以下のファイルに書かれています。
exercises/src/main/scala/fpinscala/gettingstarted/GettingStarted.scala
初期状態では下記の通りのコードになっています。
object MyModule { ... def fib(n: Int): Int = ??? ... }
見慣れない???
は正しいScalaの構文で、コードが未定義であることを示すメソッドです。実行するとNotImplementedError
の例外が送出され、実行時エラーになります。
まずはこの???
を削除して、解答となるコードを書いていきます。回答はanswer
の同名のファイルに書かれています。
ここでもフィボナッチ数列と、Scalaにおける再帰を一度に理解しようとすると混乱してしまうので、まずは既に理解している言語で再帰を使って記述してみることをお勧めします。
コードを書いたら、以下の手順で実行します。サンプルコードではMyModule
というobject内に定義するようになっているので、fpinscala.gettingstarted
パッケージをインポートして、MyModule.fib
という形式で関数を呼び出します。
$ ./sbt > project exercises > console scala> import fpinscala.gettingstarted._ scala> MyModule.fib(10) res1: Int = 55 scala> MyModule.fib(0) res2: Int = 0
カリー化や、関数合成と言った重要なキーワードが出てきますが、この段階ではexerciseをどんどん解いていって、先に進める方が良さそうです。
ただし、これらのキーワードはのちのち重要になってきますが、凄くさらっと説明されているので、先に進む上ではこの本以外のソースから定義や使い方を調べておいた方が良いでしょう。
あくまで自分が初めて読んだときにつまづいた所を中心に書いていったので、当然別のバックグランドの人は別のところでつまづくと思います。
次回は第3章からスタートです。
Scala関数型デザイン&プログラミング―Scalazコントリビューターによる関数型徹底ガイド
Scalaではありませんが、Haskellベースの関数型プログラミングの入門本が増補改訂されたので、ぜひ読んでみたいところです。
[増補改訂]関数プログラミング実践入門 ──簡潔で、正しいコードを書くために (WEB+DB PRESS plus)
引き続き、「Scala関数型デザイン&プログラミング」を読み進めて、第3章に突入します。
Scala関数型デザイン&プログラミング―Scalazコントリビューターによる関数型徹底ガイド
第3章はListクラスの実装から始まります。実装したListクラスの内容は詳細に解説されていますが、割とサクっと進むので、実際には実行結果を丁寧に確認していった方が理解が深まります。
まずは、例として紹介されているコード例を確認します。
$ ./sbt > project exercises > console scala> import fpinscala.datastructures._ scala> val ex1: List[Double] = Nil ex1: fpinscala.datastructures.List[Double] = Nil scala> val ex2: List[Int] = Cons(1, Nil) ex2: fpinscala.datastructures.List[Int] = Cons(1,Nil) scala> val ex3: List[String] = Cons("a", Cons("b", Nil)) ex3: fpinscala.datastructures.List[String] = Cons(a,Cons(b,Nil))
apply
メソッドを使ったパターンが載っていないので、以下のように試してみましょう。
scala> val ex4: List[Int] = List[Int](1, 2, 3) ex4: fpinscala.datastructures.List[Int] = Cons(1,Cons(2,Cons(3,Nil))) scala> val ex6: List[Double] = List(1, 2, 3) ex6: fpinscala.datastructures.List[Double] = Cons(1.0,Cons(2.0,Cons(3.0,Nil))) scala> val ex7 = List[Double](1, 2, 3) ex7: fpinscala.datastructures.List[Double] = Cons(1.0,Cons(2.0,Cons(3.0,Nil))) scala> val ex8 = List(1.0, 2, 3) ex8: fpinscala.datastructures.List[Double] = Cons(1.0,Cons(2.0,Cons(3.0,Nil)))
scalaが型を推論してくれるので、ex7や、ex8のような書き方でもきちんとList[Double]型になっていることが分かるかと思います(ex8ではパラメータが1.0になっていることに注意!)。
まずは、こうやって色々な組み合わせでREPLからListのオブジェクトを作ってみると、オブジェクトの仕組みやリテラルで書いたデータの型がどうのように推論されるのか、分かると思います。
第3章で一番難しいのが「共変」とNothing型の概念です。
空のリストを作ると、NilだけのListが生成され、List全体がNothing型になります。
scala> val e = List()
e: fpinscala.datastructures.List[Nothing] = Nil
そのリストをConsで引数に渡すと、リスト全体の型は有効な値に合わせて推論されます。
scala> val list = Cons(1, e) list: fpinscala.datastructures.Cons[Int] = Cons(1,Nil)
これは、Nothing型がすべての型のタイプになっていることで実現されていますが、すぐに理解するのは難しいところです。
下記の記事が理解の参考になるでしょう。
最初からsumメソッド(リストの要素の足し算)と、productメソッド(リストの要素のかけ算)が用意されてますが、これも実行してみましょう。
scala> val r0 = List.sum(List()) r0: Int = 0 scala> val r1 = List.sum(List(1, 2, 3)) r1: Int = 6 scala> val r2 = List.product(List()) r2: Double = 1.0 scala> val r3 = List.product(List(1, 2, 3)) r3: Double = 6.0
よく考えると空のリストの積が1.0になるのは正しいのか?という気もしますが、足し算とかけ算が実行されてることが分かります。
「object型で定義されるものはシングルトンで、同名のクラスが定義されているobjectはコンパニオンオブジェクトで…」という所も、いきなり色々な概念が登場して、Scalaの分かりづらいと感じるところです。
「Scalaのコンパニオンオブジェクト」のコラムと、実際のコードを上から順に追いかけるのが理解の近道です。
パターンマッチもまたScalaの中でも特に強力な機能の一つです。「Scala関数型デザイン&プログラミング」の中では割とさらっと解説されているだけなので、「Scala スケーラブルプログラミング」の第15章「ケースクラスとパターンマッチ」をよく読んで理解することをお勧めします。
最初の要素を削除するので、実行結果は下記の通りになります。
scala> val list0 = List(1,2,3) list0: fpinscala.datastructures.List[Int] = Cons(1,Cons(2,Cons(3,Nil))) scala> val list1 = List.tail(list0) list1: fpinscala.datastructures.List[Int] = Cons(2,Cons(3,Nil))
ListがNilの場合について特別に言及されていますが、以下のようなオブジェクトに対してtailを実行することを想定すると、いくつかの設計上の選択肢が有り得ることを示唆しています。
scala> val e = List()
e: fpinscala.datastructures.List[Nothing] = Nil
確かに、「先頭を削除する」ということをよく考えて実装を決めた方が良さそうです。
scala> val list0 = List(1,2,3) list0: fpinscala.datastructures.List[Int] = Cons(1,Cons(2,Cons(3,Nil))) scala> val list2 = List.setHead(list0, 4) list2: fpinscala.datastructures.List[Int] = Cons(4,Cons(2,Cons(3,Nil)))
回答事例のコードでは、Nilを指定すると、Nilが2回格納された不正なリストができあがります(型もAnyになってしまい、sumメソッドが実行できない)。そこまでは考慮されていないようです。
scala> val list3 = List.setHead(list0, Nil) list3: fpinscala.datastructures.List[Any] = Cons(Nil,Cons(2,Cons(3,Nil)))
ここまでsbtのコンソール(REPL)を使って実行結果を確認してきましたが、やはり実行結果はテストコードを書いて、テストで確認した方が、何度でも試すことができるのでお勧めです。exerciseで書いたコードをテストコードで確認する方法を紹介します。
Scalaのテスティングフレームワークとしては、ScalaTestか、Specs2がよく使われていますが、ここではScalaTestを使うことにします。
Build.scala
のoptsに、ScalaTestへの依存を追加します。
resolversの最後にカンマを追加するのを忘れないように。
val opts = Project.defaultSettings ++ Seq( scalaVersion := "2.11.7", resolvers += "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/", libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.0" % "test" )
exercises/src/test/scala/fpinscala/datastructures/ListTest.scala
というテストコードのファイルを用意します。パスの3番目がtest
になっていることに注意して下さい。Javaではおなじみですが、テストコードはtestディレクトリ
に保存します。
tailメソッドのテストコードは以下のように書けます。
ScalaTestではいくつかのテストスタイルが使えますが、ここでは一番シンプルに書けるFunSuiteを使っています。
最後のshould equal
で期待する値との比較をしています。
import org.scalatest._ import fpinscala.datastructures._ class ListSuite extends FunSuite with Matchers { test("tailメソッドは先頭の要素を削除する") { val listInt = List(1, 2, 3) val listDouble = List(1.0, 2.0, 3.0) val listString = List("one", "two", "three") List.tail(listInt) should equal (List(2, 3)) List.tail(listDouble) should equal (List(2.0, 3.0)) List.tail(listString) should equal (List("two", "three")) } }
テストはsbtから実行します。
$ ./sbt > test ... [info] ListSuite: [info] - tailメソッドは先頭の要素を削除する ... [success]...
最後に[success]が出力されればテスト全体が成功したことになります。テストが一つでも失敗すると[error]が表示されます。
setHeadメソッドのテストを追加してみます。
import org.scalatest._ import fpinscala.datastructures._ class ListSuite extends FunSuite with Matchers { val listInt = List(1, 2, 3) val listDouble = List(1.0, 2.0, 3.0) val listString = List("one", "two", "three") test("tailメソッドは先頭の要素を削除する") { List.tail(listInt) should equal (List(2, 3)) List.tail(listDouble) should equal (List(2.0, 3.0)) List.tail(listString) should equal (List("two", "three")) } test("setHeadメソッドは先頭の要素を置き換える") { List.setHead(listInt, 4) should equal (List(4, 2, 3)) List.setHead(listDouble, 4) should equal (List(4.0, 2.0, 3.0)) List.setHead(listString, "four") should equal (List("four", "two", "three")) } }
この後出てくるdropメソッド以降も同様にテストを書きながら進めていくと良いでしょう。
exerciseのコードの中身については次回以降に見ていきます。
Kichijojipm-mini 011、無事に開催できました!というエントリーをアップし忘れていました…
詳しくはメインゲストのcodehexさんのブログを参照くださいませ…。
一時は台風でどうなることかと思いましたが、急遽参加いただいた方も含めてとても盛況でした!
前回に続いて、「キチスカ002」開催に向けて、「Scala関数型デザイン&プログラミング」の読み方を解説します。
Scala関数型デザイン&プログラミング―Scalazコントリビューターによる関数型徹底ガイド
第1章は関数型プログラミングの概念の解説で、なかなか実際に動くコードが出てこないのですが、第2章から実際に動くコードが出てきます。
第2章では最初に基本的なScalaのコード例として、「リスト2-1 gettingstarted/GettingStarted.scala
」が出てきます。本文中にはコードの解説は有りますが、sbtからの実行方法が書かれていません。このコードは、下記の手順で実行することができます。
$ git clone https://github.com/fpinscala/fpinscala.git $ cd fpinscala $ ./sbt > project exercises > run
sbtのrunコマンドは、mainメソッドを実行するコマンドですが、サンプルコードのように一つのプロジェクトに複数のmainメソッドが有ると、どのパッケージに属するmainメソッドを実行するか、確認してくれます。
Multiple main classes detected, select one to run: [1] fpinscala.gettingstarted.MyModule .... Enter number: 1 ....
今回の場合は、最初に表示されるfpinscala.gettingstarted.MyModule
を実行するとので、1を選択します(1を入力して、enter keyを押下)。
実行結果が表示され、終了します。
The absolute value of -42 is 42
引き続き、[success]...
という文字列は、sbtが出力している文字列で、MyModule
の中で定義されたものではありません。
あとは繰り返し、コードを書いて、同じようにsbtから実行します。
2016/10/16追記:全般的に書き直し 2017/4/1追記:誤りをいくつか修正
先日、「キチスカ001」と称して、「Scala関数型デザイン&プログラミング」の読書会を開催しました。
Scala関数型デザイン&プログラミング―Scalazコントリビューターによる関数型徹底ガイド
今回は1章、2章をざっと進めました。次の「キチスカ002」では3章をメインに進める予定です。
「Scala関数型デザイン&プログラミング」の1章、2章は関数型プログラミングの概説や、ごく基本的なScalaの文法(高階関数やカリー化などの難易度が高いものも出てきますが…)などの解説になっていて、3章以降ひたすら例題に沿って、実際にScalaのコードを書くことで関数型プログラミングの理解を進める、という内容になっています。
しかし、もともとこの本はScalaという言語そのものや、関連するツールチェーン自体の入門書ではなく(冒頭にもしっかり書かれています)、あくまでScalaを題材にして関数型のプログラミングや、デザイン(設計)を学ぶための本なので、Scala自体のインストール方法や、実行方法についてはあまり親切に書かれていません。
GitHubに置かれているサンプルコードのリポジトリには例題(exercise)を解くためのヒントや回答がかなり詳細に書かれていますが、本文中ではURLが書かれているだけで、詳しい構成や、使い方ついてはまったく触れられていません。
「キチスカ002」の開催に向けて、例題(exercise)を解き進めるための準備について、以下にまとめておきます。参考にしてください。
Scalaはご存じの通り、JVM(Java Virtual Machine)上で実行される言語です。そのため、Scalaを使うためにはJDK(Java Development Kit)をインストールする必要が有ります。
JDKのインストール方法はいろいろな所で解説されていますので、ここでは詳細は割愛しますが、自分の環境に合わせてOracleのサイトからインストーラをダウンロードしてインストールしてください。
macOSではbrew cask
コマンドでインストールできます。以下の1行で最新版がインストールされます。
$ brew cask install java
Linuxの場合は、パッケージマネージャーから簡単にインストールできるOpenJDKを使った方が良いでしょう。
yum
や、apt
といったパッケージマネージャーからインストールしてください。
「Scala関数型デザイン&プログラミング」に記載されているコード例や、例題(exercise)のヒント、解答などはすべてGitHub上のリポジトリに置かれています。そのため、サンプルコードをダウンロードするためには、Gitをインストールする必要があります。
Gitのインストール方法もいろいろな所で解説されているので割愛しますが、公式サイトのドキュメントに詳しく書かれていますので、そちらを参考にしてみてください。
$ brew install git
任意のディレクトリに、サンプルコードのリポジトリをクローンします。
$ git clone https://github.com/fpinscala/fpinscala.git
これで準備完了です。
特にScalaのコンパイラをインストールしていませんが、そのあたりの仕組みは次の「sbtの実行」で解説します。
ダウンロードしたサンプルコードのディレクトリに、sbt
(Windows用はsbt.cmd
)というコマンドが含まれていますので、まずはこれを実行します。
$ cd fpinscala $ ./sbt
Windowsの場合は、sbt.cmd
を起動します。
$ cd fpinscala $ .\sbt.cmd
sbt
は、Simple Build Toolという身も蓋もないくらい普通の名前が付けられたScala用のビルドツールです。
sbt Reference Manual — 始める sbt
sbt自体がScalaのコンパイラ一式をダウンロードしてくれるので、Scala自体を手動でインストールする必要はありません。
sbt
を起動して特にエラーが表示されなければ、準備はOKです。
sbtはひじょうに高機能なビルドツールですが、のちほど説明するproject
、compile
、console
、run
、test
の5つのコマンドを覚えれば、以降のexerciseのコードを実装する上では十分です。
sbtでは一つのリポジトリで複数のプロジェクトを管理できます。そのプロジェクトを切り替えるためのコマンドです。どのようなプロジェクトが含まれているかは、projects
コマンドで確認します。
ターゲットのプロジェクトに含まれるすべてのソースコードをコンパイルします。
console
sbt
からScalaのREPL(Read - Eval - Print - Loop)を起動します。
run
ScalaもJavaと同様にプログラムの実行は、main関数
から始まります。run
は、main関数
を実行します。
もし、プロジェクト内に複数のmain関数
が含まれる場合は、どのパッケージに属するmain関数
を実行するか、選択するためのリストが表示されます。
Javaや、Scalaでは、テストコードはsrc/test/*
ディレクトリに格納されますが、test
は、testディレクトリ
に格納されたテストコードを実行します。
macOSや、Linux環境で実行する際には、sbt
コマンドを使いますが、Java8以降では不要なパラメータ(PermSize)が書かれており、実行のたびに警告メッセージが出てしまうので、Java8以降のJDKをインストールしている場合は、下記の通り書き換えることをお勧めします。
sbtは単なるシェルスクリプトで、実態はsbt-launch.jarにパラメータを与えて起動しているだけです(Windows用のsbt.cmd
には最初から記述は有りません)。
変更前
SBT_OPTS="-Xms512M -Xmx1536M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256M"
変更後
SBT_OPTS="-Xms512M -Xmx1536M -Xss1M -XX:+CMSClassUnloadingEnabled"
以降、コードの実装を始めますが、サンプルコードのリポジトリは今でも日々コミットが続いています。自分が書いたコードを区別するためにも、gitのbranchを作成しておきましょう。
$ git checkout -b myexecirse
サンプルコードのディレクトリ構成は大きく分けて、以下の3つに分かれています。
exerciseと、answerが対になっているので、テキストエディタを上下分けて、それぞれ表示しながら進めるのがおすすめです。
書籍に掲載されているサンプルコードと、exerciseで書かれている関数のひな形(関数名と、引数、返値だけが書かれている)が掲載されています。例えば最初のリストの実装であれば、以下のファイルを書き換えていく形になります。
関数のひな形が掲載されているものはコードの本体が「sys.error(“todo”)」となっていて、実行するとエラーになるようになっています。まずはこの「sys.error(“todo”)」という部分を削除して実装を始めることになります。
def tail[A](l: List[A]): List[A] = sys.error("todo")
↓
def tail[A](l: List[A]): List[A] = l match { ... }
なぜか関数のひな形が書かれていないものや、本誌に書かれたひな形と引数名が違ったりするものも有りますが、後述のanswerに書かれている回答の関数名と、引数、返値を見ながら進めると良いでしょう。
exerciseの回答と、その解説が書かれています。
先ほどのListの回答は、以下のファイルに書かれています。
exerciseの回答がファイル別に置かれています。また、回答ごとに、ヒントも置かれているので、まずはこのヒントを見ながら実装していくと良いでしょう。
exerciseのディレクトリ配下に置かれているファイルを書き換えながら進めていくので、exerciseのディレクトリだけがコンパイルされるようにsbt
上のプロジェクトを切り替えておきます。
また、compileコマンド
でエラーが無いことを確認したら、consoleコマンド
でScalaのREPL(Read-eval-print loop)が起動するので、そのまま実装したパッケージ(Listでいえば、fpinscala.datastructures)をロードすることで、実装の確認ができます。
$ ./sbt > project exercise > compile > console scala> import fpinscala.datastructures._ scala> val x = List.Cons(1, Cons(2, Nil)) ...
エラーが出たり、挙動が正しく無いときは、実装の正しさを、answerか、answerkeyを参照して確認します。
あとはひたすら繰り返しです。頑張りましょう。
なお、例題によってはmain関数
を起動するものも有ります(第2章のgettingstarted
など)。それらのmain関数
を起動するときはsbt
からrunコマンド
を実行します。
プロジェクトの中には、複数のmain関数
が有るので、どれを起動するか選択するリストが表示されます。パッケージ名を確認して、該当する番号を入力して、エンターキーを押下してください。
> run ... Multiple main classes detected, select one to run: [1] fpinscala.streamingio.ProcessTest [2] fpinscala.gettingstarted.MyModule [3] fpinscala.gettingstarted.FormatAbsAndFactorial [4] fpinscala.gettingstarted.TestFib [5] fpinscala.gettingstarted.AnonymousFunctions [6] fpinscala.iomonad.IO2aTests [7] fpinscala.iomonad.IO2bTests Enter number:
exerciseのファイルには、関数のひな形は書かれていても、対象のexerciseの番号が書かれていません。あとで振り返ったときに分かりづらいので、コメントでexerciseの番号を書いておくと便利です(「// exercise 3.3
」のような形式で書いておきます)。
また、いろいろと自分で気がついたところも都度コメントで残しておくと、あとで振り返ったときに便利です。
章ごとに実装が終わったら、gitでコミットしておくとよいでしょう。
また、急激に難易度が上がるところや、最初から「難問」と書かれているような例題も有りますが、そうゆうときはさっさとanswerのコードを写経して、挙動や実装の背景を理解するようにシフトした方が良いでしょう。そのときに気がついたことをひたすらコメントで残しておく方が理解が早いでしょう。
sbt
のconsole
を使って実行結果を確認しても良いですが、いまどき実行結果はテストコードを書いて、テスト結果で確認すべきです。
以下に、exerciseで書いたコードをテストコードで確認する方法を紹介します。
Scalaのテスティングフレームワークとしては、ScalaTestか、Specs2がよく使われていますが、ここではScalaTestを使うことにします。
Build.scala
のoptsに、ScalaTestへの依存を追加します。
resolversの最後にカンマを追加するのを忘れないように。
val opts = Project.defaultSettings ++ Seq( scalaVersion := "2.11.7", resolvers += "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/", libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.0" % "test" )
exercises/src/test/scala/fpinscala/datastructures/ListTest.scala
というテストコードのファイルを用意します。パスの3番目がtest
になっていることに注意して下さい。Javaではおなじみですが、テストコードはtestディレクトリ
に保存します。
例えば、3章で出てくるtailメソッド
のテストコードは以下のように書けます。
ScalaTestではいくつかのテストスタイルが使えますが、ここでは一番シンプルに書けるFunSuiteを使っています。
最後のshould equal
で期待する値との比較をしています。
import org.scalatest._ import fpinscala.datastructures._ class ListSuite extends FunSuite with Matchers { test("tailメソッドは先頭の要素を削除する") { val listInt = List(1, 2, 3) val listDouble = List(1.0, 2.0, 3.0) val listString = List("one", "two", "three") List.tail(listInt) should equal (List(2, 3)) List.tail(listDouble) should equal (List(2.0, 3.0)) List.tail(listString) should equal (List("two", "three")) } }
テストはsbtから実行します。
$ ./sbt > test ... [info] ListSuite: [info] - tailメソッドは先頭の要素を削除する ... [success]...
最後に[success]が出力されればテスト全体が成功したことになります。テストが一つでも失敗すると[error]が表示されます。
どうように3章に出てくるsetHeadメソッド
のテストを追加します。
import org.scalatest._ import fpinscala.datastructures._ class ListSuite extends FunSuite with Matchers { val listInt = List(1, 2, 3) val listDouble = List(1.0, 2.0, 3.0) val listString = List("one", "two", "three") test("tailメソッドは先頭の要素を削除する") { List.tail(listInt) should equal (List(2, 3)) List.tail(listDouble) should equal (List(2.0, 3.0)) List.tail(listString) should equal (List("two", "three")) } test("setHeadメソッドは先頭の要素を置き換える") { List.setHead(listInt, 4) should equal (List(4, 2, 3)) List.setHead(listDouble, 4) should equal (List(4.0, 2.0, 3.0)) List.setHead(listString, "four") should equal (List("four", "two", "three")) } }
すべて英語で書かれていますが、本書に載っていないChapter NotesがGitHubのWikiに有りますので、時間に余裕があればこちらも読んでおくと参考になります。
Home · fpinscala/fpinscala Wiki · GitHub
「Scala関数型デザイン&プログラミング」は非常に噛み応えが有るというか、じっくり取り組む必要が有るし、exerciseのコードを書いてみても、書いたことの意味をきちんと解説してくれる人が近くにいないと、挫折し易いというか、本当にストロングスタイルな本ですが、最後まで進めると確実に実力がつく良本ですね。
Scala関数型デザイン&プログラミング―Scalazコントリビューターによる関数型徹底ガイド
「Scala関数型デザイン&プログラミング」はScalaの入門書ではないので、Scalaの入門書としては元祖Scalaの解説本である「Scalaスケーラブルプログラミング」の方がおすすめです。つい先日最新の第3版が邦訳されました。
なお、技術書と言えば定番のO'Reillyからも何冊かScala本がリリースされていますが、邦訳がなかなかリリースされないですね。
Programming Scala: Scalability = Functional Programming + Objects
Learning Scala: Practical Functional Programming for the JVM