Created
March 18, 2012 21:32
-
-
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>.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
} |
Second Commit
- SHA: 3e8d6467d863b47b2723cc9fc8882f828f88fabd
- Replaced
minimum_common_size
withshortest_size
. Only used 2 overloads instead of 3 (because I'm still getting used to C++11). - Added
min_v
functions to separate concerns fromshortest_size
and make both function groups more generic.
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 usezip_with_n
. - Changed
zipWith
to help indicate that the requirements for containers is that they support thesize
andbegin
member functions, while the printing function needs thebegin
andend
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 andstd::forward_list
, can usezip_with
.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
First Commit
I used GCC 4.6.2 (from MacPorts) on a Mac OS X 10.4.11/PowerPC system to write and test the code.