Las otras respuestas demuestran la diferencia entre array_walk (modificación en el lugar) y array_map (devolución de copia modificada) bastante bien. Sin embargo, en realidad no mencionan array_reduce, que es una forma ilustrativa de comprender array_map y array_filter.
La función array_reduce toma una matriz, una función de dos argumentos y un 'acumulador', así:
array_reduce(array('a', 'b', 'c', 'd'),
'my_function',
$accumulator)
Los elementos de la matriz se combinan con el acumulador de uno en uno, utilizando la función dada. El resultado de la llamada anterior es el mismo que hacer esto:
my_function(
my_function(
my_function(
my_function(
$accumulator,
'a'),
'b'),
'c'),
'd')
Si prefiere pensar en términos de bucles, es como hacer lo siguiente (en realidad, he usado esto como una alternativa cuando array_reduce no estaba disponible):
function array_reduce($array, $function, $accumulator) {
foreach ($array as $element) {
$accumulator = $function($accumulator, $element);
}
return $accumulator;
}
Esta versión en bucle deja en claro por qué he llamado al tercer argumento "acumulador": podemos usarlo para acumular resultados a través de cada iteración.
Entonces, ¿qué tiene esto que ver con array_map y array_filter? Resulta que ambos son un tipo particular de array_reduce. Podemos implementarlos así:
array_map($function, $array) === array_reduce($array, $MAP, array())
array_filter($array, $function) === array_reduce($array, $FILTER, array())
Ignore el hecho de que array_map y array_filter toman sus argumentos en un orden diferente; Esa es solo otra peculiaridad de PHP. El punto importante es que el lado derecho es idéntico, excepto por las funciones que he llamado $ MAP y $ FILTER. Entonces, ¿cómo se ven?
$MAP = function($accumulator, $element) {
$accumulator[] = $function($element);
return $accumulator;
};
$FILTER = function($accumulator, $element) {
if ($function($element)) $accumulator[] = $element;
return $accumulator;
};
Como puede ver, ambas funciones toman el acumulador $ y lo devuelven nuevamente. Hay dos diferencias en estas funciones:
- $ MAP siempre se agregará a $ acumulador, pero $ FILTER solo lo hará si $ function ($ element) es TRUE.
- $ FILTER agrega el elemento original, pero $ MAP agrega $ function ($ element).
Tenga en cuenta que esto está lejos de ser una trivia inútil; ¡podemos usarlo para hacer que nuestros algoritmos sean más eficientes!
A menudo podemos ver códigos como estos dos ejemplos:
// Transform the valid inputs
array_map('transform', array_filter($inputs, 'valid'))
// Get all numeric IDs
array_filter(array_map('get_id', $inputs), 'is_numeric')
El uso de array_map y array_filter en lugar de bucles hace que estos ejemplos se vean bastante bien. Sin embargo, puede ser muy ineficiente si $ input es grande, ya que la primera llamada (mapa o filtro) atravesará $ input y creará una matriz intermedia. Esta matriz intermedia se pasa directamente a la segunda llamada, que atravesará todo nuevamente, luego la matriz intermedia tendrá que ser recolectada de basura.
Podemos deshacernos de esta matriz intermedia explotando el hecho de que array_map y array_filter son ejemplos de array_reduce. Al combinarlos, solo tenemos que atravesar $ input una vez en cada ejemplo:
// 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: Mis implementaciones de array_map y array_filter anteriores no se comportarán exactamente como PHP, ya que mi array_map solo puede manejar una matriz a la vez y mi array_filter no usará "vacío" como su función $ predeterminada. Además, ninguno conservará las claves.
No es difícil hacer que se comporten como PHP, pero sentí que estas complicaciones harían que la idea central fuera más difícil de detectar.