従来だとeach()
で配列ポインタをぶん回したり、array_map(null)
を使ったりするのが一般的な方法だった。
要約
-
each()
はPHP8の頃には削除されるよ -
array
がだめならArrayIterator
でいいじゃん
今回の提案
parallel()
ってヘルパー関数を作ってやります。
/**
* @param iterable[] $iters イテレータ/配列の組
* @param int $key_index foreachのkeyになるindex
* @param int|null $stop_index イテレーションを停止する条件のindex (無指定は$key_index)
*/
function parallel(array $iters, $key_index = 0, $stop_index = null)
{
foreach ($iters as $i => $iter) {
if (is_array($iter)) {
$iters[$i] = new ArrayIterator($iter);
}
}
if (count($iters) == 0) {
throw new BadFunctionCallException();
}
$stop_index = $stop_index ?? $key_index;
if (!isset($iters[$key_index])) {
throw new OutOfRangeException();
}
if (!isset($iters[$stop_index])) {
throw new OutOfRangeException();
}
while ($iters[$stop_index]->valid()) {
$key = $iters[$key_index]->key();
$current = [];
foreach ($iters as $iter) {
if ($iter->valid()) {
$current[] = $iter->current();
$iter->next();
} else {
$current[] = null;
}
}
yield $key => $current;
}
}
こうやって動かす。
<?php
$fruits = ['りんご', 'ばなな', 'みかん'];
$prices = [100, 140, 80];
foreach (parallel([$fruits, $prices]) as $key => [$fruit, $price]) {
var_dump([$key =>compact('fruit', 'price')]);
}
// array(1) {
// [0]=>
// array(2) {
// ["fruit"]=>
// string(9) "りんご"
// ["price"]=>
// int(100)
// }
// }
// ...
as $key => [$fruit, $num]
の部分を as $key => $row
にすれば、実際は array_map(null)
と同じようになる。つまりはzip
関数と同じ動作だ。
大事なポイント
-
array_map()
と違って「配列以外も同時にイテレーションできる- ジェネレータなども同時に指定できる ←超重要
-
array_map(null)
と同じように、長さが足りない場合はnull
で埋められる -
foreach
のキーになるイテレータを指定できる (無指定では0
) -
foreach
を停止する条件のイテレータを指定できる (無指定ではキーと同じ)- 迂闊にも無限リストを停止条件にすると永遠に停止しなくなるお
ねむいので続きはそのうち書く。