1

I would like to do something like:

vector<int> v;
v.push_back(0);
v.push_back(1);

v
    .transform([](auto i) { return i + 2; })
    .transform([](auto i) { return i * 3; })
    .inner_product(0);

In other words, just implicitly use begin() and end() for the first and last iterators and chain the results.

Is there anything (eg some library) that would allow this?

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
Noel Yap
  • 18,822
  • 21
  • 92
  • 144
  • 2
    If it's anywhere, it'd be in [Boost::range](http://www.boost.org/doc/libs/1_63_0/libs/range/doc/html/index.html) – Mooing Duck Apr 10 '17 at 21:28
  • 1
    Also, inner_product takes two ranges: http://en.cppreference.com/w/cpp/algorithm/inner_product – Mooing Duck Apr 10 '17 at 21:35
  • @MooingDuck, `boost::algorithm::join` is what I actually want to do. – Noel Yap Apr 10 '17 at 23:29
  • Sounds like a dupe of [this question](http://stackoverflow.com/questions/16964353/can-boostalgorithmjoin-concat-a-container-of-floats) – Mooing Duck Apr 11 '17 at 00:07
  • There is not such thing as `std::vector::transform`, you have to use (and reuse) iterators, or ranges (Boost or Pstade.Oven) for concatenation. – alfC Apr 11 '17 at 00:09

2 Answers2

0

Just write your own class to augment std::vector.

#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>
#include <utility>

template < typename T >
class augmented_vector
{
  std::vector < T > m_vec;
public:
  augmented_vector() : m_vec() {}
  explicit augmented_vector(std::vector<T> const& in) : m_vec(in) {}

  void push_back ( T&& value )
  {
    m_vec.push_back(std::forward<T>(value));
  }

  template < typename F >
  augmented_vector < T > transform(F const& f)
  {
    std::vector < T > new_vec(m_vec.size());
    std::transform( m_vec.begin(), m_vec.end(), new_vec.begin(), f);
    return augmented_vector < T > ( new_vec );
  }

  T inner_product(T value)
  {
    return std::inner_product( m_vec.begin(), m_vec.end(), m_vec.begin(), value);
  }
};

int main()
{
  augmented_vector<int> v;
  v.push_back(0);
  v.push_back(1);

  auto val = v
    .transform([](auto i) { return i + 2; })
    .transform([](auto i) { return i * 3; })
    .inner_product(0);

  std::cout << val << '\n';
}

Or use the D programming language. It has universal function call syntax.

import std.algorithm : fold, map;
import std.stdio : writeln;

void main()
{
    auto v = [0, 1];
    auto x = v
        .map!(a => a+2)
        .map!(a => a*3)
        .fold!((a,b) => a + b^^2)(0);
    writeln(x);
}
Henri Menke
  • 10,705
  • 1
  • 24
  • 42
  • That `transform` member function makes *two* copies of the entire vector! – Daniel Jour Apr 10 '17 at 23:22
  • @DanielJour Are you sure? I think it only makes one copy (`new_vec`), but it can also be implemented to mutate the state of `augmented_vector`. However, that is something one usually doesn't expect, which is why I implemented it this way. – Henri Menke Apr 10 '17 at 23:40
  • Yes, `new_vec` as well as the member `m_vec` of the returned `augmented_vector`. – Daniel Jour Apr 11 '17 at 18:47
0

Wrap the functions, providing the boilerplate in the wrapper functions:

template<typename Container, typename Transform>
void transform_container(Container & container, Transform transform) {
  std::transform(std::begin(container), std::end(container),
                 std::begin(container), /* requires output iterator */
                 transform);
}

template<typename T, typename Container>
auto inner_product_self(Container&& container, T initial) {
  return std::inner_product(std::begin(container), std::end(container),
                            std::begin(container),
                            initial);
}

Your code then becomes:

int main() {
  std::vector<int> v(2);
  std::itoa(std::begin(v), std::end(v), 0);

  transform_container(v, [](auto i) { return i + 2; });
  transform_container(v, [](auto i) { return i * 3; });

  auto result = inner_product_self(container, 0);

  std::cout << "result: " << result;
}

(Live on ideone)

You're not chained to object oriented programming!

Daniel Jour
  • 15,896
  • 2
  • 36
  • 63
  • This is still not chaining the calls. Chaining the calls is important if the types of the containers aren't the same or if the containers themselves need to be `const`. – Noel Yap Apr 11 '17 at 16:52
  • @NoelYap Neither of those last two statements is true. If the containers are `const` or if you want to have a different container for the result, then of course you cannot (as my code does) modify the container itself. What you can easily do is write a wrapper function that uses and then returns a different container (of possibly different type). – Daniel Jour Apr 11 '17 at 18:18
  • What I would like is what languages like Groovy, Scala, and Java 8 have. Yes, the programming model is a little different in that one deals more with immutable objects. I would also like to be able to do that without having to hand-code it. – Noel Yap Apr 15 '17 at 22:28
  • AFAIK calling std::transform leads to populating the output iterator. So you're traversing the container 3 times! Plus the natural tendency is to syntactically chain it. That would allow seamlessly plugging in existing functions from algorithm library. E. g. std::find_if and std::remove. This is bullshit performance-wise and also should be syntactically chained to show the intention. Python has this by default (iterables, generator expressions). I don't know about c++, maybe the Boost::range as mentioned above. It absolutely has to part of the std and be user friendly. – Adam Aug 24 '18 at 16:15