rangeless::fn
Transform and Adapt

Functions

template<typename F >
impl::adapt< F > rangeless::fn::adapt (F fn)
 Create a custom processing-stage function-object. More...
 
template<typename F >
impl::transform< F > rangeless::fn::transform (F map_fn)
 Create a seq yielding results of applying the transform functions to input-elements. More...
 

Detailed Description

Function Documentation

◆ adapt()

template<typename F >
impl::adapt<F> rangeless::fn::adapt ( fn)

Create a custom processing-stage function-object.

This is somewhat similar to fn::transform, except the correspondence between inputs and outputs is not necessarily 1:1, and instead of taking a single element to transform we take a nullary generator callable gen and use it to fetch however many input elements we need to generate the next output element.

NB: If bool(gen)==false, the next invocation of gen() shall throw fn::end_seq::exception signaling end-of-inputs.

auto my_transform = [](auto fn)
{
return fn::adapt([fn = std::move(fn)](auto gen)
{
return fn(gen());
});
};
auto my_where = [](auto pred)
{
return fn::adapt([pred = std::move(pred)](auto gen)
{
auto x = gen();
while(!pred(x)) {
x = gen();
}
return x;
});
};
auto my_take_while = [](auto pred)
{
return fn::adapt([pred = std::move(pred)](auto gen)
{
auto x = gen();
return pred(x) ? std::move(x) : fn::end_seq();
});
};
auto my_intersperse = [](auto delim)
{
#if 1
return [delim = std::move(delim)](auto inputs)
{
return fn::seq([ delim,
inputs = std::move(inputs),
it = inputs.end(),
started = false,
flag = false]() mutable
{
if(!started) {
started = true;
it = inputs.begin();
}
return it == inputs.end() ? fn::end_seq()
: (flag = !flag) ? std::move(*it++)
: delim;
});
};
#elif 0 // or
return [delim = std::move(delim)](auto inputs)
{
return std::move(inputs)
% fn::transform([delim](auto inp)
{
return std::array<decltype(inp), 2>{{ std::move(inp), delim }};
})
% fn::drop_last(); // drop trailing delim
};
#else // or
return fn::adapt([delim, flag = false](auto gen) mutable
{
return !gen ? fn::end_seq()
: (flag = !flag) ? gen()
: delim;
});
#endif
};
auto my_inclusive_scan = []
{
return fn::adapt([sum = 0](auto gen) mutable
{
return sum += gen();
});
};
auto res =
fn::seq([i = 0]() mutable
{
return i++;
}) // 0,1,2,3,...
% my_where([](int x)
{
return x >= 3;
}) // 3,4,5,...
% my_take_while([](int x)
{
return x <= 5;
}) // 3,4,5
% my_intersperse(-1) // 3,-1,4,-1,5
% my_transform([](int x)
{
return x + 1;
}) // 4,0,5,0,6
% my_inclusive_scan() // 4,4,9,9,15
VERIFY((res == vec_t{{4, 4, 9, 9, 15}}));

A more realistic example: suppose you have a pipeline transforming inputs to outputs in parallel, and you want to compress the output, but the outputs are small and compressing them individually would be ineffective, and you want to serialize the incoming results into a buffer of some minimum size, e.g. 100kb, before passing it to the compressing stage.

auto make_result = [](std::string s){ return s; };
auto compress_block = [](std::string s){ return s; };
auto write_to_ostr = [](std::string s){ std::cout << s; };
namespace fn = rangeless::fn;
using fn::operators::operator%;
fn::seq([&]() -> std::string
{
std::string line;
return std::getline(std::cin, line) ? std::move(line)
: fn::end_seq();
})
% fn::adapt([](auto get_next)
{
std::ostringstream buf{};
while(get_next && buf.tellp() < 100000) {
buf << get_next();
}
return buf.tellp() ? buf.str() : fn::end_seq();
})
% fn::transform_in_parallel(compress_block)
% fn::for_each(write_to_ostr);

Definition at line 3657 of file fn.hpp.

◆ transform()

template<typename F >
impl::transform<F> rangeless::fn::transform ( map_fn)

Create a seq yielding results of applying the transform functions to input-elements.

Returns a unary function-object, which will capture the arg by value and return an adapted seq<...> which will yield results of invoking map_fn on the elements (passed by value) of arg. See fn::where for an example.

Definition at line 3670 of file fn.hpp.