NSU-Programming/NSU-Programming.github.io

Примеры разворачивания коллекции через std::accumulate

sekrasoft opened this issue · 4 comments

Стоит добавить в лекцию что-то про мощь std::accumulate, например с наивным копированием:

#include <iostream>
#include <vector>
#include <numeric>

int main() {
	std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
	
	std::vector<int> v1 = std::accumulate(v.begin(), v.end(), std::vector<int>(),
		[](std::vector<int> xs, int x){ xs.push_back(x); return xs; });

	std::cout << "copy:";
	for (auto& x : v1) std::cout << " " << x;
	std::cout << std::endl;

	std::vector<int> v2 = std::accumulate(v.begin(), v.end(), std::vector<int>(),
		[](std::vector<int> xs, int x){ xs.insert(xs.begin(), x); return xs; });

	std::cout << "reversed:";
	for (auto& x : v2) std::cout << " " << x;
	std::cout << std::endl;

	return 0;
}

Или на ссылках, хотя там функциональная чистота теряется.

https://habr.com/ru/post/57503/

А не лучше ли второй пример написать так:

std::vector<int> v2 = std::accumulate(v.rbegin(), v.rend(), std::vector<int>(),
		[](std::vector<int> xs, int x){ xs.push_back(x); return xs; });

Ну и я совсем не уверен, что эти примеры показывают хороший стиль программирования. Мы обсуждаем выразительность и читабельность кода. А тут с помощью accumulate делаются не очевидные на первый взгляд вещи. std::copy здесь куда уместнее. Нет ли каких-нибудь реалистичных примеров, где accumulate действительно выручает неочевидным образом? И даже copy здесь не нужен. И первый и второй случаи решаются с помощью конструктора из двух итераторов...

А не лучше ли второй пример написать так:

std::vector<int> v2 = std::accumulate(v.rbegin(), v.rend(), std::vector<int>(),
		[](std::vector<int> xs, int x){ xs.push_back(x); return xs; });

Наверно лучше. Раз в коде не используется конструктор 2-кортежа или списка, полной аналогии ФП нет, не грешно улучшить код со стороны C++.

Ну и я совсем не уверен, что эти примеры показывают хороший стиль программирования. Мы обсуждаем выразительность и читабельность кода. А тут с помощью accumulate делаются не очевидные на первый взгляд вещи. std::copy здесь куда уместнее.

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

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

Нет ли каких-нибудь реалистичных примеров, где accumulate действительно выручает неочевидным образом? И даже copy здесь не нужен. И первый и второй случаи решаются с помощью конструктора из двух итераторов...

У меня наоборот есть антипример с подсчётом неправильной дисперсии за один проход.

Когда коллекция сворачивается по

f ((sx, sx2), x) = (sx+x, sx2+x*x)
(sx0, sx20) = (0, 0)

и при взятии (sx2-sx^2)/n точность теряется.

Надо посмотреть код моего сервера. Там активно используются map, reduce, filter. Где-то там был код, который сшивал перекрывающиеся диапазоны.