The code of this article builds on the code of the prior.

#### Zipping.

If we have several tuples, what if we want to apply a function to the nth element of each one?template< template<size_t> class Fi = Get, size_t i, class F, class ...T > constexpr auto zipRow( const F& f, T&& ...t ) -> decltype( f(Fi<i>()(std::forward<T>(t))...) ) { return f( Fi<i>()( std::forward<T>(t) )... ); }

Not hard at all! It basically squishes that row (thinking of

*t*as a column and

*ti...*as a row), using

*f*, into one value. Now, let's say we want to zip together

*t...*into one tuple.

template< template<size_t> class Fi = Get, size_t ...i, class Ret, class F, class ...T > constexpr auto zipIndexList( IndexList<i...>, const Ret& r, const F& f, T&& ...t ) -> decltype( r(zipRow<Fi,i>(f,std::forward<T>(t)...)...) ) { return r( zipRow< Fi, i >( f, std::forward<T>(t)... )... ); } template< template<size_t> class Fi = Get, class Ret, class F, class T, class ...U, class _T = typename std::decay<T>::type, class IL = typename IListFrom<_T>::type > constexpr auto zipTupleTo( const Ret& r, const F& f, T&& t, U&& ...u ) -> decltype( zipIndexList<Fi>(IL(),r,f,std::forward<T>(t),std::forward<U>(u)...) ) { return zipIndexList<Fi>( IL(), r, f, std::forward<T>(t), std::forward<U>(u)... ); } int main() { auto zipped = zipTupleTo( tuple, std::plus<int>(), tuple(1,10), tuple(2,20) ); std::cout << " 1 + 2 = " << std::get<0>(zipped) << std::endl; std::cout << "10 + 20 = " << std::get<1>(zipped) << std::endl; }

In

*zipIndexList*,

*r*represents the function defining how the output is returned.

*tuple*(gist), from the previous article, is just a function object form of

*std::make_tuple*that can be passed to higher order functions. By supplying it as our

*r*, we're saying "just make it a tuple again."

Since most often, we want to zip back into a tuple, it makes sense to define

*zipTuple*like so:

template< template<size_t> class Fi = Get, class F, class ...T > constexpr auto zipTuple( const F& f, T&& ...t ) -> decltype( zipTupleTo<Fi>(tuple,f,std::forward<T>(t)...) ) { return zipTupleTo<Fi>( tuple, f, std::forward<T>(t)... ); }

*zipTuple*is to tuples what

*std::transform*is to sequences. The drawback of

*std::transform*is that it only allows for a unary transformation or binary. Let's write a version that accepts any number of arguments.

// We require these polymorphic function objects. constexpr struct Inc { template< class X > constexpr X operator () ( X x ) { return ++x; } } inc{}; constexpr struct Eq { template< class X > constexpr bool operator () ( const X& a, const X& b ) { return a == b; } } eq{}; struct Or { template< class X > constexpr bool operator () ( const X& a, const X& b ) { return a || b; } }; // Wrapper to dereference arguments before applying. // indirect : (a -> b) -> (a* -> b) template< class F > struct Indirect { F f = F(); constexpr Indirect( F f ) : f(std::move(f)) { } template< class ...It > constexpr auto operator () ( It ...it ) -> decltype( f(*it...) ) { return f( *it... ); } }; template< class F, class I = Indirect<F> > constexpr I indirect( F f ) { return I( std::move(f) ); } #include <vector> #include <algorithm> template< class F, class ...X, class Result = typename std::result_of<F(X...)>::type, class Ret = std::vector<Result> > Ret transform( const F& f, const std::vector<X>& ...vs ) { Ret r; const auto ends = tuple( vs.end()... ); // Iterate through each vector in parallel. for( auto its = tuple( vs.begin()... ); // This unrolls to: not (it0==end0 || it1==end1 || ...) not foldl( Or(), zipTuple(eq,its,ends) ); // Increment each iterator. its = zipTuple( inc, its ) ) { r.emplace_back ( applyTuple( indirect(f), its ) ); } return r; } int main() { std::vector<int> v = {1,10,100}, w = {2,20,200}, x = {3,30,300}; auto vw = transform ( [](int x, int y, int z){ return x+y+z; }, v, w, x ); std::cout << " 1 + 2 + 3 = " << vw[0] << std::endl; std::cout << " 10 + 20 + 30 = " << vw[1] << std::endl; std::cout << "100 + 200 + 300 = " << vw[2] << std::endl; }

*Note: foldl (gist).*

#### Mapping.

Suppose we want to know the results of adding every combination of {1,2,3} with {9,8,7}. We could write a function that cross-applied every variable from each tuple, but slightly more generally, we can start by taking the Cartesian product.// product : {x,y} x {a,b} -> {{x,a},{x,b},{y,a},{y,b}} constexpr struct Product { // {...xi...} x {...aj...} -> {xi,aj} template< size_t i, size_t j, class T, class U > static constexpr auto zip( const T& t, const U& u ) -> decltype( tuple(std::get<i>(t),std::get<j>(u)) ) { return tuple( std::get<i>(t), std::get<j>(u) ); } // {...xi...} x {a0,a1,a2...} -> { {xi,a0}, {xi,a1}, ... } template< size_t i, size_t ...j, class T, class U > static constexpr auto withI( IndexList<j...>, const T& t, const U& u ) -> decltype( tuple(zip<i,j>(t,u)...) ) { return tuple( zip<i,j>(t,u)... ); } // {x...} x {a...} -> { {x,a}... } template< size_t ...i, size_t ...j, class T, class U > static constexpr auto withIndexes( IndexList<i...>, IndexList<j...> js, const T& t, const U& u ) -> decltype( std::tuple_cat(withI<i>(js,t,u)...) ) { return std::tuple_cat( withI<i>(js,t,u)... ); } template< class T, class U, class IL = typename IListFrom<T>::type, class IL2 = typename IListFrom<U>::type > constexpr auto operator () ( const T& t, const U& u ) -> decltype( withIndexes(IL(),IL2(),t,u) ) { return withIndexes( IL(), IL2(), t, u ); } } product{};We can now define a map operation to apply the product.

template< class F > struct ApplyF { F f = F(); constexpr ApplyF( F f ) : f(std::move(f)) { } template< class T > constexpr auto operator () ( T&& t ) -> decltype( applyTuple(f,std::forward<T>(t)) ) { return applyTuple( f, std::forward<T>(t) ); } }; template< class F > constexpr ApplyF<F> applyF( F f ) { return ApplyF<F>(std::move(f)); } constexpr struct MapTuple { template< class F, class T, class U > constexpr auto operator () ( const F& f, const T& t, const U& u ) -> decltype( zipTuple(applyF(f),product(t,u)) ) { return zipTuple( applyF(f), product(t,u) ); } } mapTuple{}; int main() { auto sums = mapTuple( std::plus<int>(), tuple(1,2,3), tuple(7,8,9) ); std::cout << "map (+) (1,2,3) (7,8,9) = "; forEach( printItem, sums ); std::cout << std::endl; }

This prints out:

map (+) (1,2,3) (7,8,9) = 8 9 10 9 10 11 10 11 12

Zipping applies elements across from each other. Mapping applies everything to everything. (

*Note: a unary definition of map would be equivalent to a unary definition of zip.*)

#### Tuples as function environments.

This might seem a little off topic, but Haskell has this neat function,*id*. It works like this:

*id x = x*

Simple, right?

*(id f) x y = f x y = id f x y*

*id*has this neat property that if applied multiple arguments, it applies the tail arguments to the first. This is an artifact of Haskell's curried notation, but we can emulate this behaviour:

constexpr struct Id { template< class X > constexpr X operator () ( X&& x ) { return std::forward<X>(x); } template< class F, class X, class ...Y > constexpr auto operator () ( const F& f, X&& x, Y&& ...y ) -> typename std::result_of< F(X,Y...) >::type { return f( std::forward<X>(x), std::forward<Y>(y)... ); } } id{};And now tuples take on a new role: Contained function environments. Consider:

*applyTuple( id, tuple(std::plus<int>(),1,2) );*

What does this output? How about

*mapTuple( id, tuple(inc,dec), tuple(5,9) );*

*auto pm =*

*tuple(std::plus<int>(),std::minus<int>());**zipTuple( id, pm, tuple(10,5), tuple(10,5) );*

*Or:*

*mapTuple( id, pm, tuple(1,2), tuple(3,4);*

I leave implementing the three-tuple version of

*mapTuple*as an exercise, but here's a hint:

*cross( cross({f},{x}), {y}) = {{{f,x},{y}}}*, but you need to take it from

*that*to

*{{f,x,y}}*. (Another good exercise might be to write

*zipTuple*in terms of transposition (wiki).)

#### Conclusions.

This flushes out some basic applications of tuples to functions.*applyTuple*unpacks a tuple and applies it to a function.

*foldl*and

*foldr*let one apply binary functions to nary tuples, or even singletons (maths concept, not design pattern).

*zipTuple*transforms multiples tuples by a functions, member-wise.

*mapTuple*performs a function for every combination of arguments.

Tuples have unusual mathematical properties compared to other data structures due to the profundity of what they generalize. They can help us shorthand functions to operate in parallel (

*zip*), be passed around as partial or complete function environments, control variadic template parameters, and much, much more that I have either not discussed or yet realized.

One use I haven't discussed, for example, is as a relation, but for an example of this use of tuples, look no further than

*std::map.*

I hope this post has been interesting. Happy coding!

**Source for this article:**https://gist.github.com/4268029