Статьи

Ленивый PHP: часть 2

В продолжение Lazy PHP: часть 1 , пришло время снова стать ленивым с некоторой Lazy Evaluation .

Переменные функции
Менее известной особенностью PHP является возможность помещать имя функции в переменную и вызывать функцию через переменную, как описано здесь в руководстве.

Например, используя функцию strtolower () ;

$function = 'strtolower'; $string = 'HELLO WORLD!'; echo $function($string); // displays 'hello world!' 

Хорошо, но какой в ​​этом смысл? Недавно мне понадобилась встроенная реализация PHP встроенной функции array_change_key_case () для версий PHP ниже 4.2.0 (то есть, когда она стала доступна). Благодаря первому пользователю, предоставившему комментарий от 5 февраля 2004 года, мне даже не пришлось задействовать мой мозг. Было это сразу;

function array_change_key_case($array, $changeCase = CASE_LOWER) { $return = array(); foreach($array as $key => $value) { switch($changeCase) { case CASE_LOWER: $return[strtolower($key)] = $value; break; case CASE_UPPER: default: $return[strtoupper($key)] = $value; break; } } return $return; }
function array_change_key_case($array, $changeCase = CASE_LOWER) { $return = array(); foreach($array as $key => $value) { switch($changeCase) { case CASE_LOWER: $return[strtolower($key)] = $value; break; case CASE_UPPER: default: $return[strtoupper($key)] = $value; break; } } return $return; } 

[small] Обратите внимание, что использование массива $ return связано с тем, что встроенная реализация array_change_key_case () устраняет дубликаты ключей. [/ small]

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

Здесь переменная функция может помочь;

function array_change_key_case($array, $changeCase = CASE_LOWER) { switch($changeCase) { case CASE_LOWER: $caseFunc = 'strtolower'; break; case CASE_UPPER: default: $caseFunc = 'strtoupper'; break; } $return = array(); foreach($array as $key => $value) { $return[$caseFunc($key)] = $value; } return $return; }
function array_change_key_case($array, $changeCase = CASE_LOWER) { switch($changeCase) { case CASE_LOWER: $caseFunc = 'strtolower'; break; case CASE_UPPER: default: $caseFunc = 'strtoupper'; break; } $return = array(); foreach($array as $key => $value) { $return[$caseFunc($key)] = $value; } return $return; } 

Помещая имя функции, которую я хочу использовать (strtolower () или strtoupper ()), в переменную, прежде чем я начну циклически проходить по массиву, условие оценивается только один раз. Для небольшого массива выигрыш в производительности незначителен, но когда вы обрабатываете много данных, вы можете заметить разницу …

Лямбда-функции
Теперь иметь возможность помещать только имя функции в переменную — это хорошо, но жизнь становится еще интереснее, если взглянуть на create_function () , функцию PHP для создания других функций!

По сути, это позволяет вам создать функцию и сохранить ее в переменной, не называя функции именем.

Вот простой пример;

$lambda = create_function('$str', 'return strtolower($str);'); $string = 'HELLO WORLD!'; $string = $lambda($string); echo $string; // Displays 'hello world!'
$lambda = create_function('$str', 'return strtolower($str);'); $string = 'HELLO WORLD!'; $string = $lambda($string); echo $string; // Displays 'hello world!' 

На самом деле первая строка говорит PHP о том, что нужно создать что-то вроде этого;

function "anonymous"($str) { return strtolower($str); }
function "anonymous"($str) { return strtolower($str); } 

… Но без указания имени функции (для примера я использовал «анонимный»).

Вы также можете использовать ссылки с create_function () например;

$lambda = create_function('& $str', '$str = strtolower($str);'); $string = 'HELLO WORLD!'; $lambda($string); echo $string; // Displays 'hello world!'
$lambda = create_function('& $str', '$str = strtolower($str);'); $string = 'HELLO WORLD!'; $lambda($string); echo $string; // Displays 'hello world!' 

Первая строка, если вы объявите ее как функцию, будет выглядеть следующим образом;

function "anonymous"(& $str) { $str = strtolower($str); }
function "anonymous"(& $str) { $str = strtolower($str); } 

Опять же, это пригодится при обработке больших наборов данных.

Используя гипотетический пример, подобный приведенному выше, предположим, что вам нужна функция для расчета налога с продаж из вашего интернет-магазина (nRonOnline.com). Допустим, НДС установлен в размере 17%, поэтому его необходимо добавить к цене товаров, которые вы продаете.

Будучи проницательным человеком, вы обнаруживаете шанс на некоторую дополнительную прибыль с сомнительной игрой чисел. Для клиентов вы будете рассчитывать налог и округлять до ближайшего целого числа. Но когда дело доходит до отчетности налоговому органу, вы округляете цифру, включающую налог ( не пытайтесь делать это дома! ).

С первой попытки, вот функция, которую вы пишете, чтобы решить эту проблему;

function calcTax($array, $round = ROUND_DOWN) { foreach ( $array as $key => $value ) { switch ( $round ) { case ROUND_UP: $array[$key] = ceil($number * 1.17); break; case ROUND_DOWN: default: $array[$key] = floor($number * 1.17); break; } } return $array; }
function calcTax($array, $round = ROUND_DOWN) { foreach ( $array as $key => $value ) { switch ( $round ) { case ROUND_UP: $array[$key] = ceil($number * 1.17); break; case ROUND_DOWN: default: $array[$key] = floor($number * 1.17); break; } } return $array; } 

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

Вместо того чтобы использовать это условие переключения внутри цикла, вы можете использовать лямбда-функцию и вычислять условие один раз в начале;

function calcTax($array, $round = ROUND_DOWN) { switch ( $round ) { case ROUND_UP: $func = create_function('$number', 'return ceil($number * 1.17);'); break; case ROUND_DOWN: default: $func = create_function('$number', 'return floor($number * 1.17);'); break; } foreach ( $array as $key => $value ) { $array[$key] = $func($value); } return $array; }
function calcTax($array, $round = ROUND_DOWN) { switch ( $round ) { case ROUND_UP: $func = create_function('$number', 'return ceil($number * 1.17);'); break; case ROUND_DOWN: default: $func = create_function('$number', 'return floor($number * 1.17);'); break; } foreach ( $array as $key => $value ) { $array[$key] = $func($value); } return $array; } 

Если вы хотите узнать больше о лямбда-функциях, лучше всего спросить ближайшего программиста на Python . Например, Марк Пилигрим отлично справляется с объяснением лямбда-функций в Dive into Python .

Во всяком случае, хватит Lazy PHP на сегодня.