Skip to content

Instantly share code, notes, and snippets.

@ruffianeo
Created September 21, 2018 15:03
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ruffianeo/b95e7ebfaa1e5ce09c2e55943e6201c9 to your computer and use it in GitHub Desktop.
Save ruffianeo/b95e7ebfaa1e5ce09c2e55943e6201c9 to your computer and use it in GitHub Desktop.
Lazy C++ with stateful Enumerator and referentially transparent lambdas. FizzBuzz included ;)
#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