Std :: transform generalization

Consider this simple generalization of std :: transform that I wrote for N input iterators:

#include <iostream>
#include <vector>
#include <string>

template <typename InputIterator, typename OutputIterator, typename NaryOperator, typename... InputIterators>
OutputIterator transform (InputIterator first, InputIterator last, OutputIterator result,
NaryOperator op, InputIterators... iterators) {
    while (first != last) {
        *result = op(*first, *iterators++...);
        ++result;  ++first;
    }
    return result;
}

int main() {
    const std::vector<int> a = {1,2,3,4,5};
    const std::vector<double> b = {1.2, 4.5, 0.6, 2.8, 3.1};
    const std::vector<std::string> c = {"hi", "howdy", "hello", "bye", "farewell"};
    std::vector<double> result(5);
    transform (a.begin(), a.end(), result.begin(),
        [](int i, double d, const std::string& s)->double {return i + d + s.length();},
        b.begin(), c.begin());
    for (double x : result) std::cout << x << ' ';  // 4.2 11.5 8.6 9.8 16.1
}

Now I want to vectors a, b, chave different lengths (and the argument InputIterator lastcan be removed), and in this case transformwill continue to convert until the longest vector is used, using default values for shorter vectors.

, transform, transform , . transform, , ? :

transform (OutputIterator result, NaryOperator op, InputIterators... iterators);

: , - :

template <typename OutputIterator, typename NaryOperator, typename... InputIterators>
OutputIterator transform (OutputIterator result, NaryOperator op, InputIterators... first, InputIterators... last) {
    while (true) {
        *result = op((first == last ?
            typename std::iterator_traits<InputIterators>::value_type() : *first++)...);
        ++result;
    }
    return result;
}

transform (result.begin(),
    [](int i, double d, const std::string& s)->double {return i + d + s.length();},
    a.begin(), b.begin(), c.begin(), a.end(), b.end(), c.end());

. , , last....

, :

template <typename OutputIterator, typename NaryOperator, typename... InputIteratorsPairs>
OutputIterator transform (OutputIterator result, NaryOperator op, InputIteratorsPairs... pairs) {
    while (true) {
        *result = op((pairs.first == pairs.second ?
            typename InputIteratorsPairs::first_type() : *pairs.first++)...);
        ++result;
    }
    return result;
}

transform_ (result.begin(),
    [](int i, double d, const std::string& s)->double {return i + d + s.length();},
    std::make_pair(a.begin(), a.end()), std::make_pair(b.begin(), b.end()), std::make_pair(c.begin(), c.end()));

( ).

+4
3
#include <cstddef>
#include <utility>
#include <tuple>
#include <iterator>

bool all(bool a)
{
    return a;
}

template <typename... B>
bool all(bool a, B... b)
{
    return a && all(b...);
}

template <typename OutputIterator, typename NaryOperator, typename... InputIterators, std::size_t... Is>
OutputIterator transform(OutputIterator result, NaryOperator op, std::index_sequence<Is...>, InputIterators... iterators)
{
    auto tuple = std::make_tuple(iterators...);
    while (!all(std::get<2*Is>(tuple) == std::get<2*Is + 1>(tuple)...))
    {
        *result = op((std::get<2*Is>(tuple) != std::get<2*Is + 1>(tuple)
              ? *std::get<2*Is>(tuple)++
              : typename std::iterator_traits<typename std::tuple_element<2*Is, decltype(tuple)>::type>::value_type{})...);
        ++result;
    }
    return result;
}

template <typename OutputIterator, typename NaryOperator, typename... InputIterators>
OutputIterator transform(OutputIterator result, NaryOperator op, InputIterators... iterators)
{
    return transform(result, op, std::make_index_sequence<sizeof...(InputIterators)/2>{}, iterators...);
}

:

int main()
{
    const std::vector<int> a = {1,2,3,4,5};
    const std::vector<double> b = {1.2, 4.5, 0.6, 2.8, 3.1};
    const std::vector<std::string> c = {"hi", "howdy", "hello", "bye", "farewell"};
    std::vector<double> result(5);

    transform(result.begin(),
        [] (int i, double d, const std::string& s) -> double
        {
            return i + d + s.length();
        },
        a.begin(), a.end(),
        b.begin(), b.end(),
        c.begin(), c.end());

    for (double x : result) std::cout << x << ' ';
}

:

4.2 11.5 8.6 9.8 16.1 

DEMO

+4
pairs.first == pairs.second ?
        typename InputIteratorsPairs::first_type() : *pairs.first++

: , . undefined, result. , ( <algorithm> :

bool any(std::initializer_list<bool> vs)
{
    return std::any_of(begin(vs), end(vs), [](bool b) { return b; });
}

template<typename OutputIterator, typename NaryOperator, typename... InputIteratorsPairs>
OutputIterator transform(OutputIterator result, NaryOperator op, InputIteratorsPairs... pairs) {
    while (any({(pairs.first != pairs.second)...})) {
        *result = op((pairs.first == pairs.second ?
            typename InputIteratorsPairs::first_type::value_type() : *pairs.first++)...);
        ++result;
    }
    return result;
}
+2

. .

- . , :

template<class It> using it_value_type =
  typename std::iterator_traits<It>::value_type;
template<class It> using it_reference =
  typename std::iterator_traits<It>::reference;

template<class It>
struct range_t {
  It b, e;
  range_t():b(),e(){}
  range_t(It s, It f):b(s),e(f){}
  template<class C, class=std::enable_if_t<!std::is_same<std::decay_t<C>,range_t>{}>>
  range_t(C&& c):
    range_t(std::begin(c),std::end(c))
  {}
  It begin() const { return b; }
  It end() const { return e; }

  bool empty() const { return begin()==end(); }
  it_reference<It> front() const { return *begin(); }
  it_reference<It> back() const { return *std::prev(end()); }

  range_t pop_front() const {
    if (empty()) return {};
    else return {std::next(begin()), end()};
  }
};

, range_t:

template<class C, class It=std::decay_t<decltype(std::begin(std::declval<C&>()))>>
range_t<It> range( C&& c ) {
  return {std::begin(c), std::end(c)};
}

, bool, , :

bool all_of( std::initializer_list<bool> il ) {
  return std::all_of( il.begin(), il.end(), [](bool b){return b;} );
}

. :

template<class Sink, class Operator, class...Its>
Sink transform( Sink sink, Operator op, range_t<Its>... srcs ) {
  while(!all_of({srcs.empty()...})) {
    *sink++ = op( (srcs.empty()?it_value_type<Its>{}:srcs.front())... );
    using discard=int[];
    (void)discard{0,
      ((srcs = srcs.pop_front()),0)...
    };
  }
  return sink;
}

, , :

template<class Sink, class Operator, class...Srcs>
Sink transform( Sink sink, Operator op, Srcs&&... srcs ) {
  return transform( sink, op, range(srcs)... );
}

.

Switching to a stop when the first range ends is swap all_offor any_of.

living example .

+1
source

Source: https://habr.com/ru/post/1598322/


All Articles