phpのarray_mapをphpで実装してみる
こんにちは(๑╹ω╹๑ )
今日はPHPのarray_mapをPHPで実装してみます!
array_mapの動作仕様は公式リファレンスの通りです!
C言語上でのPHPのarray_map定義を拾って厳密に実装したかったのですが今回は割愛します笑
低レイヤーライクなarray_mapを実装してみる
少しずつ本来の仕様に近づいていく形で実装していきます。
callback関数で許容する値はvalueのみ
基本的には、
- 高階関数
- 可変関数
- 参照渡し
- 再帰処理
- 内部ポインタ操作
で実装することになるかと思います。
<?php
function getNextValue(&$array){
$value = current($array);
next($array);
return $value;
}
function isEndValue($array, $value){
end($array);
return current($array) === $value;
}
function __array_map($callback, $array) {
$result = [];
$value = getNextValue($array);
$result[] = $callback($value);
if (!isEndValue($array, $value)) {
$result = array_merge($result, __array_map($callback, $array));
}
return $result;
}
$array = [
"key1" => "value1",
"key2" => "value2",
"key3" => "value3",
];
$callback = function($value){
return $value . " Hello!";
};
$result = __array_map($callback, $array);
print_r($result);
/*
[
"value1 Hello!",
"value2 Hello!",
"value3 Hello!",
]
*/
低レイヤーライクな実装をするとまずこのようになっていくかと思います。
ですがこの実装だとCallableな関数からの返却値には元々のkeyを維持していません。
元々のkeyを維持する実装に修正してみる
<?php
function getNextValue(&$array){
$value = current($array);
$cp_array = $array;
$key = array_flip($cp_array)[$value];
next($array);
return ["key" => $key, "value" => $value];
}
function isEndValue($array, $value){
end($array);
return current($array) === $value;
}
function __array_map($callback, $array) {
$result = [];
$next = getNextValue($array);
$key = $next["key"];
$value = $next["value"];
$result[$key] = $callback($value);
if (!isEndValue($array, $value)) {
$result = array_merge($result, __array_map($callback, $array));
}
return $result;
}
$array = [
"key1" => "value1",
"key2" => "value2",
"key3" => "value3",
];
$callback = function($value){
return $value . " Hello!";
};
$result = __array_map($callback, $array);
print_r($result);
/*
[
"key1" => "value1 Hello!",
"key2" => "value2 Hello!",
"key3" => "value3 Hello!",
]
*/
基本的には参照渡し後にcurrentポインタからvalueを取得するタイミングでkeyを抜き取れば問題ないかと思います。
function getNextValue(&$array){
$value = current($array);
$cp_array = $array;
$key = array_flip($cp_array)[$value];
next($array);
return ["key" => $key, "value" => $value];
}
注意する点は $array
を参照渡ししているので、
array_flip
でkey,valueを反転させる時は
- ディープコピー後の
$array
をarray_flip
- 二度
array_flip
のどちらかになると思います。
そして配列のサイズはもちろん可変なので成るべく全走査を避けたいところです。
という意図でディープコピーをしています。
ですが、このままだとcallback関数でkeyの走査が出来ません。
それでは本来の array_map
の仕様に近づけていきましょう。
callback関数でkeyを走査できるように修正を加えてみる
この辺りまでスムーズにアプローチが定まればあと少しです!
<?php
function getNextValue(&$array){
$value = current($array);
$cp_array = $array;
$key = array_flip($cp_array)[$value];
next($array);
return ["key" => $key, "value" => $value];
}
function isEndValue($array, $value){
end($array);
return current($array) === $value;
}
function __array_map($callback, $array) {
$result = [];
$next = getNextValue($array);
$key = $next["key"];
$value = $next["value"];
$result[$key] = $callback($value, $key);
if (!isEndValue($array, $value)) {
$result = array_merge($result, __array_map($callback, $array));
}
return $result;
}
$array = [
"key1" => "value1",
"key2" => "value2",
"key3" => "value3",
];
$callback = function($value, $key){
return $value . " " . $key;
};
$result = __array_map($callback, $array);
print_r($result);
/*
[
"key1" => "value1 key1",
"key2" => "value2 key2",
"key3" => "value3 key3",
]
*/
PHPでは、
call先に明示されている引数の個数 < call元に明示されている引数の個数
上記のような事象下では Warning
扱いとはならないので、
$result[$key] = $callback($value, $key);
このように $key
をinjectionさせてあげれば単純に実装出来ます。
$callback = function($value, $key){
return $value . " " . $key;
};
ですので、callback関数側でこのような実装となっていても、
$callback = function($value){
return $value . " Hello!";
};
のような実装となっていても想定通りに動作します。
そして array_map
の第一引数に割り当てるCallableな関数では、
走査後のvalueを返却するため理に叶った実装になっているかと思います。
またcallback関数をcallする方法として可変関数を用いましたが、
call_user_func_array
を用いても同じような実装を出来るかと思います。
まとめ
Web領域よりは低レイヤー領域の方が大好きなので、
どうしても何気ない実装から低レイヤーを思い返して脳トレしたくなります笑
2021年もよろしくお願いします!
Discussion