As outras respostas demonstram a diferença entre array_walk (modificação no local) e array_map (retornar cópia modificada) muito bem. No entanto, eles realmente não mencionam array_reduce, que é uma maneira esclarecedora de entender array_map e array_filter.
A função array_reduce pega uma matriz, uma função de dois argumentos e um 'acumulador', assim:
array_reduce(array('a', 'b', 'c', 'd'),
'my_function',
$accumulator)
Os elementos da matriz são combinados com o acumulador, um de cada vez, usando a função fornecida. O resultado da chamada acima é o mesmo que fazer isso:
my_function(
my_function(
my_function(
my_function(
$accumulator,
'a'),
'b'),
'c'),
'd')
Se você preferir pensar em termos de loops, é como fazer o seguinte (na verdade, usei isso como um fallback quando array_reduce não estava disponível):
function array_reduce($array, $function, $accumulator) {
foreach ($array as $element) {
$accumulator = $function($accumulator, $element);
}
return $accumulator;
}
Esta versão em loop deixa claro por que chamei o terceiro argumento de 'acumulador': podemos usá-lo para acumular resultados a cada iteração.
Então, o que isso tem a ver com array_map e array_filter? Acontece que ambos são um tipo específico de array_reduce. Podemos implementá-los assim:
array_map($function, $array) === array_reduce($array, $MAP, array())
array_filter($array, $function) === array_reduce($array, $FILTER, array())
Ignore o fato de que array_map e array_filter recebem seus argumentos em uma ordem diferente; isso é apenas outra peculiaridade do PHP. O ponto importante é que o lado direito é idêntico, exceto pelas funções que chamei de $ MAP e $ FILTER. Então, como eles são?
$MAP = function($accumulator, $element) {
$accumulator[] = $function($element);
return $accumulator;
};
$FILTER = function($accumulator, $element) {
if ($function($element)) $accumulator[] = $element;
return $accumulator;
};
Como você pode ver, ambas as funções pegam o acumulador $ e o devolvem novamente. Existem duas diferenças nessas funções:
- O $ MAP sempre será anexado ao acumulador $, mas o $ FILTER somente o fará se a função $ (elemento $) for VERDADEIRA.
- $ FILTER anexa o elemento original, mas $ MAP acrescenta a função $ (elemento $).
Observe que isso está longe de ser trivial; podemos usá-lo para tornar nossos algoritmos mais eficientes!
Muitas vezes, podemos ver código como esses dois exemplos:
// Transform the valid inputs
array_map('transform', array_filter($inputs, 'valid'))
// Get all numeric IDs
array_filter(array_map('get_id', $inputs), 'is_numeric')
Usar array_map e array_filter em vez de loops faz com que esses exemplos pareçam bastante agradáveis. No entanto, pode ser muito ineficiente se $ inputs for grande, pois a primeira chamada (mapa ou filtro) percorrerá $ inputs e criará uma matriz intermediária. Essa matriz intermediária é passada diretamente para a segunda chamada, que percorrerá a coisa toda novamente, e a matriz intermediária precisará ser coletada como lixo.
Podemos nos livrar dessa matriz intermediária explorando o fato de que array_map e array_filter são exemplos de array_reduce. Ao combiná-los, precisamos apenas percorrer $ inputs uma vez em cada exemplo:
// 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: Minhas implementações de array_map e array_filter acima não se comportarão exatamente como as do PHP, pois meu array_map pode lidar apenas com uma matriz de cada vez e meu array_filter não usará "vazio" como sua função $ padrão. Além disso, nenhum dos dois preservará as chaves.
Não é difícil fazê-los se comportar como os do PHP, mas senti que essas complicações dificultariam a identificação da ideia principal.