Skip to content

Instantly share code, notes, and snippets.

@ane
Created April 9, 2011 07:49
Show Gist options
  • Save ane/911220 to your computer and use it in GitHub Desktop.
Save ane/911220 to your computer and use it in GitHub Desktop.
// Geneerisiä C++-toteutuksia Haskellin (2-)tuplafunktioista, ks.
// http://hackage.haskell.org/packages/archive/base/4.1.0.0/doc/html/Prelude.html#19
// uuden C++ 2011 -standardin ominaisuuksista osaa käyttäen.
//
// Kääntyy GCC 4.5.1 vivulla -std=c++0x
// ja VS2010 sellaisenaan.
//
// (c) ane 2011 <ane@iki.fi> (lisenssi: bsd v3)
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
#include <tuple>
#include <string>
#include <functional>
using namespace std;
// Transforms two lists (vectors) into a list of tuples
// e.g. zipping [1,2,3,4] with ['a', 'b', 'c', 'd'] would
// yield [(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')].
template <typename First, typename Second>
vector<tuple<First, Second>> zip(vector<First>& firstList, vector<Second>& secondList)
{
vector<tuple<First, Second>> tupleVec;
auto firstIter = firstList.begin();
auto secondIter = secondList.begin();
// Assume the two lists are isomorphisms, so the resulting list
// spans the shortest one of the two.
while (firstIter != firstList.end() && secondIter != secondList.end())
{
auto newTuple = make_tuple(*firstIter, *secondIter);
tupleVec.emplace_back(newTuple);
firstIter++;
secondIter++;
}
return tupleVec;
}
// Transforms a list of tuples into two lists, where the first
// list is the list of the first values and the second list
// is the list of the second values, e.g., transforming
// [(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')]
// into
// ([1,2,3,4], ['a', 'b', 'c', 'd'])
template <typename First, typename Second>
tuple<vector<First>, vector<Second>> unzip(vector<tuple<First, Second>>& zippedList)
{
vector<First> firstList;
vector<Second> secondList;
for (auto iter = zippedList.begin(); iter != zippedList.end(); iter++)
{
First fst = get<0>(*iter);
Second snd = get<1>(*iter);
firstList.emplace_back(fst);
secondList.emplace_back(snd);
}
return make_tuple(firstList, secondList);
}
// Combines two lists using a function that takes an input pair
// consisting of the items in the first list and the items in the second list,
// fusing the two lists together using a custom function.
//
// For example, if the function is simply a * b, and the first list would be
// [1, 3, 5, 7] and the second [2, 4, 6, 8], the resulting list would be
// [2, 12, 30, 42].
//
// Keep in mind zip can be defined using this function, where func simply produces
// a tuple of the form (First, Second).
template <typename First, typename Second, typename Result>
vector<Result> zip_with(vector<First>& firstList, vector<Second>& secondList, const function<Result (First, Second)>& func)
{
auto tuples = zip(firstList, secondList);
vector<Result> resultVec;
for (auto tupleIter = tuples.begin(); tupleIter != tuples.end(); tupleIter++)
{
First fst = get<0>(*tupleIter);
Second snd = get<1>(*tupleIter);
// Apply the function on the tuple items
Result res = func(fst, snd);
resultVec.emplace_back(res);
}
return resultVec;
}
// Does the inverse of zip_with. Uses a function to parse an input value, e.g. "1:a", to generate
// two separate lists from tuples. For example, giving it a function that splits at ":" and produces a tuple
// (1, a). Thus ["1:a", "2:b", "3:c"] would turn into ([1,2,3], ["a", "b", "c"]).
template <typename First, typename Second, typename Input>
tuple<vector<First>, vector<Second>> unzip_with(vector<Input>& inputList, const function<tuple<First, Second> (Input)>& func)
{
vector<tuple<First, Second>> tuples;
for (auto inputIter = inputList.begin(); inputIter != inputList.end(); inputIter++)
{
tuple<First, Second> newTuple = func(*inputIter);
tuples.emplace_back(newTuple);
}
return unzip(tuples);
}
int main()
{
typedef tuple<int, string> MyTuple;
vector<int> intVec;
vector<string> strVec;
for (int i = 0; i < 10; i++) {
intVec.emplace_back(i);
strVec.emplace_back(string(1, i + 'a'));
}
auto zipped = zip(intVec, strVec);
auto unzipped = unzip(zipped);
// Anonymous function for converting 1 and "a" into "1:a".
auto joinWithColonFunc = [&](const int& first, const string& second) {
// FYI: VS2010 bug: http://bit.ly/fCxvC5
// To compile this on VS2010, cast first to long long.
return to_string(first) + ":" + second;
};
// Splits a string into two from "x:y", reading x as an integer
// and y as a string.
auto splitterFunc = [&](const string& item) -> MyTuple {
int wheres_my_colon = item.find(":");
string first = item.substr(0, wheres_my_colon);
string second = item.substr(wheres_my_colon + 1);
int firstVal = stoi(first); // string to integer
return make_tuple(firstVal, second);
};
// (Why does this need an explicit template parameter list?
auto zipped_with = zip_with<int, string, string>(intVec, strVec, joinWithColonFunc);
vector<string> blargVec; blargVec.emplace_back("1:a"); blargVec.emplace_back("2:b"); blargVec.emplace_back("3:b");
auto unzipped_with = unzip_with<int, string, string>(blargVec, splitterFunc);
// Mostly ugly printing stuff.
// Print the zipped list.
for_each(zipped.begin(), zipped.end(), [&](const MyTuple& tup) {
cout << "(" << get<0>(tup) << ", " << get<1>(tup) << ")" << endl; }
);
// Print the function zipped list.
cout << "["; copy(zipped_with.begin(), zipped_with.end(), ostream_iterator<string>(cout, ", ")); cout << "]" << endl;
// Print the unzipped list in a pretty format!
auto vecA = get<0>(unzipped);
auto vecB = get<1>(unzipped);
cout << "([";
copy(vecA.begin(), vecA.end(), ostream_iterator<int>(cout, ", "));
cout << "], [";
copy(vecB.begin(), vecB.end(), ostream_iterator<string>(cout, ", "));
cout << "])" << endl;
// Printing unzipped_with is omitted - it is the same as the above.
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment