2009年11月4日水曜日

[Scala] Programming in Scala, Listing 9.1 を噛み砕いてみる

Programming in Scala P165, Listing 9.1 がいまいちわからなかったので、コードの変化を1歩ずつ追ってみました。

1. (手始めの)高階関数でないバージョン。

object FileMatcher {
private def filesHere = (new java.io.File(".")).listFiles

def filesEnding(query: String) =
for(file <- filesHere; if file.getName.endsWith(query))
yield file
def filesContaining(query: String) =
for(file <- filesHere; if file.getName.contains(query))
yield file
def filesRegex(query: String) =
for(file <- filesHere; if file.getName.matches(query))
yield file
}


2. ヘルパメソッド(マッチングの関数をとる高階関数)導入
filesEnding, filesContaining, filesRegex で重複していた for(...) yield ... をヘルパメソッド filesMatching にくくりだします。

object FileMatcherHO {
private def filesHere = (new java.io.File(".")).listFiles

// マッチング処理を行うヘルパメソッド
private def filesMatching(query: String, matcher: (String, String) => Boolean) = {
for(file <- filesHere; if matcher(file.getName, query))
yield file
}

// filesMatching にクエリ文字列とマッチング関数を渡す
def filesEnding(query: String) =
filesMatching(query, (name:String,query:String) => name.endsWith(query))
def filesContaining(query: String) =
filesMatching(query, (name:String,query:String) => name.contains(query))
def filesRegex(query: String) =
filesMatching(query, (name:String,query:String) => name.matches(query))
}


2-a. プレースホルダー導入
filesEnding, etc. の第2引数で与える無名関数は引数の型が省略できます(コンパイラが型推論してくれるので)。

def filesEnding(query: String) =
filesMatching(query, (name,query) => name.endsWith(query))

さらに、プレースホルダーが使えてもっと短くなります。

object FileMatcherHOPlaceHolder {
private def filesHere = (new java.io.File(".")).listFiles
private def filesMatching(query: String, matcher: (String, String) => Boolean) = {
for(file <- filesHere; if matcher(file.getName, query))
yield file
}

// プレースホルダー使用
def filesEnding(query: String) =
filesMatching(query, _.endsWith(_))
def filesContaining(query: String) =
filesMatching(query, _.contains(_))
def filesRegex(query: String) =
filesMatching(query, _.matches(_))
}


2-b. クロージャ導入
filesMatching 第1引数の query は、第2引数のマッチング関数に渡されるだけなので、第2引数をクロージャにすることで省略できます。

object FileMatcherHOClosure {
private def filesHere = (new java.io.File(".")).listFiles

// ヘルパメソッドの定義からqueryが除かれている
// クエリ文字列は、matcherクロージャが(実行時に)文脈からキャプチャする
private def filesMatching(matcher: String => Boolean) = {
for(file <- filesHere; if matcher(file.getName))
yield file
}

// filesMatchingにクロージャを渡す
// name は束縛変数(bound variable), query は自由変数(free variable)
def filesEnding(query: String) =
filesMatching((name:String) => name.endsWith(query))
def filesContaining(query: String) =
filesMatching((name:String) => name.contains(query))
def filesRegex(query: String) =
filesMatching((name:String) => name.matches(query))
}


2-ab. プレースホルダー&クロージャ使用 (Listing 9.1)
2-a. と 2-b. をがっちゃんこすると Listing 9.1 のコードになります。

object FileMatcherHOClosurePlaceHolder {
private def filesHere = (new java.io.File(".")).listFiles
private def filesMatching(matcher: String => Boolean) = {
for(file <- filesHere; if matcher(file.getName))
yield file
}

def filesEnding(query: String) =
filesMatching(_.endsWith(query))
def filesContaining(query: String) =
filesMatching(_.contains(query))
def filesRegex(query: String) =
filesMatching(_.matches(query))
}


# しかしくどいなぁ。クロージャなんとなくわかってきた。

0 件のコメント:

コメントを投稿