高階関数にゅうもん1（Scalaで）

# 高階関数にゅうもん1（Scalaで）
## 目的
- 高階関数をつかえば、重複するコードを削減できる！
- コップ本の第09章をそのまま。ちょっとだけ問題形式にしてみて何かの時ようにメモを残す。。

## 環境
- scala: 2.11.8

## お題
- ファイル名のリストの中から、何らかの基準に合致するファイルを探したい。

## Exerciseの準備
- ちょっと無理やり感がありますが許してください・・・

```
// ファイルをたくさんつくるので、適当にディレクトリ作ってください！
$ mkdir scala_hof && cd scala_hof  // 英: higher-order function = 高階関数
$ touch {prd,stg}-{web,db}-2017{01,02,03}{01,02,03}.{log,error}
```

## Step1: いくつか重複したコードを書いてみます。
以下のExerciseで作る関数は、このobjectの中に書いてください。

```
object FileMatcher {
  private def filesHere = new File(".").listFiles // カレントディレクトリのファイル一覧がとれる
    
  def echoHoge = println("hoge") // こんな感じで書く
}
```

#### 【Exercise1-1】 カレントディレクトリの中から `.error`で終わるファイル一覧を取得したい。シグネチャは以下のとおり。

```
def filesEnding(query: String): Array[File]
```

- Answer1-1

```
def filesEnding(query: String): Array[File] =
  for (file <- filesHere; if file.getName.endsWith(query))
    yield file
```

#### 【Exercise1-2】 カレントディレクトリの中から `web`を含むファイル一覧を取得したい。シグネチャは以下のとおり。

```
def filesContaining(query: String): Array[File]
```

- Answer1-2

```
def filesContaining(query: String): Array[File] = 
  for (file <- filesHere; if file.getName.contains(query))
    yield file
```

#### 【Exercise1-3】 カレントディレクトリの中から `prd x 201703 x log`的なファイルを正規表現でマッチさせてファイル一覧を取得したい（正規表現は `"""prd.+201703\d{2}.log"""`）。シグネチャは以下のとおり。

```
def filesRegex(query: String): Array[File]
```

- Answer1-3

```
def filesRegex(query: String): Array[File] =
  for (file <- filesHere; if file.getName.matches(query))
    yield file
```

#### 【ここまで】

```
import java.io.File

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

  def filesEnding(query: String): Array[File] =
    for (file <- filesHere; if file.getName.endsWith(query))
      yield file

  def filesContaining(query: String): Array[File] = 
    for (file <- filesHere; if file.getName.contains(query))
      yield file
      
  def filesRegex(query: String): Array[File] =
    for (file <- filesHere; if file.getName.matches(query))
      yield file
}
```

- REPLで確認

```
$ scala
Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_60).
Type in expressions for evaluation. Or try :help.

scala> import java.io.File
import java.io.File

scala> object FileMatcher {
     |   private def filesHere = new File(".").listFiles
     |
     |   def filesEnding(query: String): Array[File] =
     |     for (file <- filesHere; if file.getName.endsWith(query))
     |       yield file
     |
     |   def filesContaining(query: String): Array[File] =
     |     for (file <- filesHere; if file.getName.contains(query))
     |       yield file
     |
     |   def filesRegex(query: String): Array[File] =
     |     for (file <- filesHere; if file.getName.matches(query))
     |       yield file
     | }
defined object FileMatcher

scala> FileMatcher.filesEnding("error")
res0: Array[java.io.File] = Array(./prd-db-20170101.error, ./prd-db-20170102.error, ./prd-db-20170103.error, ./prd-db-20170201.error, ./prd-db-20170202.error, ./prd-db-20170203.error, ./prd-db-20170301.error, ./prd-db-20170302.error, ./prd-db-20170303.error, ./prd-web-20170101.error, ./prd-web-20170102.error, ./prd-web-20170103.error, ./prd-web-20170201.error, ./prd-web-20170202.error, ./prd-web-20170203.error, ./prd-web-20170301.error, ./prd-web-20170302.error, ./prd-web-20170303.error, ./stg-db-20170101.error, ./stg-db-20170102.error, ./stg-db-20170103.error, ./stg-db-20170201.error, ./stg-db-20170202.error, ./stg-db-20170203.error, ./stg-db-20170301.error, ./stg-db-20170302.error, ./stg-db-20170303.error, ./stg-web-20170101.error, ./stg-web-20170102.error, ./stg-web-20170103.error, ....
scala> FileMatcher.filesContaining("web")
res1: Array[java.io.File] = Array(./prd-web-20170101.error, ./prd-web-20170101.log, ./prd-web-20170102.error, ./prd-web-20170102.log, ./prd-web-20170103.error, ./prd-web-20170103.log, ./prd-web-20170201.error, ./prd-web-20170201.log, ./prd-web-20170202.error, ./prd-web-20170202.log, ./prd-web-20170203.error, ./prd-web-20170203.log, ./prd-web-20170301.error, ./prd-web-20170301.log, ./prd-web-20170302.error, ./prd-web-20170302.log, ./prd-web-20170303.error, ./prd-web-20170303.log, ./stg-web-20170101.error, ./stg-web-20170101.log, ./stg-web-20170102.error, ./stg-web-20170102.log, ./stg-web-20170103.error, ./stg-web-20170103.log, ./stg-web-20170201.error, ./stg-web-20170201.log, ./stg-web-20170202.error, ./stg-web-20170202.log, ./stg-web-20170203.error, ./stg-web-20170203.log, ./stg-web-201...
scala> FileMatcher.filesRegex("""prd.+201703\d{2}.log""")
res2: Array[java.io.File] = Array(./prd-db-20170301.log, ./prd-db-20170302.log, ./prd-db-20170303.log, ./prd-web-20170301.log, ./prd-web-20170302.log, ./prd-web-20170303.log)

```

## Step2: 共通ヘルパー関数をつくりたい！
と、ここまで書いてみると、どうも重複していることに気づく（はず）。こんな感じの共通したヘルパー関数をつくりたいな。

```
// 動かないよ！
def filesMatching(query: String, method) =
  for (file <- filesHere; if file.getName.method(query))
    yield file
```
um....値としてメソッド名が渡せないなら、メソッドを呼び出してくれる関数値を渡せばいい！

#### 【Exercise2-1】 `(String, String) => Boolean`型の関数を引数にとるような共通ヘルパー関数をつくりたい。シグネチャは以下のとおり。

```
def filesMatching(query: String, matcher: (String, String) => Boolean)
```

- Answer2-1

```
def filesMatching(query: String, matcher: (String, String) => Boolean) =
  for (file <- filesHere; if matcher(file.getName, query))
    yield file
```
引数に渡した `matcher`は、 `String型の引数を2つとって、Boolean型を返す関数`を表している。

#### 【Exercise2-2】 Exercise2-1で作った `filesMatching`を使って、Exercise1-1でつくった　`filesEnding`を書き直したい。

- Answer2-2

```
def filesEnding(query: String): Array[File] =
  filesMatching(query, (fileName: String, query: String) => fileName.endsWith(query): Boolean)
```
`(fileName: String, query: String) => fileName.endsWith(query): Boolean` の部分は、
`fileNameと、queryの２つを引数にとって、fileName.endsWith(query)の結果を返す関数`ということになる。
ただ、冗長な感じ。そもそも `query`って、 `filesMatching`の中でmatcher関数に渡されてるだけじゃん。。

#### 【Exercise2-3】 `String => Boolean`型のメソッドを引数にとるように共通ヘルパー関数を書き直して、`filesEnding` を書き直したい。シグネチャは以下のとおり。

```
def filesMatching(query: String, matcher: String => Boolean)
```

- Answer2-3

```
def filesMatching(query: String, matcher: String => Boolean) = 
  for (file <- filesHere; if matcher(file.getName))
    yield file

def filesEnding(query: String): Array[File] =
  filesMatching(query, fileName: String => fileName.endsWith(query): Boolean)
```
`filesEnding`は、プレースホルダー構文を使って、型推論をつかえばさらに簡略化できる。

```
def filesEnding(query: String) =
  filesMatching(query, _.endsWith(query))
```
ここまでのを全部使ったコードは以下！
おつかれさまでした。

```
import java.io.File

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

  private def filesMatching(query: String, matcher: String => Boolean) = 
    for (file <- filesHere; if matcher(file.getName))
      yield file
      
  def filesEnding(query: String) =
    filesMatching(query, _.endsWith(query))
    
  def filesContaining(query: String) =
    filesMatching(query, _.contains(query))
      
  def filesRegex(query: String) =
    filesMatching(query, _.matches(query))
}  
```