Как стать автором
Обновить

Комментарии 10

Я что-то не понимаю, там сравнивается хранение в памяти массива из 800.000 элементов и одной переменной? Тёплое более широкое, чем мягкое?

Очередная ересь. Чего ещё ожидать от otus, с их эталонным говнокодом, который они не первый год преподают на своих "курсах".

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

Память экономит не генератор. А цикл, который выдаёт по одному значению.

В данном примере - это цикл for.

А генератор всего лишь позволяет замаскировать обращение к этому циклу под перебор массива. В программировании это называется синтаксическим сахаром, а не экономией памяти. Генератор позволяет писать более красивый код, генератор позволяет унифицировать интерфейсы. Но вот что он не делает - так это не экономит память.

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

Вот так приходит человек получить "цифровые навыки от ведущих экспертов", а получает вот такую ерунду. Я понимаю, что автор статьи намеренно передёргивает, чтобы собрать побольше кликов. Но на Хабр-то зачем это тащить?

"Если вы в своем приложении создаете огромные массивы", то генератор вам не поможет ни капли. Потому что во-первых, массив и последовательность чисел - это разные вещи. Второе - это всего лишь ничтожное подмножество первого, которое практически не встречается в реальной жизни. В то время как с реальным массивом, хранящим не просто цыферки, а какую-то осмысленную информацию, и к которому ещё и требуется произвольный доступ, генераторы помогут от слова "никак".

А во-вторых, как уже говорилось выше, в реальности нам помогает соответствующий цикл - либо for, как в примере из статьи, либо while, который получает по одной строчке из внешнего источника данных - файла или БД. Вот они-то как раз и будут экономить память. А генератор всего лишь позволит упаковать их в красивую оберточку.

Понятно, что перевод, но непонятно, зачем так все усложнять?..

yield используется для возврата значений промежуточных вычислений в функциях-генераторах.

Генератор - функция с хранимым состоянием. Генератор выполняется до следующего слова yield в коде, где выбрасывает рассчитанное значение значение наружу и "засыпает", ожидая следующего вызова, чтобы потом продолжить с прерванной точки. С помощью генератора можно реализовать поддежку [эффективных по помяти] бесконечных последовательностей, по типу ряда Фибоначчи или гармонических рядов. Генераторы также называют "сопрограммами".

С помощью генераторов реализуют протокол итерации, который используется для удобного обхода коллекций через стандартные механизмы языка (как правило, через цикл for). Поддержка протокола итераций реализована во многих языках, как то Python, JavaScript, C#, Rust.

Использование слова return в генераторе полностью останавливает его, после чего генератор нельзя уже будет пробудить повтрно.

Ну а потом уже можно и пример с большим массивом. А то прям какой-то детектив написан, о каких-то [мнимых] различиях совершенно разных конструкций - да еще и в стиле секрета Полишинеля...

А кто-нибудь может привести реальный пример использования где yield полезен? И при этом был бы предпочтительнее других реализаций?

Понятно что если сделать вот так вот как в статье, то можно сэкономить памяти, но в реальности у меня ещё не было случаев генерации на лету огромных массивов...

Я писал выше, что память экономит не генератор, а цикл

foreach c генератором - это всего лишь красивая обёртка для цикла

for ($i = 1; $i < 800000; $i++) {
    echo $i;
}

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

Отсюда и практическое применение генератора: оно не в "экономии памяти", а в использовании foreach. Везде, где foreach удобнее while или for, генератор будет предпочтительнее. Он позволяет писать более красивый, и - главное - более универсальный код. К примеру мы пишем обработчик каких-то данных, причем источник этих данных может быть совершенно разным - это может быть массив, или текстовый файл, или база данных.

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

А генератор позволит обращаться ко всем трем источникам через foreach, то есть код можно будет написать только один раз.

Это понятно что память экономит не генератор сам по себе.

Поэтому и возник вопрос практичности данной конструкции. Про универсальность и не подумал. В принципе довольно интересное применение, спасибо

С помощью генераторов удобно писать [бесконечные] итераторы всяких последовательностей - к примеру, четных чисел, когда каждое последующее обращение к генератору будет возвращать следующий элемент из последовательности. Такое себе вычисление по-требованию, то есть "ленивое".

Но этим спектр применения не исчерпывается. К примеру, у Дэвида Бизли есть пример реализации на сопрограммах (на Питоне) ядра операционной системы с кооперативной многозадачностью.

Как пример: удобен при реализации обращений к бд на большие объемы данных, где используется SQL вида:

c yield:

function getDbValues() {
	$query = "
		SELECT *
		FROM table
		WHERE :id > id
		LIMIT 1000
		ORDER BY id DESC
	";

	$id = 0;
	
	for {
		$rows = executeQuery($query, $id);
		yield $rows;

		if (count($rows) === 0) {
			return ;
		}

		$id = $rows[count($rows)];
	}
}

без yield:

function getDbValues($lastID) {
	$query = "
		SELECT *
		FROM table
		WHERE :id > id
		LIMIT 1000
		ORDER BY id DESC
	";

	return executeQuery($query, $lastID);
}

В случае без yield логику выбора последнего id необходимо будет вынести в место вызова функции из-за чего код может выглядеть менее красивым/читабельным :)

Так же можно дополнительно сэкономить время, если выполнять запрос через PreparedStatement в функции генераторе. Это можно, конечно же, реализовать и через return, но опять же через yield это будет сделать проще/читабельней.

Вот да, собственно фраза "В случае без yield логику выбора последнего id необходимо будет вынести в место вызова функции" и является ключевой. Причем, когда я недавно смотрел, в самых первых статьях про генераторы, например у Феррары, как раз об этом и говорится. А потом полезли какие-то фрики размахивать экономией памяти. Польза yield не в экономии памяти, а в более красивом коде, который и память экономит, и позволяет инкапсулировать детали реализации.


А в данной статье это никак не обозначено, к сожалению, что приводит людей к неверным выводам.

Супер, спасибо за инфу👋👍

Зарегистрируйтесь на Хабре, чтобы оставить комментарий