Тут интересна не задача, а подход к решению. С такого вот баловства начинаются серьёзные вещи. FFI в языке уже давно, но случаи использования всё ещё единичные. А с таких дурацких на вид примеров, глядишь, начнётся и более массовое использование.
Бывает часто такая ситуация, что неправильно можем называть имя класса, имя переменной или название файла, не всегда это получается заметить в силу разных факторов, а потом из этого в проекте могут быть опечатки, что могут приводить к complexity кода и путаницы в прочтении
Даже не знаю, что это: пример такой путаницы или вы забыли натравить свой анализатор на текст статьи.
В любом случае, из этого описания непонятно почти ничего, и в частности - что эта утилита собственно делает. "Правильность" может означать совсем разные вещи - например соответствие правилам PSR-4. Но судя по всему, тут просто делается проверка орфографии. И похоже, что лично два вас такая проверка является насущность необходимостью.
В любом случае - спасибо. С одной стороны, у меня орфографию проверяет PHPStorm, но с другой - всегда приятно видеть такой законченный проект, даже с интеграцией в композер.
Коллега, это гениально! Увидев только заголовок, мой не проснувшийся мозг построил другую траекторию - валидация на Go, в РНР через FFI, а в браузере - через WASM. Но так тоже хорошо! Вряд ли кто-то будет проделывать все эти телодвижения ради довольно простой и механически решаемой задачи (несколько строчек с правилами валидации) - особенно при наличии всех подводных камней, таких как устаревшая версия JS - но как идея это очень красиво!
Кстати, я сейчас подумал, что куда проще это всё решается двумя библиотеками с одинаковым синтаксисом валидации! Просто закинул с сервера массив
И JS по ним всё провалидировал. Конечно, всё равно будут проблемы с валидациями, требующими доступа к БД, но их можно игнорировать и сделать сервер-онли. Впрочем, в каком-нибудь Livewire это наверняка уже реализовано?
Вы или троллите, или не можете воспринять два предложения вместе. Оба варианта вас не красят.
Здесь нет вопроса, куда поместить этот код. И поэтому ваш ответ, "генератор это и есть место, чтобы поместить туда этот код" не имеет смысла.
Если читать вопрос целиком, то он звучит очень просто - зачем сначала читать ВЕСЬ миллион записей, и только потом начинать записывать в БД? Почему нельзя это делать поблочно? И это очень хороший, логичный вопрос. На который автор не дал ответа.
Не дали его и вы. Вопрос, повторюсь, был совсем не про взывающий код. А про осмысленность исходного алгоритма - сначала читать, а потом только писать.
Правильным подходом здесь будет прочитать первые десять тысяч и записать их в БД. Потом прочитать следующие 10, и снова записать. И так до конца файла. Это основной, базовый алгоритм. При этом генератор для него совершенно некритичен, он здесь является синтаксическим сахаром. И с ним, и без него, алгоритм поблочной записи работает одинаково. Разница только в "вызывающем коде", который ни к алгоритму, ни вопросу в исходном комментарии отношения не имеет.
Потому что бизнес-требования такие?
И здесь опять вы опять вырвали предложение из контекста. Вопрос формулируется из двух предложений. Второе уточнят первое. То есть вопрос не "зачем извлекать и записывать", а "почему не извлекать порциями?". При том что весь миллион вы всё равно за раз не запихнёте - попросту упрётесь в max_allowed_packet. То есть читать весь миллион - хоть с генератором, хоть без - нет никакого смысла.
Вы здесь из пальца высосали некий метод $db->query(), который исходно возвращает массив. И начали оптимизировать getOrders() через нелепый лимит.
Но оптимизировать здесь надо было сам метод $db->query(). Который сейчас в цикле while получает из бд строки по одной, и зачем-то запихивает их в массив. Вот этот-то цикл и является исходным.
Вместо массива, метод query() должен возвращать переменную, предоставляющую доступ результатам запроса. А дальше у нас есть два варианта:
Использовать этот цикл как есть, как делают миллионы крудошлёпов по всему миру:
$result = $db->query('SELECT * FROM products');
while ($row = $result->fetch()) {
// что-то делаем со строкой
}
Обернуть этот цикл в генератор
function getOrders() {
$result = $db->query('SELECT * FROM products');
while ($row = $result->fetch()) {
yield $row;
}
}
И спокойно использовать его без нелепых ужимок с лимитами, которые заставляют базу каждый раз выполнять один и тот же запрос, а потом ходить по его результату с самого начала всё дальше и дальше, в полном соответствии с алгоритмом маляра Шлемиэля.
И здесь легко увидеть, что исходный цикл стал основой генератора. И поскольку именно этот цикл экономит память, то и генератор, вслед за ним - тоже. И даже вы, если сможете немного умерить свою спесь, тоже это увидите.
В данном случае Вторая древнейшая служба Хабра не при чём. Да, судя по всему, люди и раньше замечали эту несуразность, но хода их открытия не получали. Нужно было чтобы человек сел и сделал полноценное исследование, а не просто "обратил внимание". А такое исследование появилось как раз только две недели назад.
При чём здесь важность? Редактор не проходил по критериям конфликта интересов и ботоводства. Создавать/править статью о себе, спрятавшись за прокси - это кринж, не только в Википедии.
Вы знаете, я и сам, будучи анонимным редактором Википедии, иногда сталкиваюсь с формализмом или автоматизированными откатами, когда проще плюнуть, чем ходить обивать пороги, доказывая адекватность правки. Что не добавляет теплых чувств к администраторам. Есть там и откровенные нарциссы, и самодуры, к сожалению. Тем не менее, система правил там вполне стройная и логичная, и в подавляющем большинстве случаев работает в плюс.
В вашем же случае это уже какая-то патологическая ненависть к ресурсу в целом, вы просто однобоко поливаете его грязью. Увы, к адекватности такой позиции есть большие вопросы.
передачу данных в циклы foreach без предварительной загрузки массива в память
У множества людей какая-то поразительная слепота, они в упор не видят foreach, а видят только "вызывает превышение программой предела памяти". И в итоге пишут полную чушь.
В вашей статье память упоминается десятки раз, но как в мануале - в контексте использования foreach - ни разу. Если бы вы сами следовали информации с "малоизвестного и почти неиспользуемого сайта", каждый раз упоминая foreach в контексте экономии памяти, то претензий бы к вам не было. То есть вместо
значительно сократить использование памяти при обработке больших наборов данных
правильно будет
значительно сократить использование памяти при обработке больших наборов данных через foreach
Ну как разные, если ваш генератор работает только потому, что у него внутри это самое fetch()? 😂 Ну уже смешно, ей-богу :)
Генератор - это обёртка для кода, который экономит память. Конвертирующая этот код в универсальный интерфейс. Но сам по себе он ничего не экономит. Память экономит чтение из базы по одной строке. Генератор же - это такой конвертор, который позволяет результат любого цикла представить в виде массива.
Если исходный цикл экономит память, то и генератор на его основе будет экономить. Если исходный цикл не экономит память, то и генератор на его основе ничего не сэкономит. Ну это уже если совсем на пальцах объяснять эту примитивную логику. Даже так до вас не доходит?
Разработчики говорят, что это способ использовать меньше памяти с циклом foreach. Увы, у большинства читателей этой фразы проблемы с глазами, и часть про foreach они в упор не видят.
Я на этом не останавливался раньше, но стоило, да. Если мне сотрудник принесёт вот такой вот код из вашего комментария,
// Пример: взять первые 100 машин дороже 1 млн, лишние страницы не дергаются
foreach (take(filter(fetchCars(), fn($c) => $c['price'] > 1_000_000), 100) as $car) {
process($car);
}
Код, который каждый раз ходит за выборкой напрямую во внешнее API - а не закэширует сначала данные локально - то такой сотрудник вылетит с работы без выходного пособия.
Даже слишком модно, увы. https://habr.com/ru/articles/942398/
Тут интересна не задача, а подход к решению. С такого вот баловства начинаются серьёзные вещи. FFI в языке уже давно, но случаи использования всё ещё единичные. А с таких дурацких на вид примеров, глядишь, начнётся и более массовое использование.
Даже не знаю, что это: пример такой путаницы или вы забыли натравить свой анализатор на текст статьи.
В любом случае, из этого описания непонятно почти ничего, и в частности - что эта утилита собственно делает. "Правильность" может означать совсем разные вещи - например соответствие правилам PSR-4. Но судя по всему, тут просто делается проверка орфографии. И похоже, что лично два вас такая проверка является насущность необходимостью.
В любом случае - спасибо. С одной стороны, у меня орфографию проверяет PHPStorm, но с другой - всегда приятно видеть такой законченный проект, даже с интеграцией в композер.
Бинго, мне такая же идея пришла в голову!
Коллега, это гениально! Увидев только заголовок, мой не проснувшийся мозг построил другую траекторию - валидация на Go, в РНР через FFI, а в браузере - через WASM. Но так тоже хорошо! Вряд ли кто-то будет проделывать все эти телодвижения ради довольно простой и механически решаемой задачи (несколько строчек с правилами валидации) - особенно при наличии всех подводных камней, таких как устаревшая версия JS - но как идея это очень красиво!
Кстати, я сейчас подумал, что куда проще это всё решается двумя библиотеками с одинаковым синтаксисом валидации! Просто закинул с сервера массив
И JS по ним всё провалидировал. Конечно, всё равно будут проблемы с валидациями, требующими доступа к БД, но их можно игнорировать и сделать сервер-онли.
Впрочем, в каком-нибудь Livewire это наверняка уже реализовано?
Всё правильно. Потому что генераторы вообще не про память. А про более удобную организацию кода.
Ай, Моська! знать она сильна
Неправда. В документации написано "уменьшить расход памяти при использовании foreach". А закончить действительно стоит.
Вы или троллите, или не можете воспринять два предложения вместе. Оба варианта вас не красят.
Здесь нет вопроса, куда поместить этот код. И поэтому ваш ответ, "генератор это и есть место, чтобы поместить туда этот код" не имеет смысла.
Если читать вопрос целиком, то он звучит очень просто - зачем сначала читать ВЕСЬ миллион записей, и только потом начинать записывать в БД? Почему нельзя это делать поблочно? И это очень хороший, логичный вопрос. На который автор не дал ответа.
Не дали его и вы. Вопрос, повторюсь, был совсем не про взывающий код. А про осмысленность исходного алгоритма - сначала читать, а потом только писать.
Правильным подходом здесь будет прочитать первые десять тысяч и записать их в БД. Потом прочитать следующие 10, и снова записать. И так до конца файла. Это основной, базовый алгоритм. При этом генератор для него совершенно некритичен, он здесь является синтаксическим сахаром. И с ним, и без него, алгоритм поблочной записи работает одинаково. Разница только в "вызывающем коде", который ни к алгоритму, ни вопросу в исходном комментарии отношения не имеет.
И здесь опять вы опять вырвали предложение из контекста. Вопрос формулируется из двух предложений. Второе уточнят первое. То есть вопрос не "зачем извлекать и записывать", а "почему не извлекать порциями?". При том что весь миллион вы всё равно за раз не запихнёте - попросту упрётесь в max_allowed_packet. То есть читать весь миллион - хоть с генератором, хоть без - нет никакого смысла.
Вы здесь из пальца высосали некий метод $db->query(), который исходно возвращает массив. И начали оптимизировать getOrders() через нелепый лимит.
Но оптимизировать здесь надо было сам метод $db->query(). Который сейчас в цикле while получает из бд строки по одной, и зачем-то запихивает их в массив. Вот этот-то цикл и является исходным.
Вместо массива, метод query() должен возвращать переменную, предоставляющую доступ результатам запроса. А дальше у нас есть два варианта:
Использовать этот цикл как есть, как делают миллионы крудошлёпов по всему миру:
Обернуть этот цикл в генератор
И спокойно использовать его без нелепых ужимок с лимитами, которые заставляют базу каждый раз выполнять один и тот же запрос, а потом ходить по его результату с самого начала всё дальше и дальше, в полном соответствии с алгоритмом маляра Шлемиэля.
И здесь легко увидеть, что исходный цикл стал основой генератора. И поскольку именно этот цикл экономит память, то и генератор, вслед за ним - тоже. И даже вы, если сможете немного умерить свою спесь, тоже это увидите.
Вспоминается анекдот "Но такая фигня получается!.."
Если это такой динозавр, что о нём по какой-то причине пишут только журналисты (а не палеонтологи), то тут даже и "документалка" не поможет.
Ну вот я такой новичок. Под описанные вами ужасы попадает максимум 5% моих правок. Что я делаю не так?
В данном случае Вторая древнейшая служба Хабра не при чём. Да, судя по всему, люди и раньше замечали эту несуразность, но хода их открытия не получали. Нужно было чтобы человек сел и сделал полноценное исследование, а не просто "обратил внимание". А такое исследование появилось как раз только две недели назад.
При чём здесь важность? Редактор не проходил по критериям конфликта интересов и ботоводства. Создавать/править статью о себе, спрятавшись за прокси - это кринж, не только в Википедии.
Вы знаете, я и сам, будучи анонимным редактором Википедии, иногда сталкиваюсь с формализмом или автоматизированными откатами, когда проще плюнуть, чем ходить обивать пороги, доказывая адекватность правки. Что не добавляет теплых чувств к администраторам. Есть там и откровенные нарциссы, и самодуры, к сожалению. Тем не менее, система правил там вполне стройная и логичная, и в подавляющем большинстве случаев работает в плюс.
В вашем же случае это уже какая-то патологическая ненависть к ресурсу в целом, вы просто однобоко поливаете его грязью. Увы, к адекватности такой позиции есть большие вопросы.
И по поводу мануала. В нём написано.
У множества людей какая-то поразительная слепота, они в упор не видят foreach, а видят только "вызывает превышение программой предела памяти". И в итоге пишут полную чушь.
В вашей статье память упоминается десятки раз, но как в мануале - в контексте использования foreach - ни разу. Если бы вы сами следовали информации с "малоизвестного и почти неиспользуемого сайта", каждый раз упоминая foreach в контексте экономии памяти, то претензий бы к вам не было. То есть вместо
правильно будет
Ну как разные, если ваш генератор работает только потому, что у него внутри это самое fetch()? 😂
Ну уже смешно, ей-богу :)
Генератор - это обёртка для кода, который экономит память. Конвертирующая этот код в универсальный интерфейс. Но сам по себе он ничего не экономит. Память экономит чтение из базы по одной строке. Генератор же - это такой конвертор, который позволяет результат любого цикла представить в виде массива.
Если исходный цикл экономит память, то и генератор на его основе будет экономить. Если исходный цикл не экономит память, то и генератор на его основе ничего не сэкономит. Ну это уже если совсем на пальцах объяснять эту примитивную логику. Даже так до вас не доходит?
Разработчики говорят, что это способ использовать меньше памяти с циклом foreach. Увы, у большинства читателей этой фразы проблемы с глазами, и часть про foreach они в упор не видят.
И отдельно я напишу про
Я на этом не останавливался раньше, но стоило, да. Если мне сотрудник принесёт вот такой вот код из вашего комментария,
Код, который каждый раз ходит за выборкой напрямую во внешнее API - а не закэширует сначала данные локально - то такой сотрудник вылетит с работы без выходного пособия.