2. Tuple tools

Various standard functional operations on tuple.

2.1. apply

Purpose: apply a function on a tuple of arguments

Given a function object f, and its arguments stored in a tuple t, and we want to apply f on t.

Python equivalent:

def apply(f,t):
  return f(*t)

Synopsis

template<typename Function, typename Tuple> auto apply (Function && f, Tuple const & t);

Example :

#include <triqs/utility/tuple_tools.hpp>
#include <iostream>
int main() {
  auto fun = [](int i, double x, double y, int k) { return 6 * k + i - 1.3 * x + 2 * y; };
  auto t   = std::make_tuple(1, 2.3, 4.3, 8);

  auto res = triqs::tuple::apply(fun, t);

  std::cout << " f(t) =" << res << std::endl;
}

2.2. for_each

Purpose: apply a function for each element of a tuple (in order)

Given a function object f, we want to apply it to all elements of a tuple t.

Python equivalent:

def for_each(t,f):
  for x in t:
    f(x)

Synopsis

template<typename Function, typename Tuple> void for_each(Tuple const & t, Function && f);

Example :

#include <triqs/utility/tuple_tools.hpp>
#include <iostream>

int main() {
  auto t = std::make_tuple(1, 2.3, 4.3, 8);
  auto l = [](double x) { std::cout << x << " "; };

  triqs::tuple::for_each(t, l);
}

2.3. for_each_zip

Purpose: apply a function for each element of tuple zip (in order)

Python equivalent:

def for_each(f,t0,t1):
  for x0,x1 in itertools.zip(t0,t1):
    f(x0,x1)

Synopsis

template <typename F, typename T0, typename T1>              void for_each_zip(F &&f, T0 &&t0, T1 &&t1);
template <typename F, typename T0, typename T1, typename T2> void for_each_zip(F &&f, T0 &&t0, T1 &&t1, T2 &&t2);

Example :

#include <triqs/utility/tuple_tools.hpp>
#include <iostream>

int main() {
  auto t1 = std::make_tuple(1, 2.3, 4.3, 8);
  auto t2 = std::make_tuple('|', '|', '|', '|');
  auto l  = [](double x, char c) { std::cout << c << " " << x << " "; };

  triqs::tuple::for_each_zip(l, t1, t2);
}

2.4. map

Purpose: map a function on a tuple to create a new tuple

Python equivalent:

def map(f,t):
  return (f(x) for x in t)

Synopsis

template <typename T, typename F> auto map(F &&f, T &&t);

Returns:

The result is a tuple, of the same length as T, made of the evaluation of f on the elements on T

Example :

#include <triqs/utility/tuple_tools.hpp>
#include <iostream>

int main() {
  auto t = std::make_tuple(1, 2.3, 4.3, 8);
  auto l = [](double x) { return -x; };

  auto res = triqs::tuple::map(l, t);

  std::cout << res << std::endl;
}

2.5. fold

Purpose: reduction of a tuple with a function

Python equivalent:

def fold(f,t,r):
  return reduce(f,t,r)

Synopsis

(1) template <typename F, typename T, typename R>
       decltype(auto) fold(F &&f, T &&t, R &&r);

(2) template <typename F, typename T0, typename T1, typename R>
       decltype(auto) fold(F &&f, T0 &&t0, T1 &&t1, R &&r);

Returns:

f(get<N>(t),
   f(get<N-1>(t),
      ...,
        f(get<0>(t),r)))     (1)

f(get<N>(t0), get<N>(t1),
    f(get<N-1>(t0), get<N-1>(t1),
       ...,
        f(get<0>(t0), get<0>(t1), r)))         (2)

Parameters :

  • f: a callable object of signature

    f(x, r)    -> r'      (1)
    f(x, y, r) -> r'      (2)
    

    The return type of f must be a valid last parameter for f (at least for one overload).

  • t: a tuple

  • t0,t1: two tuples of the same size

  • r: anything that can be a last parameter for f.

  • Precondition: everything so that the resulting expression is valid (in particular, f must be called on each tuple elements, with its return type as last parameter.

Warning

The type of the result is not necessarly R: it is automatically deduced from this expression. Cf example.

Example :

#include <triqs/utility/tuple_tools.hpp>
#include <iostream>

int main() {
  auto t = std::make_tuple(1, 2.3, 4.3, 8);

  // a simple example : make a sum
  auto f   = [](double x, double r) { return x + r; };
  auto res = triqs::tuple::fold(f, t, 0); // <-- R is int but res is a double (from f's return type).

  std::cout << res << std::endl;

  // the types of x and r may be different
  triqs::tuple::fold([](double x, std::ostream &os) -> std::ostream & { return os << "|" << x; }, t, std::cout);

  // example with zip
  auto t0 = std::make_tuple(1, 2, 3, 4);
  auto t1 = std::make_tuple(1, -1, 1, -1);

  auto res2 = triqs::tuple::fold([](double x, double y, double r) { return y * x + r; }, t0, t1, 0);

  std::cout << "\n " << res2 << std::endl;
}

2.6. reverse

Purpose: lazy reverse of a tuple

Python equivalent: None.

Synopsis

namespace std {
 template<typename ... T> TU  reverse(std::tuple<T...> && x);
 template<typename ... T> TU  reverse(std::tuple<T...> & x);
 template<typename ... T> TU  reverse(std::tuple<T...> const& x);
}

Warning

reverse is declared in std:: to benefit from ADL (a bit dangerous, but ok here).

Returns:

TU is a tuple like type, that :

  • Contains a ref of the original tuple, or the tuple if a && was passed.

  • Hence, no copy is ever made.

  • Accepts std::get and std::tuple_size, like tuple.

reverse(t) can therefore be used in place of a regular tuple in the algorithms of this section.

Example :

#include <triqs/utility/tuple_tools.hpp>
#include <iostream>

int main() {
  auto t = std::make_tuple(1, 2.3, 4.3, 8);
  auto l = [](double x) { std::cout << x << " "; };

  triqs::tuple::for_each(t, l);
  std::cout << std::endl;
  triqs::tuple::for_each(reverse(t), l);
}

2.7. called_on_tuple

Purpose: Adapting a function to call with a tuple argument and flatten it

Python equivalent:

def called_on_tuple(f):
    return lambda x: f(*x)

Synopsis

template <typename F> F2 called_on_tuple(F &&f);

Returns:

F2 is a function object which adapts the function f for calling on a tuple.

The following call are therefore equivalent:

called_on_tuple(f)( std::tie(x0,x1,x2))

f(x0,x1,x2)

Example :

#include <triqs/utility/tuple_tools.hpp>
#include <iostream>
int main() {
  auto fun = [](int i, double x, double y, int k) { return 6 * k + i - 1.3 * x + 2 * y; };
  auto t   = std::make_tuple(1, 2.3, 4.3, 8);

  auto res = triqs::tuple::called_on_tuple(fun)(t);

  std::cout << " f(t) =" << res << std::endl;
}
 f(t) =54.61

Implementation :

The C++ is simple in fact

template <typename F> struct _called_on_tuple {
 F _f;
 template <typename Tu> decltype(auto) operator()(Tu &&tu) {
   return apply(_f, std::forward<Tu>(tu));
 }
};

template <typename F> _called_on_tuple<F> called_on_tuple(F &&f) {
 return {std::forward<F>(f)};
}