Le altre risposte dimostrano abbastanza bene la differenza tra array_walk (modifica sul posto) e array_map (ritorno copia modificata). Tuttavia, in realtà non menzionano array_reduce, che è un modo illuminante per comprendere array_map e array_filter.
La funzione array_reduce accetta un array, una funzione a due argomenti e un 'accumulatore', come questo:
array_reduce(array('a', 'b', 'c', 'd'),
'my_function',
$accumulator)
Gli elementi dell'array vengono combinati con l'accumulatore uno alla volta, utilizzando la funzione fornita. Il risultato della chiamata sopra è lo stesso di fare questo:
my_function(
my_function(
my_function(
my_function(
$accumulator,
'a'),
'b'),
'c'),
'd')
Se preferisci pensare in termini di loop, è come fare il seguente (l'ho effettivamente usato come fallback quando array_reduce non era disponibile):
function array_reduce($array, $function, $accumulator) {
foreach ($array as $element) {
$accumulator = $function($accumulator, $element);
}
return $accumulator;
}
Questa versione in loop chiarisce perché ho chiamato il terzo argomento un "accumulatore": possiamo usarlo per accumulare risultati attraverso ogni iterazione.
Cosa c'entra questo con array_map e array_filter? Si scopre che sono entrambi un particolare tipo di array_reduce. Possiamo implementarli in questo modo:
array_map($function, $array) === array_reduce($array, $MAP, array())
array_filter($array, $function) === array_reduce($array, $FILTER, array())
Ignora il fatto che array_map e array_filter accettano i loro argomenti in un ordine diverso; è solo un'altra stranezza di PHP. Il punto importante è che il lato destro è identico ad eccezione delle funzioni che ho chiamato $ MAP e $ FILTER. Quindi, che aspetto hanno?
$MAP = function($accumulator, $element) {
$accumulator[] = $function($element);
return $accumulator;
};
$FILTER = function($accumulator, $element) {
if ($function($element)) $accumulator[] = $element;
return $accumulator;
};
Come puoi vedere, entrambe le funzioni accettano l'accumulatore $ e lo restituiscono di nuovo. Esistono due differenze in queste funzioni:
- $ MAP verrà sempre aggiunto a $ accumulator, ma $ FILTER lo farà solo se $ function ($ element) è TRUE.
- $ FILTER aggiunge l'elemento originale, ma $ MAP aggiunge la funzione $ ($ element).
Nota che questo è tutt'altro che inutile banalità; possiamo usarlo per rendere i nostri algoritmi più efficienti!
Spesso vediamo codice come questi due esempi:
// Transform the valid inputs
array_map('transform', array_filter($inputs, 'valid'))
// Get all numeric IDs
array_filter(array_map('get_id', $inputs), 'is_numeric')
L'uso di array_map e array_filter al posto dei loop rende questi esempi piuttosto belli. Tuttavia, può essere molto inefficiente se $ input è grande, poiché la prima chiamata (mappa o filtro) attraverserà $ input e costruirà un array intermedio. Questo array intermedio viene passato direttamente alla seconda chiamata, che attraverserà di nuovo tutto, quindi l'array intermedio dovrà essere raccolto.
Possiamo sbarazzarci di questo array intermedio sfruttando il fatto che array_map e array_filter sono entrambi esempi di array_reduce. Combinandoli, dobbiamo attraversare $ input solo una volta in ciascun esempio:
// 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())
NOTA: Le mie implementazioni di array_map e array_filter sopra non si comporteranno esattamente come quelle di PHP, poiché la mia array_map può gestire solo un array alla volta e il mio array_filter non utilizzerà "vuoto" come sua funzione $ predefinita. Inoltre, nessuno dei due manterrà le chiavi.
Non è difficile farli comportare come quelli di PHP, ma ho sentito che queste complicazioni avrebbero reso più difficile individuare l'idea di base.