『Don’t Be Scared Of Functional Programming』 PHPで関数型プログラミングに触れてみる。

# はじめに
これは[『Don’t Be Scared Of Functional Programming』 JavaScriptで関数型言語に触れてみる。](http://qiita.com/kaznmb/items/1511840cb2aa46ca938b)
を拝見して、分かりやすくて面白そうだったのでPHP版をやってみたものです。

kaznmbさんの記事同様、[Don't Be Scared of Functional Programming](http://www.smashingmagazine.com/2014/07/02/dont-be-scared-of-functional-programming/)を参考にしておりますので、
そちらも併せてご覧下さい。（リンク先で使用されているのはJavaScriptです）

コードなどもお二人の記事を参考にしつつ、PHP用に書き換えていく手法でやってみます。

## 関数型プログラミングの基本コンセプト

[以前の記事](http://qiita.com/piotzkhider/items/aa0c7ce0c21f01a8f8cd)に記述しましたので、詳細は割愛しますが、

* 副作用がないこと。
* 参照透過性を大切にすること。

kaznmbさんと[Don't Be Scared of Functional Programming](http://www.smashingmagazine.com/2014/07/02/dont-be-scared-of-functional-programming/)では、

* Immutable
* Stateless

として表現されていますが、ほぼ同じ意味だと思っています。

## 関数型プログラミングらしい実装のために

kaznmbさんの記事に

>慣れていないと、どうしても関数型プログラミングっぽくない実装をしてしまいます。
よりベストプラクティスに沿うように、以下のルールを守りましょう。

とあるように、私も同じルールを引き継ぎ、実装してみようと思います。

* 【ルール１】全ての関数は、最低でも１つの引き数を取ります。
* 【ルール２】全ての関数が、何かしらの値、もしくは関数を返します。
* 【ルール３】ループは使用禁止。

# 実際にやってみよう

こんな値を返すAPIがあったとします。

```php:
$data = array(
    array(
        'name'         => 'Jamestown',
        'population'   => '2047',
        'temperatures' => array(-34, 67, 101, 87),
    ),
    array(
        'name'         => 'Awesome Town',
        'population'   => '3568',
        'temperatures' => array(-3, 4, 9, 12),
    ),
    array(
        'name'         => 'Funky Town',
        'population'   => '1000000',
        'temperatures' => array(75, 75, 75, 75),
    ),
);
```

人口（population）と平均気温（temperature）をグラフに可視化したいとします。
グラフを描画するために、まずは上記のデータを以下の様な状態にしたいです。
xが平均気温、yが人口を表しています。

```php:
array(
    array(x, y),
    array(x, y),
    ...
);
```

関数型プログラミングを意識せず実装してみると以下のような感じ。

```php:
$result = array();
foreach ($data as $detail) {
    $result[] = array(
        array_sum($detail['temperatures']) / count($detail['temperatures']),
        $detail['population'],
    );
}
```

ほら、従来だとこんなに読みにく…あれ？読みやすい！？

これだけ短くて分かりやすければこれで良いんじゃ…？
しかし勉強のため、関数型プログラミングの手法を試してみます。

### 必要な論理を考える


```php:
// $data から key 'population' の value を取得して配列にする
$populations     =
// $data から key 'temperatures' の value を取得して配列にする
$allTemperatures =
// $allTemperatures からそれぞれの平均を算出する
$averageTemps    =
// $populations, $averageTemps を　array　にまとめる
$combineArrays   = 
```

ではまず上からコードにしてみます。

### 1. 配列から指定したキーの値を取得して配列にする

* `$data` から `key` `'population'` の `value` を取得して配列にする
* `$data` から `key` `'temperatures'` の `value` を取得して配列にする

この2つは、

* 配列`array`から指定したキー`specified`の値`value`を取得して配列にする

機能と言えますので、まずは

* 配列 から 指定したキー の 値 を取得する

という部分を一つの関数として実装します。

```php:配列から指定したキーの値を返す
function getSpecifiedKey($specified)
{
    return function ($value) use ($specified) {
        return $value[$specified];
    }
}
```

私はぱっと見`$value`がなぜ突然出てくるのか分からなくて混乱しましたが、
関数そのものを展開せず返しているだけと思えば分かりやすかったです。

（同様の質問とその回答[本家記事のコメント](http://www.smashingmagazine.com/2014/07/02/dont-be-scared-of-functional-programming/#comment-1018146)にもありました）

次に「配列にする」ところまでを関数化します。

```php:配列の指定したキーの値を返して「配列にする」
function pluck($array, $specified)
{
    return array_map(getSpecifiedKey($specified), $array);
}
```

これらをまとめると以下の様になります。

```php:配列から指定したキーの値を取得して配列にする
function pluck($array, $specified)
{
    return array_map(getSpecifiedKey($specified), $array);
}

function getSpecifiedKey($specified)
{
    return function ($value) use ($specified) {
        return $value[$specified];
    }
}

...

$populations     = pluck($data, 'population');
$allTemperatures = pluck($data, 'temperatures');
```

### 2. 配列の各値（配列）それぞれの平均を算出する

* `$allTemperatures` からそれぞれの平均を算出する

現在`$allTemperatures`は

```php:
// allTemperatures
array(array(-34, 67, 101, 87), array(-3, 4, 9, 12), array(75, 75, 75, 75));
```

上記のような二次元配列構造になっているので、
各値（配列）に使用する、平均を算出する関数を作成します。

平均を算出するために必要なものは、 値の平均＝「値の合計」÷「値の個数」 なので、
それぞれを求める処理を関数化します。

まずは「値の合計」を算出する関数を作成します。
とりあえず「ループを使わない」約束に則り、再帰で関数化します。

```php:
function sumForArray($array, $carry = 0)
{
    $carry += $array[0];
    $item = array_slice($array, 1);
    if (count($item) == 0) {
        return $carry;
    }
    return sumForArray($item, $carry);
}
```

良い感じ。

しかし知っての通り、PHPには`array_reduce`という便利関数が存在しているので、
そちらを使用して実装しなおします。

```php:値の合計
function sumForArray($array)
{
    return array_reduce($array, function ($carry, $item) {
        return $carry + $item;
    }, 0);
}
```

`array_sum`あるよねと思われた方も多いと思いますが、勉強のためとお思い下さい。

`$carry + $item`の処理も汎用処理ですので、関数化しちゃいます。

```php:a足すb
function plus($a, $b)
{
    return $a + $b;
}
```

「値の個数」に関しては、PHPには`count`がありますので、そちらを使います。

計算できる材料が揃いましたので、まずは平均を算出する関数を作成します。

```php:平均
function average($total, $count)
{
    return $total / $count;
}
```

これだけでは配列の平均を出すには少し役不足のため、`array_average`関数を作ります。

```php:配列の平均
function array_average($array)
{
    return average(sumForArray($array), count($array));
}
```

これらをまとめると以下の様になります。

```php:配列の各値（配列）それぞれの平均を算出する
function array_average($array)
{
    return average(sumForArray($array), count($array));
}

function average($total, $count)
{
    return $total / $count;
}

function sumForArray($array)
{
    return array_reduce($array, function ($carry, $item) {
        return plus($carry, $item);
    }, 0);
}

function plus($a, $b)
{
    return $a + $b;
}

...

$averageTemps = array_map(function ($value) {
    return array_average($value)
}, $allTemperatures);
```

### 3. 2つの配列を1つにまとめる

* `$populations`, `$averageTemps` を`array`にまとめる

現在`$populations`と`$averageTemps`は

```php:
// populations
array(2047, 3568, 1000000);

// averageTemps
array(55.25, 5.5, 75);
```

上のような形になっているので、

```php:
array(
    array(55.25, 2047),
    array(5.5, 3568),
    array(75, 1000000),
);
```

こんな形に整形する関数を実装します。

これもルールに従い、ループを使わず再起で実装します。

```php:
function combineArrays($array1, $array2, $result = array())
{
    $result[] = array($array1[0], $array2[0]);

    $remainingArray1 = array_slice($array1, 1);
    $remainingArray2 = array_slice($array2, 1);

    if (count($remainingArray1) > 0 && count($remainingArray2) > 0) {
        return combineArrays($remainingArray1, $remainingArray2, $result);
    }
    return $result;
}

...

$processed = combineArrays($populations, $averageTemps);
```

### まとめる

`sumForArray`はもう`array_sum`に書き換えてしまいます。
そうなると`plus`関数も必要なくなるので、その辺りも調整して、できあがりです。

```php:調整後のまとめ
/**
 * 2つの配列を1つにまとめる
 */
function combineArrays($array1, $array2, $result = array())
{
    $result[] = array($array1[0], $array2[0]);

    $remainingArray1 = array_slice($array1, 1);
    $remainingArray2 = array_slice($array2, 1);

    if (count($remainingArray1) > 0 && count($remainingArray2) > 0) {
        return combineArrays($remainingArray1, $remainingArray2, $result);
    }
    return $result;
}

/**
 * 配列の平均を算出する
 */
function array_average($array)
{
    return average(array_sum($array), count($array));
}

/**
 * 平均を算出する
 */
function average($total, $count)
{
    return $total / $count;
}

/**
 * 配列の指定したキーの値を返して配列にする
 */
function pluck($array, $specified)
{
    return array_map(getSpecifiedKey($specified), $array);
}

/**
 * 指定したキーの値を返す
 */
function getSpecifiedKey($specified)
{
    return function ($value) use ($specified) {
        return $value[$specified];
    };
}

$populations     = pluck($data, 'population');

$allTemperatures = pluck($data, 'temperatures');
$averageTemps    = array_map(function ($value) {
    return array_average($value);
}, $allTemperatures);

$processed       = combineArrays($averageTemps, $populations);
```

# やってみて

思ったより処理が長くなってしまった印象。最初の`foreach`と比べると…うーん。
可読性だけで言えば、常に関数型プログラミングが高いかと言われるとそうでもない感じ。
慣れもあるかな？
でも処理が多くなればなるほど、関数型の方が可読性が上がりそう。