В чем же разница между array_map
, array_walk
и array_filter
. Что я вижу из документации, так это то, что вы можете передать функцию обратного вызова для выполнения действия в поставляемом массиве. Но я, кажется, не обнаружил особых различий между ними.
Выполняют ли они то же самое?
Можно ли их использовать взаимозаменяемо?
Я был бы признателен за вашу помощь с иллюстративным примером, если они совсем разные.
array_map
не имеет побочных эффектов, в то время как array_walk
может; в частности, array_map
никогда не меняет своих аргументов. array_map
не может работать с ключами массива, array_walk
может. array_map
возвращает массив, array_walk
возвращает true
/ false
. Следовательно, если вы не хотите создавать массив в результате прохождения одного массива, вы должны использовать array_walk
. array_map
также может принимать произвольное количество массивов, а array_walk
работает только на одном. array_walk
может получить дополнительный произвольный параметр для перехода к array_walk
вызову. Это в основном не имеет значения с PHP 5.3 (когда были введены анонимные функции ). array_map
/ array_walk
имеет такое же количество элементов, что и аргумент (ы); array_filter
выбирает только подмножество элементов массива в соответствии с функцией фильтрации. Он сохраняет ключи. Пример:
<pre> <?php $origarray1 = array(2.4, 2.6, 3.5); $origarray2 = array(2.4, 2.6, 3.5); print_r(array_map('floor', $origarray1)); // $origarray1 stays the same // changes $origarray2 array_walk($origarray2, function (&$v, $k) { $v = floor($v); }); print_r($origarray2); // this is a more proper use of array_walk array_walk($origarray1, function ($v, $k) { echo "$k => $v", "\n"; }); // array_map accepts several arrays print_r( array_map(function ($a, $b) { return $a * $b; }, $origarray1, $origarray2) ); // select only elements that are > 2.5 print_r( array_filter($origarray1, function ($a) { return $a > 2.5; }) ); ?> </pre>
Результат:
Array ( [0] => 2 [1] => 2 [2] => 3 ) Array ( [0] => 2 [1] => 2 [2] => 3 ) 0 => 2.4 1 => 2.6 2 => 3.5 Array ( [0] => 4.8 [1] => 5.2 [2] => 10.5 ) Array ( [1] => 2.6 [2] => 3.5 )
Идея сопоставления функции с массивом данных исходит из функционального программирования. Вы не должны думать о array_map
как о цикле foreach
который вызывает функцию для каждого элемента массива (даже если это так реализовано). Это следует рассматривать как применение функции к каждому элементу в массиве независимо.
Теоретически такие вещи, как сопоставление функций, могут выполняться параллельно, поскольку функция, применяемая к данным, должна ТОЛЬКО влиять на данные и НЕ глобальное состояние. Это связано с тем, что array_map
может выбрать любой порядок применения функции к элементам (хотя в PHP это не так).
array_walk
с другой стороны, представляет собой совершенно противоположный подход к обработке массивов данных. Вместо того, чтобы обрабатывать каждый элемент отдельно, он использует состояние ( &$userdata
) и может редактировать элемент на месте (подобно циклу foreach). Поскольку каждый раз, когда элемент имеет $funcname
применяемое к нему, он может изменять глобальное состояние программы, и для этого требуется один правильный способ обработки элементов.
Вернувшись в PHP land, array_map
и array_walk
почти идентичны, за исключением того, что array_walk
дает вам больше контроля над итерацией данных и обычно используется для «изменения» данных на месте и возврата нового «измененного» массива.
array_filter
– действительно приложение array_walk
(или array_reduce
), и это более или менее просто предусмотрено для удобства.
Из документации,
bool array_walk (array & $ array, callback $ funcname [, mixed $ userdata]) <-return bool
array_walk принимает массив и функцию F
и изменяет его, заменяя каждый элемент x на F(x)
.
array array_map (callback $ callback, array $ arr1 [, array $ …]) <- возвращаемый массив
array_map делает то же самое, за исключением того, что вместо изменения на месте он вернет новый массив с преобразованными элементами.
array array_filter (массив $ input [, обратный вызов обратного вызова]) <- возвращаемый массив
array_filter с функцией F
вместо преобразования элементов удалит любые элементы, для которых F(x)
не является истинным
Другие ответы демонстрируют разницу между array_walk (модификация на месте) и array_map (возврат измененной копии) достаточно хорошо. Тем не менее, они действительно не упоминают array_reduce, что является иллюстративным способом понять array_map и array_filter.
Функция array_reduce принимает массив, функцию с двумя аргументами и «аккумулятор», например:
array_reduce(array('a', 'b', 'c', 'd'), 'my_function', $accumulator)
Элементы массива объединены с аккумулятором один за раз, используя данную функцию. Результат вышеуказанного вызова аналогичен тому, как это делается:
my_function( my_function( my_function( my_function( $accumulator, 'a'), 'b'), 'c'), 'd')
Если вы предпочитаете думать в терминах циклов, это похоже на выполнение следующего (я фактически использовал это как резерв, когда array_reduce не был доступен):
function array_reduce($array, $function, $accumulator) { foreach ($array as $element) { $accumulator = $function($accumulator, $element); } return $accumulator; }
Эта циклическая версия дает понять, почему я назвал третий аргумент «аккумулятором»: мы можем использовать его для накопления результатов через каждую итерацию.
Итак, что это связано с array_map и array_filter? Оказывается, они оба являются особым видом array_reduce. Мы можем реализовать их следующим образом:
array_map($function, $array) === array_reduce($array, $MAP, array()) array_filter($array, $function) === array_reduce($array, $FILTER, array())
Игнорируйте тот факт, что array_map и array_filter принимают свои аргументы в другом порядке; это просто еще одна причуда PHP. Важным моментом является то, что правая сторона идентична, за исключением функций, которые я назвал $ MAP и $ FILTER. Итак, как они выглядят?
$MAP = function($accumulator, $element) { $accumulator[] = $function($element); return $accumulator; }; $FILTER = function($accumulator, $element) { if ($function($element)) $accumulator[] = $element; return $accumulator; };
Как вы можете видеть, обе функции берут в накопитель $ и возвращают его снова. В этих функциях есть два отличия:
Обратите внимание, что это далеко не бесполезные мелочи; мы можем использовать его для повышения эффективности наших алгоритмов!
Мы часто видим код, похожий на эти два примера:
// Transform the valid inputs array_map('transform', array_filter($inputs, 'valid')) // Get all numeric IDs array_filter(array_map('get_id', $inputs), 'is_numeric')
Использование array_map и array_filter вместо циклов делает эти примеры очень приятными. Однако это может быть очень неэффективно, если $ input велико, так как первый вызов (карта или фильтр) будет пересекать $ input и строить промежуточный массив. Этот промежуточный массив передается прямо во второй вызов, который снова пересечет все это, тогда промежуточный массив должен быть собран в мусор.
Мы можем избавиться от этого промежуточного массива, используя тот факт, что array_map и array_filter являются примерами array_reduce. Объединив их, нам нужно только пройти $ input один раз в каждом примере:
// 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())
ПРИМЕЧАНИЕ. Мои реализации array_map и array_filter выше не будут вести себя точно так же, как PHP, так как мой массив array_map может обрабатывать только один массив за один раз, и мой массив_filter не будет использовать «пустую» в качестве функции $ по умолчанию. Кроме того, ни один из них не сохранит ключи.
Нетрудно заставить их вести себя как PHP, но я чувствовал, что эти сложности затруднят определение основной идеи.
Следующая ревизия направлена на более четкое определение массивов PHP array_filer (), array_map () и array_walk (), все из которых исходят из функционального программирования:
array_filter () отфильтровывает данные, создавая в результате новый массив, содержащий только нужные элементы предыдущего массива, следующим образом:
<?php $array = array(1, "apples",2, "oranges",3, "plums"); $filtered = array_filter( $array, "ctype_alpha"); var_dump($filtered); ?>
живой код здесь
Все числовые значения отфильтровываются из массива $, оставляя $ filter только с фруктами.
array_map () также создает новый массив, но в отличие от array_filter () результирующий массив содержит каждый элемент входного файла $ filter, но с измененными значениями из-за применения обратного вызова к каждому элементу следующим образом:
<?php $nu = array_map( "strtoupper", $filtered); var_dump($nu); ?>
живой код здесь
Код в этом случае применяет обратный вызов с использованием встроенного strtoupper (), но определенная пользователем функция также является другой жизнеспособной опцией. Обратный вызов применяется к каждому элементу $ filter и тем самым порождает $ nu, чьи элементы содержат заглавные значения.
В следующем фрагменте массив walk () перемещает $ nu и вносит изменения в каждый элемент по отношению к ссылочному оператору '&'. Изменения происходят без создания дополнительного массива. Значение каждого элемента меняется на более информативную строку с указанием ее ключа, категории и значения.
<?php $f = function(&$item,$key,$prefix) { $item = "$key: $prefix: $item"; }; array_walk($nu, $f,"fruit"); var_dump($nu); ?>
См. Демонстрацию
Примечание. Функция обратного вызова по отношению к array_walk () принимает два параметра, которые автоматически получат значение элемента и его ключ и в этом порядке тоже при вызове array_walk (). (См. Больше здесь ).