Почему я предпочитаю функции массива циклам

Источник: «Why I prefer array functions over loops»
В PHP я предпочитаю использовать функции массивов, а не циклы foreach. И хотя синтаксис не такой красивый, как в JavaScript, считаю, что они работают лучше, чем "обычный" цикл.

Что такое функции массива

В PHP есть довольно много функций для работы с массивами, каждая из которых имеет своё собственное применение. В этой статье расскажу о функциях, которые обходят массив и выполняют функцию для каждого элемента в массиве.

Можно вспомнить такие функции, как array_reduce, array_map, array_filter и т. д. Каждая из этих функций выполняет определённые действия для каждого элемента массива и возвращает новое значение.

Зачем использовать функцию массива

Сообщение о намерениях

Думаю, наиболее важным фактором является то, что с помощью функции массива вы сообщаете о своих намерениях. Вызов функции даёт понять, чего вы пытаетесь добиться. array_filter? Вы фильтруете массив. array_map? Вы изменяете каждый элемент массива, но сохраняете его структуру. array_reduce? Вы изменяете массив в новую структуру.

Это означает, что читая вызов функции, можно понять, что кто-то пытается сделать, и таким образом сократить ментальное пространство, необходимое для понимания функции.

Всё происходит внутри функции

Всё, что делает функция массива, происходит внутри функции. Взгляните на следующий пример. В части цикла переменная $newNumbers не является частью цикла, а находится над ним. Хотя в данном примере это не так уж и важно, я видел, как объявление новой переменной находилось на значительном расстоянии от цикла, из-за рефакторинга и других вещей.


$newNumbers = [];

foreach ($array as $key => $number) {
$newNumbers[$key] = $number + 2;
}

$newNumbers = array_map(
fn (int $number) => $number + 2,
$array
);

Меньше вероятность побочных эффектов

В PHP можно написать два типа замыканий: с помощью синтаксиса function($parameter) use($uses) и fn($parameters). Поскольку во втором случае допускается только 1 оператор, вероятность возникновения побочных эффектов уже гораздо ниже. А в первом примере всё, что не передаётся в качестве функции, нужно добавлять через use(). Это означает, что можно чётко видеть, что использует этот цикл. И use не передаёт значения по ссылке (по умолчанию). Так что, если в цикле изменить переменную, на следующей итерации она обнуляется. И нет никакого шанса случайно переопределить переменную, которая находится вне цикла.

$value = 3;
$new = [];

foreach ($array as $value) {
$new[] = $value +1;
}
// Теперь $value - последний элемент массива;

$value = 3;
array_map(function (int $value) {
return $value + 1;
}, $array);
// $value по-прежнему 3;

Когда не стоит использовать функцию массива

Эти функции не всегда оказываются лучшим решением. Например, если необходимо выполнить фильтрацию и сопоставление, то это можно сделать в одном цикле foreach. Но если использовать array_filter и array_map, то это приведёт к двойному обходу массива. Хотя для большинства приложений это, вероятно, не является большим узким местом, но если у вас миллионы элементов, это может иметь значение. Например, при цикле на 100 тыс. элементов, где вызов фильтра отфильтровывал 10 %, я получил разницу во времени выполнения от 0,002 до 0,003 секунды на моей машине.

А иногда нужно, чтобы функция управляла несколькими переменными и имела побочные эффекты, в этом случае лучше использовать цикл.

Но если нужно отфильтровать массив, изменить элементы на другие или свести его к другой структуре, использование функции массива сделает его более читаемым, удобным в сопровождении и менее подверженным ошибкам, чем цикл.

Дополнительные материалы

Предыдущая Статья

Ускорение компиляции Sass в Vite и Webpack

Следующая Статья

Понимание операции сведения в Коллекциях Laravel