std :: transform

时间:2015-07-17 05:14:07

标签: c++ algorithm c++11

考虑一下我为N输入迭代器编写的std :: transform的简单概括:

#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
}

我现在要做的是允许向量abc具有不同的长度(并且可以删除参数InputIterator last),哪个案例transform将继续转换,直到最长的向量用完为止,使用较短的向量的默认值。

我认为这只是调整transform函数中所有短容器的大小,但transform的参数不会提供有关所有容器的持续时间的信息。有没有办法在transform内计算每个容器的长度,从而获得最大长度,从而填充较短容器的默认值?理想情况下只使用语法:

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

更新:根据Ramana的想法,我正在考虑使用以下内容:

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()));

也没有编译(我不喜欢语法)。

3 个答案:

答案 0 :(得分:4)

#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

答案 1 :(得分:2)

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

您正在初始化:左侧的迭代器,而不是迭代器指向的类型。此外,您还有一个无限循环和未定义的行为,因为您继续递增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 :(得分:1)

这是一个基于范围的解决方案。我们不是对迭代器进行操作,而是对迭代器范围进行操作。

范围是一对带有一些帮助器的迭代器。这是一个最小的实现,只写了一些帮助程序:

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)... );
}

它们应该能够作为彼此的重载而存在。

当第一个范围结束时将其切换为停止很容易 - all_of替换any_of

live example