Хватит писать foreach()
По крайней мере в php :) Обращаюсь к php-разработчикам – подумайте, сколько раз за рабочий день вы пишите for() и foreach()? Я, например, довольно много :) Сейчас я пишу на php 5.3, а в нем есть очень удобные средства для того, чтобы обойтись без обхода массива в цикле – это функции для работы с массивами, которые принимают callback в качестве аргумента и замыкания
Я говорю о array_map(), array_reduce(), array_filter() и uasort() (ну и похожих на них). Использование этих функций – неплохой способ обойтись без обхода массивов в цикле.
В PHP4 и PHP5.2 использование коллбэков выглядело весьма и весьма громоздко. Вы должны были определить функцию (или метод), а затем передать ее имя (или массив, содержащий имя класса и имя публичного или статического метода) в качестве аргумента. Можно было извернуться через create_function, но все равно это выглядело громоздко.
В PHP5.3 вы можете определить анонимную функцию где-нибудь в коде (или даже непосредственно в вызове функции). Эти функции выглядят как обычные переменные и вы спокойно можете передавать их как аргумент.
Я, конечно, не поклонник смешивания функционального и объектно-ориентированного подхода (тем более, что в PHP оба имеют проблемы с реализацией :)), но подобный прием поможет избавиться в коде от шума, который создают циклы, предназначенный для обхода массивов.
// обычный массив простых чисел до 20
$primeNumbers = array(2, 3, 5, 7, 11, 13, 17, 19);
// array_map() применяет callback-функцию к каждому элементу массива и
// возвращает результат
$square = function($number) {
return $number * $number;
};
$squared = array_map($square, $primeNumbers);
echo "Квадраты простых чисел: ",
implode(', ', $squared), "\n";
// array_reduce() рекурсивно применяет фнукцию к парам значений
// и превращает массив в одно единственное значение
// Очень похоже на array_sum(),
// но использование callback открывает неплохие перспективы
$sum = function($a, $b) {
return $a + $b;
};
$total = array_reduce($primeNumbers, $sum);
echo "Сумма простых чисел: ", $total, ".\n";
// array_filter() оставляет в массиве значения,
// которые удовлетворяют условию
$even = function($number) {
return $number % 2 == 0;
};
$evenPrimes = array_filter($primeNumbers, $even);
echo "Четные простые числа: ",
implode(', ', $evenPrimes), ".\n";
// uasort() сортирует массив и
// сохраняет связь междя ключом и значением
// похожа на asort(), но использует callback
$compare = function($a, $b) {
if ($a == $b) {
return 0;
}
return ($a > $b) ? -1 : 1;
};
uasort($primeNumbers, $compare);
echo "Упорядоченный по убыванию массив простых чисел: ",
implode(', ', $primeNumbers), ".\n";
UPDATE:
Обещанные замеры производительности. Тест, конечно, весьма синтетический, но и он весьма нагляден. Я, честно говоря, был слегка удивлен.
Код теста:
echo "We're using the PHP " . phpversion() . "\n";
$array = $array2 = range(0, 10000, 1);
echo "Array contains " . count($array) . " elements\n";
$t1 = microtime(1);
echo "BEGIN array_map\n";
array_map(
function($number){
return $number * $number;
},
$array
);
$t2 = microtime(1);
$res = $t2 - $t1;
echo "END array_map: " . round($res, 4) . "\n\n";
$t1 = microtime(1);
echo "BEGIN foreach:\n";
foreach ($array2 as $key => $value){
$array2[$key] = $value * $value;
}
$t2 = microtime(1);
$res = $t2 - $t1;
echo "END foreach: " . round($res, 4) . "\n\n";
И вот результаты:
[boombick@srv01 /tmp]$ php -f test.php We're using the PHP 5.3.1 Array contains 10001 elements BEGIN array_map END array_map: 0.0109 BEGIN foreach: END foreach: 0.0052 [boombick@srv01 /tmp]$
Как видно из результата, array_map действительно проигрывает по скорости foreach
19.02.10 | Programming | Comments (19)
array_map и иже с ним показывает меньшую производительность, чем простой foreach, поэтому я их не использую. Но от foreach тоже корёжит.
Хм.. Произведу замеры :)
$square = function($number) {
return $number * $number;
}; // <----
Теперь точка запятой обязательное требование в PHP? Или это какой-то стиль кодирования?
ура, нормальные практики приходят в php. Именно за это я и люблю js, что там так можно делать легко, и это лежит в самой сути кода.
А как реализованы анонимные функции в php? Есть контексты исполнения вроде тех же, что в js?
http://ru.php.net/manual/en/functions.anonymous.php – use official docs, Luke )))
2adw0rd
А что не так? По-моему, точка с запятой всегда ставилась после объявления переменной. Более того, ее отсутствие – это синтаксическая ошибка.
Я не считаю, что хорошо разбираюсь в пхп, но всё же напишу своё скромное мнение.
Меня всегда интересовал вопрос, зачем использовать более сложные методы, где сама логика подсказывает другое решение.
Если у вас обычный массив с числовым индексом, то решения лучше, чем
$n = sizeof($arr);
for ($i=0;$i<$n;$i++){}
не найти.
В то время как foreach необходим для обхода хэш мапы, foreach представляет тот же самый проход итератором по hashmap’e как и в java, c# и прочих языках, где внутренняя структура данных не спрятана от программиста.
Использование данных возможностей по назначению, принесёт 1). большой прирост производительности, 2). логику понимания происходящего.
Плюс и проблема пхп в том, что часто программист не задумывается, как это устроено внутри, из-за чего появляется желание использовать модные фишки из других концепций программирования. Хотя зачастую они не ложатся в концепцию данного языка.
qnikst, никогда не задумывался, Ваши слова задели и поучили! весьма признателен за Ваши пояснения =) В догунку Ваших слов:
Array contains 100001 elements
time to for: 0.2365
time to array_map: 0.4623
time to foreach: 0.3393
Ну, вообще-то при серьезных объемах данных и прочем просто foreach использовать нельзя, ибо памяти не хватит (если делать операции над итератором, который просто по одному вытягивает данные), а во-вторых ведь способ с map можно распараллелить на много (map-reduce), тогда он будет быстрее.
и да, перелазьте на питон пока не поздно, честное слово получите удовольствие начав писать на красивом и правильном языке
$square = function($number) {
return $number * $number;
};
$squared = array_map($square, $primeNumbers);
превратится в
squared = map(lambda x: x*x, prime_numbers)
(еще вариант):
squared = [x*x for x in prime_numbers]
>Я, честно говоря, был слегка удивлен.
А чему удивляться? Или для каждого элемента вызывать функцию (включая все накладные расходы по работе со стеком, параметрами и прочим), или передать ссылку на значение? Конечно первое будет дольше…
2kost BebiX:
Да и на пайтоне тоже пишем :) Красивее и приятнее – несомненно! Но и пхп имеет свою нишу, да и просто захотелось показать некоторые возможности новой версии PHP.
2qnikst:
Спасибо за отличный комментарий!
Шутка про четные простые числа – зачетная.
mrl, вот блин :))) Только что осознал. что действительно странновато :))))
А чем 2 не простое число?
Многие постоянно твердят про огромный расход памяти для foreach при простых проходах, низкую скорость копирования массива для прохода, невозможность менять элементы массива в проходе, блаблабла.
И почему-то все забывают про одну замечательную конструкцию:
foreach ($bigbigbig_array as $key => &$value)
{
# your code here (you can even modify $value and it’ll get into original array)
} unset($value);
unset нужен для того, чтобы после при $value = ‘mydata’; не получить случайное изменение последнего элемента массива по ссылке в $value
Полностью согласен с Alex-ом.
А что касается замены foreach – так извините, даже мануал с дефолтными примерами на массивы на php.net – этим оператором решается!
kost BebiX,
мнея Perl устраивает
@array = 0..1000; @squared = map{$_*$_} @array; print join” “,@array;
@array = 0..1000; push @squared, $_*$_ for @array; print join” “,@array;
@array = 0..1000; $_*$_ for @array; print join” “,@array;