Реальная работа с 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 - то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.
Программы на заказ
Отзывы
Контакты