Грокаем C++
9.37K subscribers
44 photos
1 video
3 files
562 links
Два сеньора C++ - Владимир и Денис - отныне ваши гиды в этом дремучем мире плюсов.

По всем вопросам (+ реклама) @ninjatelegramm

Менеджер: @Spiral_Yuri
Реклама: https://telega.in/c/grokaemcpp
Мы на TGstat: https://tgstat.ru/channel/@grokaemcpp/stat
Download Telegram
​​Уплощаем многомерный массив
#опытным

Иногда у вас есть коллекция элементов, для каждого из которых вы выполняете операцию, возвращающую вектор значений:

std::vector<int> Process(const std::string& str);

std::vector<std::string> elems = ...;

auto result_view = elems | std::views::transform([](const std::string& str) {
return Process(str);
})


Итоговое отображение result_view - это по факту набор векторов. Чтобы сложить это все в один массив нужен двойной цикл. А можно как-то удобно и лаконично получить плоский вектор интов?

С помощью С++20 отображения std::views::join:

std::vector<int> Process(const std::string& str);

std::vector<std::string> elems = ...;

auto result = elems | std::views::transform([](const std::string &str) {
return Process(str);
}) |
std::views::join | std::ranges::to<std::vector>();

std::print("{}", result);


Это все сработает и на экране появлятся заветные чиселки.

Здесь используется std::ranges::to и std::print, которые добавлены в 23-м стандарте

Если у вас элементы, которые хотелось бы переместить, а не скопировать, то можно добавить еще с++23 отображение as_rvalue:

auto result = elems | std::views::transform([](const auto & elem) {
return Process(elem);
}) |
std::views::join | std::views::as_rvalue |
std::ranges::to<std::vector>();


Если хочется чистого кода без циклов, то рэнджи для этого и сделаны.

Don't stuck in a loop. Stay cool.

#cpp20 #cpp23
19👍13🔥7
join
#опытным

Как прекрасно сделан в питоне метод join у строки. Чтобы соединить список строк разделителем нужно просто написать:

my_list = ["John", "Peter", "Vicky"]
x = " ".join(my_list)
print(x)
# OUTPUT
# John Peter Vicky


И как же сложно того же результата достичь в плюсах!

То делают через потоки:

std::string join(const std::vector<std::string>& vec, const std::string& delimiter) {
if (vec.empty()) return "";

std::ostringstream oss;
oss << vec[0];

for (size_t i = 1; i < vec.size(); ++i) {
oss << delimiter << vec[i];
}

return oss.str();
}


то через std::accumulate:

std::string join(const std::vector<std::string>& vec, const std::string& delimiter) {
if (vec.empty()) return "";

return std::accumulate(
std::next(vec.begin()), vec.end(),
vec[0],
[&delimiter](const std::string& a, const std::string& b) {
return a + delimiter + b;
}
);
}


Ну вы что! Стандартная строка же себе не может позволить иметь метод join, принимающий коллекцию строк и возвращающий объединенную строку с разделителями. Это же не универсально и никому не надо...

Но в С++23 наконец-то появилось хоть что-то похожее на адекватное решение. Используем std::views::join_with:

std::string join(const std::vector<std::string> &vec,
const std::string &delimiter) {
return vec | std::views::join_with(delimiter) |
std::ranges::to<std::string>();
}


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

И жизнь стала чуть-чуть счастливее...

Make thing simple. Stay cool.

#cpp23
24👍10🔥9😁5