Pozostałe odpowiedzi dość dobrze pokazują różnicę między array_walk (modyfikacja lokalna) a array_map (zwracana zmodyfikowana kopia). Jednak tak naprawdę nie wspominają o array_reduce, co jest świetnym sposobem na zrozumienie array_map i array_filter.
Funkcja array_reduce pobiera tablicę, funkcję dwóch argumentów i „akumulator”, takie jak to:
array_reduce(array('a', 'b', 'c', 'd'),
'my_function',
$accumulator)
Elementy tablicy są łączone z akumulatorem pojedynczo, przy użyciu podanej funkcji. Wynik powyższego wywołania jest taki sam, jak w przypadku:
my_function(
my_function(
my_function(
my_function(
$accumulator,
'a'),
'b'),
'c'),
'd')
Jeśli wolisz myśleć w kategoriach pętli, to jest jak wykonanie następujących czynności (faktycznie wykorzystałem to jako awarię, gdy funkcja array_reduce nie była dostępna):
function array_reduce($array, $function, $accumulator) {
foreach ($array as $element) {
$accumulator = $function($accumulator, $element);
}
return $accumulator;
}
Ta wersja z zapętleniem wyjaśnia, dlaczego nazwałem trzeci argument „akumulatorem”: możemy go używać do gromadzenia wyników podczas każdej iteracji.
Co to ma wspólnego z array_map i array_filter? Okazuje się, że oba są szczególnym rodzajem zmniejszania tablicy. Możemy zaimplementować je w następujący sposób:
array_map($function, $array) === array_reduce($array, $MAP, array())
array_filter($array, $function) === array_reduce($array, $FILTER, array())
Zignoruj fakt, że array_map i array_filter przyjmują argumenty w innej kolejności; to tylko kolejne dziwactwo PHP. Ważne jest to, że prawa strona jest identyczna, z wyjątkiem funkcji, które nazwałem $ MAP i $ FILTER. Jak więc wyglądają?
$MAP = function($accumulator, $element) {
$accumulator[] = $function($element);
return $accumulator;
};
$FILTER = function($accumulator, $element) {
if ($function($element)) $accumulator[] = $element;
return $accumulator;
};
Jak widać, obie funkcje pobierają akumulator $ i zwracają go ponownie. Istnieją dwie różnice w tych funkcjach:
- $ MAP zawsze dołącza się do $ accumulator, ale $ FILTER zrobi to tylko wtedy, gdy $ function ($ element) ma wartość TRUE.
- $ FILTER dołącza oryginalny element, ale $ MAP dołącza funkcję $ (element $).
Zauważ, że jest to dalekie od bezużytecznych ciekawostek; możemy go użyć do zwiększenia wydajności naszych algorytmów!
Często możemy zobaczyć kod podobny do tych dwóch przykładów:
// Transform the valid inputs
array_map('transform', array_filter($inputs, 'valid'))
// Get all numeric IDs
array_filter(array_map('get_id', $inputs), 'is_numeric')
Użycie array_map i array_filter zamiast pętli sprawia, że te przykłady wyglądają całkiem ładnie. Jednak może być bardzo nieefektywne, jeśli $ input jest duży, ponieważ pierwsze wywołanie (mapa lub filtr) przejdzie $ $ i zbuduje tablicę pośrednią. Ta tablica pośrednia jest przekazywana bezpośrednio do drugiego wywołania, które ponownie przejdzie całą sprawę, a następnie tablica pośrednia będzie musiała zostać wyrzucona.
Możemy pozbyć się tej pośredniej tablicy, wykorzystując fakt, że zarówno array_map, jak i array_filter są przykładami zmniejszenia tablicy. Łącząc je, musimy przejrzeć $ $ tylko raz w każdym przykładzie:
// 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())
UWAGA: Moje implementacje array_map i array_filter powyżej nie będą zachowywać się dokładnie tak, jak PHP, ponieważ moja array_map może obsługiwać tylko jedną tablicę na raz, a mój array_filter nie użyje „pustego” jako domyślnej funkcji $. Ponadto żaden nie zachowa kluczy.
Nie jest trudno sprawić, by zachowywały się jak PHP, ale czułem, że te komplikacje utrudnią dostrzeżenie podstawowej idei.