Как реализовать flatmap с использованием диапазонов rangev3

У меня довольно просто flatmap функция реализована в C ++ для std::vector, но было высказано предположение, что диапазоны, как правило, лучше. Вот векторное решение:

// flatmap: [A] -> (A->[B]) -> [B]
template<typename T, typename FN>
static auto flatmap(const std::vector<T> &vec, FN fn)
-> std::vector<typename std::remove_reference<decltype(fn(T())[0])>::type> {
std::vector<typename std::remove_reference<decltype(fn(T())[0])>::type> result;
for(auto x : vec) {
auto y = fn(x);
for( auto v : y ) {
result.push_back(v);
}
}
return result;
};

Также было предложено использовать итераторы, но это нарушает удобство компоновки функции:

map(filter(flatmap( V, fn), fn2), fn3)

Я бы предположил, что в мире range-v3 я хотел бы написать выше:

auto result = v | flatmap(fn) | filter(fn2) | transform(fn3);

Похоже на flatmap должно быть просто тривиальная комбинация views::for_each, yield_from а также transformНо я изо всех сил пытаюсь понять, как соединить их всех вместе.

7

Решение

IIUC, ваш flatmap функция ничего, кроме Range-V3 view::for_each, Пытаться:

using namespace ranges;
auto result = v | view::for_each(fn) | to_vector;

НТН

7

Другие решения

Если я правильно понял, какова ваша функция flatmap должен сделать, вы можете написать это как v | view::transform(fn) | action::join, Вот пример того, как сделать это с диапазонами:

#include <range/v3/all.hpp>

#include <iostream>
#include <string>
#include <utility>
#include <vector>// flatmap: [A] -> (A->[B]) -> [B]
template<typename T, typename FN>
static auto flatmap(const std::vector<T> &vec, FN fn)
-> std::vector<typename std::remove_reference<decltype(fn(T())[0])>::type> {
std::vector<typename std::remove_reference<decltype(fn(T())[0])>::type> result;
for(auto x : vec) {
auto y = fn(x);
for( auto v : y ) {
result.push_back(v);
}
}
return result;
};

// This will be test function for both flatmap and range usage
std::vector<std::string> testFn(int n)
{
std::vector<std::string> result;
int ofs = 0;
for(int i = 0; i < n; ++i)
{
char initialChar = 'A' + ofs;
std::string partialResult = "\"";
for(int j = 0; j <=i; ++j, ++ofs)
{
partialResult.append(1, initialChar+j);
}
partialResult += "\"";
result.push_back(partialResult);
}
return std::move(result);
}

int main(int, char**)
{
std::vector<int> vv {1, 2, 3, 4, 5, 6};
// test flatmap
auto r2 = flatmap(vv, testFn);
for(auto s:r2)
{
std::cout << s << " " ;
}
std::cout << "\n";

using namespace ranges;

// test ranges equivalent
auto rng = vv|view::transform(testFn)|action::join;
//         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this is an equivalent for flatmap in ranges terms

for(auto s:rng)
{
std::cout << s << " ";
}
std::cout << "\n";

std::cout << std::flush;
return 0;
}
2

Оба ответа верны, но я хотел добавить немного больше контекста, так как наименование for_each может немного сбить с толку (это меня смутило). Вот пример, который вы можете использовать, чтобы убедиться, что view::for_each на самом деле это flatMap диапазона:

#include <range/v3/all.hpp>
#include <iostream>
#include <vector>
using namespace ranges;

int main()
{
const std::vector<int> a = { 0, 1, 2 };
auto b = a | view::for_each([] (int x) { return view::ints(x, x+3); });

ranges::for_each( b, [] (int x) { std::cout << x << " "; } );
std::cout << std::endl;
}

Это напечатает 0 1 2 1 2 3 2 3 4, В примере также показан возможный источник путаницы, поскольку на самом деле в пространстве имен диапазонов есть функция for_each, функциональность которой аналогична, например, функции. Java forEach (то есть применение функции к каждому члену диапазона без возвращаемого значения).

Если вы посмотрите на документация view::for_each Вы видите, что он реализован с использованием transform и join.

 auto   operator() (Rng &&rng, Fun fun) const -> decltype(join(transform(static_cast< Rng &&>(rng), std::move(fun))))
1
По вопросам рекламы [email protected]