tkawachi Blog

Scala の For

C や Java と同じように Scala にも for がある。

昔の Java におけるfor 文は、C と同じように for (初期化; 継続条件; カウンタ更新) という形しかなかった。 Java 5.0 で導入された拡張for文(for (型 変数: コレクション)の形)では java.lang.Iterable を実装したオブジェクトならなんでも繰り返しができるようになった。 便利になったなあと思ったのを覚えてる(2004年の話)。

文法

Scala で C の for 的なものをやろうとすると

// C では
for (int i = 0; i < 10; i++) { … }
// Scala では
for (i <- 0 until 10) { … }

となるので「 for (i <- 初期値 until 上限) という固定形なんだー」と最初は思ったのだが、そうではなく for (i <- 初期値.until(上限)) であり for (i <- obj) が for 式のパターンである。 ここで初期値.until(上限)Range 型の値を返す。

Scalaのimplicit

Scala の implicit のお勉強メモ。

なぜ implicit を使いたくなるか?

コード片を見ただけではわからなくなるので、暗黙的な記述はあまり使わない方がいいんじゃないかと個人的に思うのだけど。 それでも便利な使い道があるから使われているみたい。

今自分が知っているところで次のパターンがあるみたい。 他の便利な使い方もきっとあるんじゃなかろうか。

  • 既存のクラスを変更せずにメソッドを追加したいとき (pimp my library pattern)。 implicit な関数, implicit なクラスを使う。
  • 型パラメータ情報を実行時に使いたいとき。implicit パラメータをつかう。
  • 共通のインタフェースを持たないクラス群に、共通のインタフェースをあとづけするとき (CONCEPT pattern)。implicit パラメータをつかう。

テストデータのセットアップに Trait を使う

9月半ばから始まった Functional Programming Principles in Scala を受講している。 課題内のテストの書き方でこうやるのか、というところがあったのでメモ。

テストケース間でデータを共有したいときがある。 たとえば以下の例では data1data2 が共通なので共通化したくなる。

class FooSuite extends FunSuite {
    test("test A") {
        val data1 = new FooData(…)
        val data2 = new FooData(…)
        // tests with data1 and data2
    }

    test("test B") {
        val data1 = new FooData(…)
        val data2 = new FooData(…)
        // another tests with data1 and data2
    }
}

普通は以下のようにメンバ変数にしたくなると思う。

class FooSuite extends FunSuite {
    val data1 = new FooData(…)
    val data2 = new FooData(…)

    test("test A") {
        // tests with data1 and data2
    }

    test("test B") {
        // another tests with data1 and data2
    }
}

この方法は少し問題がある。

  • new FooData(…) が例外を出した場合に、FooSuite のインスタンス化に失敗するという問題がある
  • FooData が mutable な場合に、前に実行したテストの内容により結果が変わる可能性がある

課題のテストケースでは trait を使って以下のようにしていた。

class FooSuite extends FunSuite {
    trait TestData {
        val data1 = new FooData(…)
        val data2 = new FooData(…)
    }

    test("test A") {
        new TestData {
            // tests with data1 and data2
        }
    }

    test("test B") {
        new TestData {
            // another tests with data1 and data2
        }
    }
}

各テスト内で TestData trait を継承した無名クラスを作り、無名クラスのコンストラクタ内でテストを実行する。 こうすることで new FooData(…) の実行はテスト実行時になり、各テストごとにデータが初期化されるので、上にあげた問題が解消する。

Sharing fixtures をみると他の方法もいろいろある。 一番上にある Calling get-fixture methods が一番単純ぽい。 この方法で例を書き換えるとこうなる。

class FooSuite extends FunSuite {
    def fixture = new {
        val data1 = new FooData(…)
        val data2 = new FooData(…)
    }

    test("test A") {
        val f = fixture
        // tests with f.data1 and f.data2
    }

    test("test B") {
        val f = fixture
        import f._
        // another tests with data1 and data2
    }
}

無名クラスのメンバとしてテストデータを作る。 こっちのほうがインデント少なくていいかも。 import f._ すれば f. prefix 要らないしね。 new のあとクラス名無くてもコンパイル通るんだ… 知らなかったよ。

Sbt Build Definition

始める sbt を以前読んだときは .sbt ビルド定義 のところでぐっと難しくなってよくわからなくなった。 今日復習したのでメモ。

最初にまとめ。

  • key := value は新しい設定項目を追加する関数のようなもの(Setting[T])を定義する。
  • Setting[T] の入力は変更されない。
  • .sbt を読み込むと Setting[T] のリストができる。Setting[T] のリストは、依存関係を考慮してソートされた後に適用される。
  • .sbt の空行で区切られた塊は Scala の式。文ではないので val, object, class などは書けない。
  • key := valuekey.:=(value) といったメソッド呼び出しを別の書き方にしたもの。
  • sbtデフォルトの設定項目は sbt.Keys に定義されている。
  • TaskKey[T] は毎回計算されるキー。
  • sbtコンソールでアクセスするときは SettingKey[T], TaskKey[T] などを呼び出す際の第一引き数文字列を使う。
  • キーの詳細はsbtコンソールから inspect で確認できる。

Akka Memo

Scala 2.10 から付いてくる Akka の Actor についてのお勉強メモ。 全然まとまっていないが晒しておく。

ScalaTestでMockito

ScalaTestでMockitoを使うためのお勉強ノート

Setup

build.sbt に追加。

libraryDependencies ++= Seq(
  "org.scalatest" %% "scalatest" % "1.9.1" % "test",
  "org.mockito" % "mockito-core" % "1.9.5" % "test"
)

テストで MockitoSugar を mixin し、以下の import 行を追加。

import org.mockito.Matchers._
import org.mockito.Mockito._

Mock作成

// ClassToMock のモックを作成
val m = mock[ClassToMock]

作ったモックはデフォルトで全メソッドコールに対して null を返す。 メソッドの戻り値としてnullではなくモックを返したい時は次のようにする。

// メソッド呼び出しでモックを返すモック
mock[ClassToMock](RETURNS_MOCKS)
// 返されたモックもモックを返すモック
mock[ClassToMock](RETURNS_DEEP_STUBS)

引数に応じて戻り値を変える

when(メソッド呼び出し).thenReturn(戻り値) で登録する。 メソッド呼び出し部の引き数には any で任意の型の任意の値に、anyString, anyBoolean, anyByte, anyCharなどで基本的な型の任意の値に anyVararg で任意の可変引き数にマッチさせることができる。

// m.method1(何か) が呼ばれたら 10 を返す
when(m.method1(any)).thenReturn(10)

引数が2つ以上あり、いずれかで any などのマッチャーを使った場合には、他の引き数もマッチャーにする必要がある。 オブジェクトが等しいことを示すマッチャーは eq(obj) で作れる。

後から登録したものが先にマッチするので、条件のゆるいもの(マッチ範囲が広いもの)を先に書く。

1回目、2回目で違う値を返す

thenReturn() の可変引き数版を使う

// 最初は 10, 次は 20, それ以降はずっと 30 を返す
when(m.method1()).thenReturn(10, 20, 30)

例外を起こす

// method1() が呼ばれたら例外を起こす
when(m.method1()).thenThrow(new RuntimeException("Gau gau"))

thenReturn と同じように1回目、2回目で違う例外を起こすことも可能。 むしろ thenReturn と組み合わせることができる。

when(m.method1())
  .thenReturn(10, 20) // 1回目は10, 2回目は20を返す
  .thenThrow(new FooException, new BarException) // 3、4回目は例外
  .thenReturn(30) // 5回目以降は30を返す

オブジェクトの一部をモックする(spy)

mock[ClassToMock] はフルのモック。一部だけモックしたい場合はこっち。spyと呼ぶ。

// obj は普通のオブジェクト
val s = spy(obj)
// s.method1(何か) が呼ばれたら obj の実装を使わずに 10 を返す。
doReturn(10).when(s).method1(any[ClassOfArg])

thenReturndoReturn になって順番が代わり、メソッド呼び出しも when の外に出す。 thenReturnの書き方では実際のメソッドが呼ばれるので、呼ばれては困るときにこちらの書き方をする。

この記法の場合は any を使うときに型を指定しなければならなかった (理由はわかってない)。

メソッドが呼び出されたことを確認する(verify)

when(), thenReturn(), doReturn() などは関数呼び出しの前に用意する。 用意したものがテスト中で呼び出されなくても問題ない。

呼び出されたことを確認するには verify() を使う。

// テストを実行
m.method1(123);
// method1() が何らかの整数引き数で呼び出されたことを確認
verify(m).method1(anyInt);

複数回呼び出されたことを確認するときは、verify()の第二引き数で回数指定をする。

// 2回呼び出されたことを確認
verify(m, times(2)).method1(anyInt)
// 少なくとも2回
verify(m, atLeast(2)).method1(anyInt)
// 多くとも3回
verify(m, atMost(3)).method1(anyInt)

呼び出されてないことを確認する

never を使う。

// 一度も呼ばれてないことを確認
verify(m, never).method1(anyInt)

より進んだ使い方

Mockitoのドキュメント に書いてある。

「すごい Haskell たのしく学ぼう!」読書 (1)

「Scala実践プログラミング」を読書したり、Scala 関連の blog を読んでいたりすると、 「HaskellではこうやるのをScalaで実現するには云々」という話が多くて困る。 なぜなら僕はHaskell知らないから。

と開き直ってもしかたがないので Haskell の入門書として評価が高そうな「すごい Haskell たのしく学ぼう!」 を購入した。 Kindle版があるのは素晴らしい。

読書メモを残しておく。今日はイントロを読んだ。

  • Haskell はおもしろい
  • 困ったら freenode #haskell へ (日本語だとどこなんだろう?)
  • 純粋関数型言語で宣言的 (<-> 命令形言語で手続き的)
  • 副作用なし。参照透明性(同じ引数で関数が呼ばれたら同じ値を返す)。
  • 遅延評価
  • 静的型付けで型推論あり。
  • 言語仕様は TheHaskellReport。最新は2010年。
  • GHC: コンパイラと対話プログラム(ghci)
  • ghci のコマンド
    • :q – 終了
    • :l foo – foo.hs のロード
    • :r – 最後の :l を再実行
  • ワークフロー
    1. foo.hs を作る
    2. :l foo
    3. foo.hs を編集
    4. :r (以下繰り返し)

語り口が柔らかで読みやすい。

Octopress で Universal Analytics へ切り替え

この blog では google analytics を入れているが、それを今年の春に公開された universal analytics に切り替えてみた。 なんか色々できることが増えてるみたい。

まず、既存のプロパティをそのまま universal analytics へ移行することは出来ない。 Google Analytics のプロパティを新しく作る必要があった。