Created
September 21, 2018 15:03
-
-
Save ruffianeo/b95e7ebfaa1e5ce09c2e55943e6201c9 to your computer and use it in GitHub Desktop.
Lazy C++ with stateful Enumerator and referentially transparent lambdas. FizzBuzz included ;)
This file contains 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
#include "stdafx.h" | |
#include <cstdint> | |
#include <functional> | |
#include <stdexcept> | |
#include <string> | |
#include <tuple> | |
#include <iostream> | |
#include <limits> | |
#include <vector> | |
#include <sstream> | |
template <class T, class S> | |
class Enumerator | |
{ | |
public: | |
typedef typename S State_t; | |
typedef typename T Value_t; | |
typedef std::function< | |
std::tuple<bool, State_t, Value_t> | |
(const State_t& | |
) | |
> Worker_t; | |
Enumerator(Worker_t worker, State_t s0) | |
: m_worker(worker) | |
, m_state(s0) | |
, m_value{} | |
{ | |
} | |
operator bool() const | |
{ | |
return m_active; | |
} | |
std::tuple<bool,S,T> | |
step(const S& state) const | |
{ | |
return m_worker(state); | |
} | |
State_t state() const | |
{ | |
return m_state; | |
} | |
Worker_t worker() const | |
{ | |
return m_worker; | |
} | |
template <class T1, class S1> | |
auto | |
zip | |
( Enumerator<T1, S1> other | |
) -> Enumerator<std::tuple<T, T1>, std::tuple<S, S1> > | |
{ | |
auto worker0 = this->m_worker; | |
auto worker1 = other.worker(); | |
auto combine = | |
[worker0,worker1](std::tuple<S, S1> state) -> | |
std::tuple<bool, std::tuple<S, S1>, std::tuple<T, T1> > | |
{ | |
auto[s0, s1] = state; | |
auto[active0, newS0, v0] = worker0(s0); | |
auto[active1, newS1, v1] = worker1(s1); | |
return std::make_tuple | |
( active0 && active1 | |
, std::make_tuple(newS0, newS1) | |
, std::make_tuple(v0, v1) | |
); | |
}; | |
return Enumerator<std::tuple<T, T1>, std::tuple<S, S1> > | |
( combine | |
, std::make_tuple(m_state, other.state()) | |
); | |
} | |
template | |
< class FT | |
, class EnumT | |
, class R = FT::result_type | |
, class T1 = EnumT::Value_t | |
, class S1 = EnumT::State_t | |
, class S2 = std::tuple<S, S1> | |
> | |
auto | |
zipWith | |
( FT zipper | |
, const EnumT& other | |
) -> Enumerator<R, S2 > | |
{ | |
auto worker0 = this->m_worker; | |
auto worker1 = other.m_worker; | |
auto zipOneWith = | |
[worker0, worker1, zipper](const S2& state) | |
-> std::tuple<bool, std::tuple<S, S1>, R> | |
{ | |
auto[s0, s1] = state; | |
auto[active0, newS0, v0] = worker0(s0); | |
auto[active1, newS1, v1] = worker1(s1); | |
return std::make_tuple | |
( active0 && active1 | |
, std::make_tuple(newS0, newS1) | |
, zipper(v0, v1) | |
); | |
}; | |
return Enumerator<R, std::tuple<S, S1> > | |
( zipOneWith | |
, std::make_tuple(m_state,other.m_state) | |
); | |
} | |
template <class T1> | |
auto | |
map | |
( std::function<T1(const T&)> mapper | |
) -> Enumerator<T1, S> | |
{ | |
auto worker = this->m_worker; | |
auto mapping = | |
[worker,mapper](const S& state) -> std::tuple<bool,S,T1> | |
{ | |
auto[active, s1, v] = worker(state); | |
return std::make_tuple(active, s1, mapper(v)); | |
}; | |
return Enumerator<T1, S>(mapping, m_state); | |
} | |
void iter(std::function<void(const T&)>body) | |
{ | |
while (m_active) | |
{ | |
std::tie(m_active, m_state, m_value) = m_worker(m_state); | |
body(m_value); | |
} | |
} | |
template <class T1> | |
auto | |
fold | |
( std::function<T1(const T1&, const T&)> folder | |
, const T1 & initVal | |
) -> T1 | |
{ | |
T1 acc{ initVal }; | |
while (m_active) | |
{ | |
std::tie(m_active, m_state, m_value) = m_worker(m_state); | |
acc = folder(acc, m_value); | |
} | |
return acc; | |
} | |
template <class T1> | |
auto | |
statefulFold | |
(std::function<void(T1&, const T&)> folder | |
, T1 & initVal | |
) -> T1 | |
{ | |
T1 acc{ initVal }; | |
while (m_active) | |
{ | |
std::tie(m_active, m_state, m_value) = m_worker(m_state); | |
folder(acc, m_value); | |
} | |
return acc; | |
} | |
auto | |
take | |
( size_t n | |
) -> Enumerator<T, std::tuple<size_t, S> > | |
{ | |
auto worker = this->m_worker; | |
auto counted = | |
[worker](const std::tuple<size_t, S> state) | |
-> std::tuple<bool, std::tuple<size_t, S>, T> | |
{ | |
auto[ncurrent, scurrent] = state; | |
if (ncurrent > 0) | |
{ | |
auto[active, snext, v] = worker(scurrent); | |
size_t nnext = ncurrent - 1; | |
return std::make_tuple | |
( active && (0 < nnext) | |
, std::make_tuple(nnext, snext) | |
, v | |
); | |
} | |
else | |
{ | |
throw std::logic_error("This code path shoud be unreachable! (empty source enumerator?)"); | |
} | |
}; | |
return Enumerator<T, std::tuple<size_t, S> > | |
( counted | |
, std::make_tuple(n, m_state) | |
); | |
} | |
private: | |
Worker_t m_worker; | |
bool m_active; | |
State_t m_state; | |
Value_t m_value; | |
}; | |
template <class T> | |
Enumerator<T, T> range(const T& first) | |
{ | |
auto infiniteRange = | |
[first](const T& state) | |
{ | |
T v = state; | |
T s1 = state + 1; | |
bool active = s1 != first; | |
return std::make_tuple(active, s1, v); | |
}; | |
return Enumerator<T,T>(infiniteRange, first); | |
} | |
template <class T> | |
Enumerator<T, T> range(const T& first, const T& last) | |
{ | |
auto finiteRange = | |
[first, last](const T& state) | |
{ | |
T v = state; | |
T s1 = (state < last) ? (state + 1) : state; | |
bool active = state != s1; | |
return std::make_tuple(active, s1, v); | |
}; | |
return Enumerator<T,T>(finiteRange, first); | |
} | |
template <class IterT, class ValueT = IterT::value_type> | |
auto | |
iterRange | |
( const IterT& cbegin | |
, const IterT& cend | |
) -> Enumerator<ValueT, IterT> | |
{ | |
auto next = | |
[cend](const IterT& state) | |
-> std::tuple<bool, IterT, ValueT> | |
{ | |
auto s1 = state; | |
s1++; | |
if (s1 == cend) | |
{ | |
return std::make_tuple(false, state, *state); | |
} | |
else | |
{ | |
return std::make_tuple(true, s1, *state); | |
} | |
}; | |
return Enumerator<ValueT, IterT>(next, cbegin); | |
} | |
template <class T, class S> | |
auto | |
cycle | |
( Enumerator<T, S> values | |
) -> Enumerator<T, S> | |
{ | |
auto eternally = | |
[values](const S& state) -> std::tuple<bool, S, T> | |
{ | |
auto[active, s1, v] = values.step(state); | |
if (active) | |
{ | |
return std::make_tuple(active, s1, v); | |
} | |
else | |
{ | |
return std::make_tuple(true, values.state(), v); | |
} | |
}; | |
return Enumerator<T, S>(eternally, values.state()); | |
} | |
template <class T> | |
void show(const T& value) | |
{ | |
std::cout << value << std::endl; | |
} | |
static void testEnumerator() | |
{ | |
auto sumt = [](const std::tuple<float, float>& values) -> float | |
{ | |
float v1; float v2; | |
std::tie(v1, v2) = values; | |
return v1 + v2; | |
}; | |
auto r1 = range(0.0F, 100.0F); | |
{ | |
auto r1clone = r1; | |
std::cout << "r1clone:" << std::endl; | |
r1clone.iter(show<float>); | |
} | |
{ | |
std::cout << "negating the whole bunch: " << std::endl; | |
auto r1clone = r1; | |
r1clone | |
.map<float>([](const float& v) -> float { return -v; }) | |
.iter(show<float>) | |
; | |
} | |
{ | |
std::cout << "folding to the minimum of the whole bunch: " << std::endl; | |
auto minimum = | |
[](const float& acc, const float& v) -> float | |
{ | |
if (v < acc) return v; else return acc; | |
}; | |
auto r1clone = r1; | |
auto result = r1clone.fold<float>(minimum, std::numeric_limits<float>::max()); | |
std::cout << "minimum value of bunch is: " << result << std::endl; | |
} | |
{ | |
std::cout << std::endl << "computation:" << std::endl; | |
auto r2 = range(1000.0F); | |
auto r1clone = r1; | |
auto comp = r1clone.zip(r2).map<float>(sumt); | |
////.take(10) | |
comp.iter(show<float>); | |
} | |
{ | |
std::cout << std::endl << "something with cycle() ..." << std::endl; | |
auto fewValues = range(1.0F, 3.0F); | |
auto fewValuesCycled = cycle(fewValues); | |
auto r1clone = r1; | |
auto comp = r1clone.zip(fewValuesCycled).map<float>(sumt); | |
comp.iter(show<float>); | |
} | |
{ | |
std::cout << std::endl << "something with take(n) and cycle() ..." << std::endl; | |
auto fewValues = range(1.0F, 3.0F); | |
auto fewValuesCycled = cycle(fewValues); | |
fewValuesCycled.take(10).iter(show<float>); | |
} | |
} | |
std::string concatStrings(const std::string& s1, const std::string& s2) | |
{ | |
std::ostringstream oss; | |
oss << s1 << s2; | |
return oss.str(); | |
} | |
/* | |
The verbose version of haskell implementation: | |
module FizzBuzz | |
( fb | |
) | |
where | |
fb n = | |
fmap merge fizzBuzzAndNumbers | |
where | |
fizz = cycle ["","","fizz"] | |
buzz = cycle ["","","","","buzz"] | |
fizzBuzz = zipWith (++) fizz buzz | |
fizzBuzzAndNumbers = zip [1..n] fizzBuzz | |
merge (x,s) = if length s == 0 then show x else s | |
*/ | |
std::string fizzbuzz(size_t n) | |
{ | |
typedef std::vector<std::string> SVec; | |
// merge (x,s) = if length s == 0 then show x else s | |
auto merge = | |
[](const std::tuple<size_t, std::string> & value) | |
-> std::string | |
{ | |
auto[x, s] = value; | |
if (s.length() > 0) return s; | |
else return std::to_string(x); | |
}; | |
SVec fizzes{ "","","fizz" }; | |
SVec buzzes{ "","","","","buzz" }; | |
return | |
range(size_t{ 1 }, n) | |
.zip | |
( cycle(iterRange(fizzes.cbegin(), fizzes.cend())) | |
.zipWith | |
( std::function(concatStrings) | |
, cycle(iterRange(buzzes.cbegin(), buzzes.cend())) | |
) | |
) | |
.map<std::string>(merge) | |
.statefulFold<std::ostringstream&> | |
( | |
[](std::ostringstream& oss, const std::string& s) | |
{ | |
if (0 == oss.tellp()) | |
{ | |
oss << s; | |
} | |
else | |
{ | |
oss << "," << s; | |
} | |
} | |
, std::ostringstream() | |
) | |
.str(); | |
} | |
int main() | |
{ | |
testEnumerator(); | |
std::cout << "-------------------------" << std::endl; | |
size_t n = 50; | |
std::cout | |
<< "fizzbuzz(" | |
<< n | |
<< ") = [" | |
<< fizzbuzz(n) | |
<< "]" | |
<< std::endl; | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment