簡単なforeach処理をarray_mapとアロー関数で書き換えて高速化してみる

この記事は、PHP8以降のユーザー向けです。
なぜなら、PHP7以前の場合は、array_mapよりもforeachのほうがパフォーマンスで有利だからです。

PHP8のJITコンパイラの実装により、array_mapのcallableが最適化されるようになりました。そのため、PHP8以降では、適宜array_mapを利用したほうが高速なようです。
特にアロー関数を使った場合は、通常のcallableを呼び出すよりも更に高速です。

そこで、本稿では今までforeachで実装されていた処理をarray_mapとアロー関数を用いて高速化する手法を紹介したいと思います。

※個人的見解が含まれていますので、間違いがあればご指摘ください。
※また、プログラミング初学者の方で、「こういう場合はどう書き直せば良い？」というご質問がありましたら、コメント欄にお書きください。可能な限りご返信します。

# 1.簡単なforeachを書き換え
まずは以下のようなforeach文を書き換えてみましょう。

```php
<?php
    $ary = range(0,5);
    foreach($ary as $val){
        $rtn[] = $val**2;
    }
    var_dump($rtn);
```

→結果。
```
array(6) {
  [0]=>
  int(0)
  [1]=>
  int(1)
  [2]=>
  int(4)
  [3]=>
  int(9)
  [4]=>
  int(16)
  [5]=>
  int(25)
}
```

これを、array_mapで書き換えます。
```php
<?php
    $ary = range(0,5);
    $rtn = array_map(fn($val) => $val ** 2, $ary);
    var_dump($rtn);
```
上と同じ結果が出ると思います。

# 2.内部でifを使うforeachの書き換え
アロー関数はifをサポートしていません。式でなくなってしまうからですね。
でも、条件分岐を式として扱う方法があります。三項演算子です。
```php
<?php
    $ary = range(0,5);
    foreach($ary as $val){
        if($val % 2 === 0){
            $rtn[] = 'even';
        }else{
            $rtn[] = 'odd';
        }
    }
    var_dump($rtn);
```
結果
```
array(6) {
  [0]=>
  string(4) "even"
  [1]=>
  string(3) "odd"
  [2]=>
  string(4) "even"
  [3]=>
  string(3) "odd"
  [4]=>
  string(4) "even"
  [5]=>
  string(3) "odd"
}
```

array_mapで書き換え
```php
<?php
    $ary = range(0,5);
    $rtn = array_map(fn($val) => $val % 2 === 0 ? 'even' : 'odd', $ary);
    var_dump($rtn);
```

# 3.配列のキーも使いたい場合
foreachでは、配列のキーを使いたい場合も多いと思います。
array_map単体では実現できないので、array_keysを使います。

```php
<?php
    $ary = [
        0 => 'a',
        1 => 'b',
        2 => 'c',
        3 => 'd',
        4 => 'e'
    ];
    foreach($ary as $key => $val){
        $rtn[] = $key.$val;
    }
    var_dump($rtn);
```
結果
```
array(5) {
  [0]=>
  string(2) "0a"
  [1]=>
  string(2) "1b"
  [2]=>
  string(2) "2c"
  [3]=>
  string(2) "3d"
  [4]=>
  string(2) "4e"
}
```
array_mapで書き直す
```php
<?php
    $ary = [
        0 => 'a',
        1 => 'b',
        2 => 'c',
        3 => 'd',
        4 => 'e'
    ];
    $rtn = array_map(fn($key, $val) => $key.$val, array_keys($ary), $ary)
    var_dump($rtn);
```

## ~~式にできないもの~~
~~実は、アロー関数内でechoは使えません。出力バッファに書き込みたい場合はprintを使います。
アロー関数を使わずに、callableの中でechoを書くこともできます。パフォーマンスはどちらも同じだと思います（未検証）。~~
そんなわけはありませんでした。下に追記。

# 4.結果をエコーしたい場合
array_mapのcallableの内部でechoを実行することはできません。
ですので、array_mapの結果をimplodeしてechoしてやります。

```php
<?php
    $ary = range(0,5);
    foreach($ary as $val){
        echo $val**2.' ';
    }
```

→結果。
```
0 2 4 8 10
```

これを、array_mapで書き換えます。
```php
<?php
    $ary = range(0,5);
    echo implde(array_map(fn($val) => $val ** 2.' ', $ary));
```



# おまけ.多重array_map
2次元配列などについて全ての要素に対して同じ処理を施したい場合は、array_mapを入れ子にすることもできます。
```php
<?php
    $ary = [
        ['a','b','c'],
        ['d','e','f'],
        ['g','h','i']
    ];
    $rtn = array_map(
        fn($val) => array_map(
            fn($str) => str_repeat($str,3)
        , $val)
    , $ary);
    var_dump($rtn);
```

結果
```
array(3) {
  [0]=>
  array(3) {
    [0]=>
    string(3) "aaa"
    [1]=>
    string(3) "bbb"
    [2]=>
    string(3) "ccc"
  }
  [1]=>
  array(3) {
    [0]=>
    string(3) "ddd"
    [1]=>
    string(3) "eee"
    [2]=>
    string(3) "fff"
  }
  [2]=>
  array(3) {
    [0]=>
    string(3) "ggg"
    [1]=>
    string(3) "hhh"
    [2]=>
    string(3) "iii"
  }
}
```