Skip to content

Instantly share code, notes, and snippets.

@CTMacUser
Created March 18, 2012 21:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CTMacUser/2081658 to your computer and use it in GitHub Desktop.
Save CTMacUser/2081658 to your computer and use it in GitHub Desktop.
Improved version of my sample code from <http://stackoverflow.com/a/9736489/1010226>.
// Taken from the final code I provided as an answer to a StackOverflow query
// at <http://stackoverflow.com/a/9736489/1010226>.
// Copyright 2012 by Daryle Walker
#include <algorithm> // min
#include <cassert> // assert
#include <cmath> // pow
#include <cstddef> // size_t
#include <iostream> // cout
#include <iterator> // back_inserter, begin, end
#include <ostream> // basic_ostream, endl
#include <type_traits> // common_type
#include <utility> // forward
#include <vector> // vector
// Return the minimum value among the values given as (C++11) vararg input.
// There must be at least one value, and there must be a shared type all the
// values can convert to.
template < typename T >
inline T min_v( T const &x ) { return x; }
template < typename T1, typename T2, typename ...MoreT >
auto min_v( T1 const &x1, T2 const &x2, MoreT const& ...more )
-> typename std::common_type<T1, T2, MoreT...>::type
{
return std::min<decltype( min_v(x1, x2, more...) )>(x1, min_v(x2, more...));
}
// Return the smallest size (i.e. element length) of all the containers
// given as (C++11) vararg input. All containers must support a "size"
// member function that works like the STL container requirements.
inline std::size_t shortest_size() { return 0u; }
template < class C, class ...MoreC >
inline auto shortest_size( C const &first, MoreC const& ...more )
-> decltype( min_v(first.size(), more.size()...) )
{ return min_v( first.size(), more.size()... ); }
// Another variant of the TC's function that uses iterators plus distance
// instead of containers.
template < typename OutIter, typename Size, typename Func, typename ...InIter >
OutIter zip_with_n( OutIter o, Size count, Func&& f, InIter ...i )
{
assert( count >= Size{} );
while ( count-- )
*o++ = f( *i++... );
return o;
}
// A variant of the TC's function that is more compliant with the STL's
// iterator-based output philosophy.
template < typename OutIter, typename Func, class ...Container >
inline OutIter zip_with( OutIter o, Func&& f, Container&& ...c )
{
return zip_with_n( o, shortest_size(c...), std::forward<Func>(f),
c.begin()... );
}
// The function the TC asked about. The interface is more refined (i.e.
// generic) from what s/he originally presented. But anything using the
// original interface should still work here.
template < typename Func, class ...Container >
auto zipWith( Func&& func, Container&& ...c )
-> std::vector<decltype( func(*c.begin()...) )>
{
using std::forward;
decltype( zipWith(forward<Func>( func ), forward<Container>( c )...) )
result;
#if 1
// `std::vector` is the only standard container with the `reserve`
// member function. Using it saves time when doing multiple small
// inserts, since you'll do reallocation at most (hopefully) once.
// The cost is that the constrained-length is already computed within
// `zip_with`, but we can't get at it. (Remember that most container
// types wouldn't need it.) Change the preprocessor flag to change
// the trade-off.
result.reserve( shortest_size(c...) );
#endif
zip_with( std::back_inserter(result), forward<Func>(func),
forward<Container>(c)... );
return result;
}
// Container-printer
template < typename Ch, class Tr, typename Cnt >
std::basic_ostream<Ch, Tr> &
print_container( std::basic_ostream<Ch, Tr> &o, Cnt &&c )
{
using std::begin;
using std::end;
auto cb = begin( c );
auto const ce = end( c );
o << '{';
if ( ce != cb )
{
o << *cb;
while ( ce != ++cb )
o << ',' << ' ' << *cb;
}
return o << '}';
}
// Demonstration engine
int main( int, char const *[] )
{
using std::vector;
using std::cout;
using std::endl;
unsigned test[] = { 2, 3, 5, 7, 11 };
vector<double> bases = { 3.0, 0.5, 0.25, 9.0 };
vector<int> exps = { -2, 2, -4, 3, -500 };
print_container( cout << "Test printing: ", test ) << endl;
print_container( cout << "Test zipWith with uneven vectors for "
"std::pow: ", zipWith(( double(*)(double, int) )std::pow, bases, exps) )
<< endl;
return 0;
}
@CTMacUser
Copy link
Author

First Commit

  • SHA: b323597d1a766c9cee6c9261e4b9b81dcf3e225d
  • Copy of the code from the StackOverflow post.
  • Driver code and a container-printing function were added.
  • Heading comments were added to each function or function group.

I used GCC 4.6.2 (from MacPorts) on a Mac OS X 10.4.11/PowerPC system to write and test the code.

@CTMacUser
Copy link
Author

Second Commit

  • SHA: 3e8d6467d863b47b2723cc9fc8882f828f88fabd
  • Replaced minimum_common_size with shortest_size. Only used 2 overloads instead of 3 (because I'm still getting used to C++11).
  • Added min_v functions to separate concerns from shortest_size and make both function groups more generic.

@CTMacUser
Copy link
Author

Third Commit

  • SHA: e9dfc67988e44ce48aadb31db8a56175f4a9f738
  • Added zip_with_n, that now does the core zipping code. I wanted to switch from container inputs to input-iterator inputs. I worried how to carry the begin- and end-points together, but I don't need to if I use begin-points and distances, and I already have to compute the smallest size (i.e. distance) that all the iterators could support.
  • Since it is no longer core, changed implementation of zip_with to now use zip_with_n.
  • Changed zipWith to help indicate that the requirements for containers is that they support the size and begin member functions, while the printing function needs the begin and end free-functions. All four functions need to meet their expected STL container requirements. I think all the containers in Standard C++(2011), except built-in arrays and std::forward_list, can use zip_with.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment