Jawaban lain menunjukkan perbedaan antara array_walk (modifikasi di tempat) dan array_map (kembalikan salinan yang dimodifikasi) dengan cukup baik. Namun, mereka tidak benar-benar menyebutkan array_reduce, yang merupakan cara menerangi untuk memahami array_map dan array_filter.
Fungsi array_reduce mengambil array, fungsi dua argumen dan 'akumulator', seperti ini:
array_reduce(array('a', 'b', 'c', 'd'),
'my_function',
$accumulator)
Elemen array dikombinasikan dengan akumulator satu per satu, menggunakan fungsi yang diberikan. Hasil dari panggilan di atas sama dengan melakukan ini:
my_function(
my_function(
my_function(
my_function(
$accumulator,
'a'),
'b'),
'c'),
'd')
Jika Anda lebih suka berpikir dalam hal loop, itu seperti melakukan yang berikut (Saya sebenarnya menggunakan ini sebagai fallback ketika array_reduce tidak tersedia):
function array_reduce($array, $function, $accumulator) {
foreach ($array as $element) {
$accumulator = $function($accumulator, $element);
}
return $accumulator;
}
Versi perulangan ini menjelaskan mengapa saya menyebut argumen ketiga sebagai 'akumulator': kita dapat menggunakannya untuk mengumpulkan hasil melalui setiap iterasi.
Jadi apa hubungannya dengan array_map dan array_filter? Ternyata mereka berdua jenis array_reduce tertentu. Kita bisa menerapkannya seperti ini:
array_map($function, $array) === array_reduce($array, $MAP, array())
array_filter($array, $function) === array_reduce($array, $FILTER, array())
Abaikan fakta bahwa array_map dan array_filter mengambil argumen mereka dalam urutan yang berbeda; itu hanyalah kekhasan dari PHP. Poin penting adalah bahwa sisi kanan identik kecuali untuk fungsi yang saya sebut $ MAP dan $ FILTER. Jadi, seperti apa mereka?
$MAP = function($accumulator, $element) {
$accumulator[] = $function($element);
return $accumulator;
};
$FILTER = function($accumulator, $element) {
if ($function($element)) $accumulator[] = $element;
return $accumulator;
};
Seperti yang Anda lihat, kedua fungsi menerima $ akumulator dan mengembalikannya lagi. Ada dua perbedaan dalam fungsi-fungsi ini:
- $ MAP akan selalu ditambahkan ke $ akumulator, tetapi $ FILTER hanya akan melakukannya jika $ function ($ element) BENAR.
- $ FILTER menambahkan elemen asli, tetapi $ MAP menambahkan $ function ($ element).
Perhatikan bahwa ini jauh dari hal-hal sepele yang tidak berguna; kita dapat menggunakannya untuk membuat algoritma kita lebih efisien!
Kita sering dapat melihat kode seperti dua contoh ini:
// Transform the valid inputs
array_map('transform', array_filter($inputs, 'valid'))
// Get all numeric IDs
array_filter(array_map('get_id', $inputs), 'is_numeric')
Menggunakan array_map dan array_filter bukan loop membuat contoh-contoh ini terlihat cukup bagus. Namun, bisa jadi sangat tidak efisien jika $ input besar, karena panggilan pertama (peta atau filter) akan melintasi $ input dan membangun array perantara. Array perantara ini diteruskan langsung ke panggilan kedua, yang akan melintasi semuanya lagi, maka array perantara perlu dikumpulkan dari sampah.
Kita dapat menyingkirkan array perantara ini dengan mengeksploitasi fakta bahwa array_map dan array_filter adalah contoh dari array_reduce. Dengan menggabungkan mereka, kita hanya perlu menelusuri $ input sekali dalam setiap contoh:
// Transform valid inputs
array_reduce($inputs,
function($accumulator, $element) {
if (valid($element)) $accumulator[] = transform($element);
return $accumulator;
},
array())
// Get all numeric IDs
array_reduce($inputs,
function($accumulator, $element) {
$id = get_id($element);
if (is_numeric($id)) $accumulator[] = $id;
return $accumulator;
},
array())
CATATAN: Implementasi saya array_map dan array_filter di atas tidak akan berperilaku persis seperti PHP, karena array_map saya hanya dapat menangani satu array pada suatu waktu dan array_filter saya tidak akan menggunakan "kosong" sebagai fungsi $ default. Juga, tidak ada yang akan menyimpan kunci.
Tidak sulit untuk membuat mereka berperilaku seperti PHP, tetapi saya merasa bahwa komplikasi ini akan membuat ide inti lebih sulit dikenali.