Реальная работа с yield (PHP)
Некоторые функции PHP могут новичкам показаться надуманными, излишними и не встречающимися в реально разработке. Одной из таких являются генераторы. Есть ли какое-либо преимущество в производительности при использовании yield по сравнению с возвратом нового массива в функции/методе?
Вообще, я читал книгу по C#, и одна из концепций, касалась интерфейса IEnumerable, и он использовал yield в методе, отвечающем за обход объекта. Читая, я начал думать о ключевом слове yield и внезапно вспомнил, что PHP также использует yield.
Первоначально во всех моих функциях и методах PHP, отвечающих за обход и возврат массива, я делал что-то вроде этого:
function gen_one_to_three () { $ arr = массив (); for ($ i = 1; $ i <= 3; $ i ++) { $ arr [] = $ i; } return $ arr; }Тогда как с yield функция будет выглядеть так:
function gen_one_to_three () { for ($ i = 1; $ i <= 3; $ i ++) { yield $ i; } }Имейте в виду, я знаю, что это, вероятно, чрезмерное упрощение yield и не может точно передать его полезность. Во всяком случае, сама по себе функция совершенно бесполезна, но я в основном использую ее в качестве примера, чтобы проиллюстрировать два общих подхода к одной и той же проблеме.
Но в основном меня интересовало, есть ли какая-либо значительная разница в производительности между двумя подходами? Я мог бы предположить, что выделение достаточного количества памяти для нового массива по сравнению с простым сохранением состояния указателя было бы немного большей работой.
Но в случае улучшения производительности, достаточно ли этого, чтобы гарантировать изменение личных привычек за 5 лет? По вашему опыту, может ли что-то подобное потенциально ухудшить читаемость? Есть ли какие-то основные проблемы, которые я мог бы упустить из-за общей важности - или отсутствия таковой - ключевого слова yield?
Это наиболее полезно в ситуациях, когда $i станет действительно очень большим. Массивы в php, ну, давайте просто не будем говорить, что это «легковесные» структуры данных. Если вы поместите баджиллион вещей в массив, чтобы вернуть его, вы очень быстро раздуваете свою память. Yield в этом очень помогает.
Для массивов не будет большой разницы в производительности, если только массив не будет большим.
Вот более практичный пример.
Допустим, у вас есть функция, которая абстрагирует строки чтения из CSV. Он читает из CSV, захватывает заголовки, а затем возвращает ассоциативный массив.
Так что-то вроде этого:
function readCSV($file){ $fh = fopen($file,'r'); $headers = fgetcsv($fh); $lines = Array(); while($line = fgetcsv($fh)){ $vars = Array(); foreach($line as $key=>$val){ $vars[$headers[$key]] = $val; } $lines[] = $vars; } return $lines; }Перебирает файл, помещает все строки в ассоциативный массив, возвращает массив.
Но что, если я ищу только одну запись? Что, если эта запись находится в верхней части CSV-файла на 10 000 строк? Если я вызываю readCSV и получаю весь массив, я прочитаю весь файл, когда мне нужна только одна строка, и все готово.
Вместо этого я могу:
function readCSV($file){ $fh = fopen($file,'r'); $headers = fgetcsv($fh); while($line = fgetcsv($fh)){ $vars = Array(); foreach($line as $key=>$val){ $vars[$headers[$key]] = $val; } yield $vars; } } foreach(readCSV($file) as $vars){ if($vars['username'] == 'JoeCoT'){ echo "This is what \n"; break; } }Теперь я могу просто перебирать строки, пока не найду то, что ищу, а затем остановиться.
Генераторы позволяют мне функционализировать такие вещи, как эта, которые раньше было довольно неэффективно абстрагировать.
Это также значительно упрощает абстрагирование при работе с такими вещами, как наборы результатов SQL-запросов, поскольку вы можете заставить свою функцию-оболочку вызывать команду sql fetch в цикле с yield, вместо того, чтобы буферизовать все результаты в массив.
public function selectRows($sql) { $result = $this->query($sql); while ($row = $result->fetch_assoc()) { yield $row; } } foreach ($db->selectRows($sql) as $row) { } function mysqli_gen (mysqli_result $res) { while($row = mysqli_fetch_assoc($res)) { yield $row; } }Но он может позволить вам замаскировать последовательность while()/fetch() как вызов foreach(), если вы предпочитаете, без дополнительных затрат памяти.
Однако PDO — не очень хороший пример, поскольку PDOStatement уже реализует проходимый интерфейс и, следовательно, его можно перебирать с помощью foreach():
$res = $mysqli->query("SELECT * FROM users"); foreach (mysqli_gen($res) as $row) { var_export($row); } function mysqli_gen (mysqli_result $res) { while($row = mysqli_fetch_assoc($res)) { yield $row; } }
Автор этого материала - я - Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML - то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.
Отправляя сообщение я подтверждаю, что ознакомлен и согласен с политикой конфиденциальности данного сайта.