libzed 1.10.2
A general-purpose library for quick and simple data manipulation.
 
Loading...
Searching...
No Matches
generator.hpp
1#pragma once
2
3#include <functional>
4#include <map>
5
6#include "../polyfill/std_optional.hpp"
7#include "array.hpp"
8#include "templates.hpp"
9
10namespace z {
11namespace core {
12
14template <typename T, typename S>
16 std::function<const std::optional<T>(S &)> lambda;
17 S state;
18 std::optional<T> current_yield;
19
20public:
27 explicit generatorIter(std::function<const std::optional<T>(S &)> lambda, const S &state, bool dummy = false) : lambda(lambda), state(state), current_yield(T()) {
28 if (!dummy) {
29 ++(*this); // Load the first value
30 }
31 }
32
37 const T &operator*() const {
38 return current_yield.value();
39 }
40
46 current_yield = lambda(state);
47 return *this;
48 }
49
54 bool operator!=(const generatorIter &other) const {
55 (void)other;
56 return current_yield.has_value();
57 }
58};
59
70template <typename T, typename S>
71class generator : public iterable<generatorIter<T, S>> {
72 S state;
73 std::function<const std::optional<T>(S &)> lambda;
74
75 struct countedState {
76 long count;
77 S state;
78 };
79
80public:
86 generator(const S &initial, std::function<const std::optional<T>(S &)> lambda) : state(initial), lambda(lambda) {}
87
93 return generatorIter<T, S>(lambda, state);
94 }
95
101 return generatorIter<T, S>(lambda, state, true);
102 }
103
111 inline std::optional<T> next() {
112 return lambda(state);
113 }
114
120 inline long count() {
121 return this
122 ->map<long>([](const T &state) {
123 (void)state;
124 return 1;
125 })
126 .reduce(0, std::plus<long>());
127 }
128
136 for (auto i : *this) {
137 result.push(i);
138 }
139 return result;
140 }
141
147 long consume() {
148 long count = 0;
149 for (auto _ : *this) {
150 count++;
151 }
152 return count;
153 }
154
166
167 for (int i = 0; i < count; i++) {
168 auto item = next();
169 if (!item.has_value()) {
170 break;
171 }
172 result.push(item.value());
173 }
174
175 return result;
176 }
177
188 template <typename U>
189 generator<U, S> map(std::function<U(const T &)> mapLambda) noexcept {
190 auto lambda = this->lambda;
191
192 return generator<U, S>(state, [lambda, mapLambda](S &state) -> std::optional<U> {
193 auto item = lambda(state);
194 if (!item.has_value()) {
195 return {};
196 } else {
197 return mapLambda(item.value());
198 }
199 });
200 }
201
212 generator filter(std::function<T(const T &)> filterLambda) noexcept {
213 auto lambda = this->lambda;
214
215 return generator(state, [lambda, filterLambda](S &state) {
216 auto val = lambda(state);
217 while (val.has_value()) {
218 if (filterLambda(val.value())) {
219 return val;
220 }
221 val = lambda(state);
222 }
223
224 return val;
225 });
226 }
227
241 T reduce(const T &defaultValue, std::function<T(const T &, const T &)> reduceLambda) {
242 auto result = lambda(state);
243
244 if (!result.has_value()) {
245 return defaultValue;
246 }
247
248 auto value = result.value();
249
250 while (true) {
251 result = lambda(state);
252 if (!result.has_value()) {
253 break;
254 }
255 value = reduceLambda(value, result.value());
256 }
257
258 return value;
259 }
260
272 generator &forEach(std::function<void(const T &)> newLambda) noexcept {
273 auto lambda = this->lambda;
274 this->lambda = [lambda, newLambda](S &state) {
275 auto item = lambda(state);
276 if (item.has_value()) {
277 newLambda(item.value());
278 }
279 return item;
280 };
281
282 return *this;
283 }
284
296 auto lambda = this->lambda;
297
298 return generator<T, countedState>({count, state}, [lambda](countedState &state) {
299 for (long i = 0; i < state.count; i++) {
300 auto item = lambda(state.state);
301 if (!item.has_value()) {
302 return item;
303 }
304 }
305 state.count = 0;
306
307 return lambda(state.state);
308 });
309 }
310
318 auto lambda = this->lambda;
319
320 return generator<T, countedState>({count, state}, [lambda](countedState &state) {
321 if (state.count <= 0) {
322 return std::optional<T>();
323 }
324
325 auto item = lambda(state.state);
326 if (!item.has_value()) {
327 return item;
328 }
329
330 state.count--;
331
332 return item;
333 });
334 }
335
345 template <typename U, typename S2>
347 typedef std::pair<T, U> pair_type;
348
350 auto item1 = next();
351 if (!item1.has_value()) {
352 return std::optional<pair_type>();
353 }
354
355 auto item2 = otherGen.next();
356 if (!item2.has_value()) {
357 return std::optional<pair_type>();
358 }
359
360 return std::optional<pair_type>({item1.value(), item2.value()});
361 });
362 }
363
375 return generator<T, std::pair<bool, generator &>>({false, other}, [this](std::pair<bool, generator &> &state) {
376 state.first = !state.first;
377
378 // Draw from first generator
379 if (state.first) {
380 auto item = next();
381 if (item.has_value()) {
382 return item;
383 }
384 return state.second.next();
385 }
386
387 // Draw from second generator
388 auto item = state.second.next();
389 if (item.has_value()) {
390 return item;
391 }
392 return next();
393 });
394 }
395
404 generator<std::pair<long, T>, std::pair<long, generator<T, S>>> enumerate() noexcept {
405 return generator<std::pair<long, T>, std::pair<long, generator<T, S>>>({0, *this}, [](std::pair<long, generator<T, S>> &state) -> std::optional<std::pair<long, T>> {
406 auto item = state.second.next();
407 if (!item.has_value()) {
408 return {};
409 }
410 return std::pair<long, T>{state.first++, item.value()};
411 });
412 }
413
432 return generator<T, std::pair<generator, std::optional<T>>>({other, other.next()}, [this](std::pair<generator, std::optional<T>> &state) -> std::optional<T> {
433 while (true) {
434 auto item1 = next();
435 if (!item1.has_value()) {
436 return {};
437 }
438
439 if (!state.second.has_value()) {
440 return item1.value(); // Yield the item from this generator, as the other generator is done
441 }
442
443 if (item1.value() != state.second.value()) {
444 return item1.value(); // Yield the item from this generator, as it is different
445 }
446
447 // Move to the next item in the other generator
448 state.second = state.first.next();
449 }
450 });
451 }
452
464 return generator<array<T>, generator>(*this, [chunkSize](generator &state) -> std::optional<array<T>> {
466 for (long i = 0; i < chunkSize; i++) {
467 auto item = state.next();
468 if (!item.has_value()) {
469 if (chunk.length() == 0) {
470 return {}; // No more items, end the generator
471 }
472 break; // The chunk has data, yield it
473 }
474 chunk.push(item.value());
475 }
476 return chunk; // Return the current chunk
477 });
478 }
479
487 struct unchunkData {
488 std::optional<T> data;
489 std::optional<iterator_value<T>> iter;
491 };
492
493 auto unchunkFn = [](unchunkData &state) -> std::optional<dereference<T>> {
494 // If the generator has not been initialized,
495 // or there is no more data in the chunk,
496 // then try to generate another chunk.
497 if (!state.data || (state.iter.value() == state.data.value().end())) {
498 // If there's nothing left, we're done.
499 auto chunkData = state.gen.next();
500 if (!chunkData) {
501 return {};
502 }
503
504 auto &val = chunkData.value();
505 // Make sure we didn't just get an empty iterator.
506 if (val.begin() == val.end()) {
507 return {};
508 }
509
510 state.data = chunkData;
511 state.iter = state.data.value().begin();
512 }
513
514 // At this point the generator has been initialized, and current chunk has data in it,
515 // so spit out the next value on the chunk.
516
517 return *state.iter.value()++;
518 };
519
520 return generator<dereference<T>, unchunkData>({{}, {}, *this}, unchunkFn);
521 }
522
533 return generator<std::pair<T, std::optional<T>>, std::pair<std::optional<T>, generator>>({next(), *this}, [](std::pair<std::optional<T>, generator> &state) -> std::optional<std::pair<T, std::optional<T>>> {
534 const auto prevValue = state.first;
535 auto &gen = state.second;
536
537 if (!prevValue.has_value()) {
538 // No more items, end the generator
539 return {};
540 }
541 auto nextValue = gen.next();
542 state.first = nextValue;
543
544 return std::pair<T, std::optional<T>>(prevValue.value(), nextValue);
545 });
546 }
547
563 template <typename U>
565 return generator<T, bool>(false, [this, &other](bool &first_exhausted) {
566 if (!first_exhausted) {
567 auto item = this->next();
568 if (!item.has_value()) {
569 first_exhausted = true;
570 } else {
571 return item;
572 }
573 }
574
575 return other.next();
576 });
577 }
578};
579
586template <typename T>
589 if (iter != list.end()) {
590 auto ret = *iter;
591 ++iter; // Move to the next item
592 return ret;
593 }
594
595 return {};
596 });
597}
598
605template <typename T>
606generator<dereference<T>, std::pair<T, iterator_value<T>>> generatorFrom(const T &&list) {
607 return generator<dereference<T>, std::pair<T, iterator_value<T>>>({list, list.begin()}, [](std::pair<T, iterator_value<T>> &state) -> std::optional<dereference<T>> {
608 if (state.second != state.first.end()) {
609 auto ret = *state.second;
610 ++state.second; // Move to the next item
611 return ret;
612 }
613
614 return {};
615 });
616}
617
624template <typename T>
625generator<T, std::pair<array<T>, long>> generatorFrom(std::initializer_list<T> list) {
626 return generator<T, std::pair<array<T>, long>>({list, 0}, [](std::pair<array<T>, long> &state) -> std::optional<T> {
627 if (state.second < state.first.length()) {
628 return state.first[state.second++];
629 }
630
631 return {};
632 });
633}
634
642template <typename K, typename V>
643generator<std::pair<K, V>, typename std::map<K, V>::const_iterator> generatorFrom(const std::map<K, V> &map) {
644 return generator<std::pair<K, V>, typename std::map<K, V>::const_iterator>(map.begin(), [&map](auto &iter) -> std::optional<std::pair<K, V>> {
645 if (iter != map.end()) {
646 auto ret = *iter;
647 ++iter; // Move to the next item
648 return ret;
649 }
650
651 return {};
652 });
653}
654
662template <typename K, typename V>
663generator<std::pair<K, V>, std::pair<typename std::map<K, V>::const_iterator, std::map<K, V>>> generatorFrom(std::map<K, V> &&map) {
664 return generator<std::pair<K, V>, std::pair<typename std::map<K, V>::const_iterator, std::map<K, V>>>({map.begin(), map}, [](auto &state) -> std::optional<std::pair<K, V>> {
665 if (state.first != state.second.end()) {
666 auto ret = *state.first;
667 ++state.first; // Move to the next item
668 return ret;
669 }
670
671 return {};
672 });
673}
674
675} // namespace core
676} // namespace z
A wrapper for std::vector.
Definition array.hpp:75
void increase(int newSize) noexcept
Increase the space allocated for this array.
Definition array.hpp:192
int push(const T &object) noexcept
Add an object to the array.
Definition array.hpp:242
int length() const noexcept override
Get the length of the array.
Definition array.hpp:1035
T * begin() const noexcept override
Get pointer to the beginning of the array.
Definition array.hpp:621
T * end() const noexcept override
Get pointer to the end of the array.
Definition array.hpp:633
array< U > map(std::function< U(const T &)> lambda) const
Applies a transformation function to each element of the array and returns a new array with the resul...
Definition array.hpp:1230
Custom iterator for generators to allow for range-based for loops.
Definition generator.hpp:15
bool operator!=(const generatorIter &other) const
Check if the generator can get more data.
Definition generator.hpp:54
generatorIter(std::function< const std::optional< T >(S &)> lambda, const S &state, bool dummy=false)
Constructor.
Definition generator.hpp:27
generatorIter & operator++()
Generate the next value.
Definition generator.hpp:45
const T & operator*() const
Get the current value from the generator.
Definition generator.hpp:37
An arbitrary generator for producing sequential results on-the-fly.
Definition generator.hpp:71
generator filter(std::function< T(const T &)> filterLambda) noexcept
Filters the generatred items based on a predicate and returns a new generator that yields only the it...
Definition generator.hpp:212
generator< array< T >, generator > chunk(long chunkSize) noexcept
Get chunks of items from the generator.
Definition generator.hpp:463
generator & forEach(std::function< void(const T &)> newLambda) noexcept
Binds a function to run each time an item comes out of the generator.
Definition generator.hpp:272
generator(const S &initial, std::function< const std::optional< T >(S &)> lambda)
Constructor with an initial state.
Definition generator.hpp:86
array< T > collect()
Concatenate all generator elements into an array.
Definition generator.hpp:134
generator< T, std::pair< generator, std::optional< T > > > diff(generator &other) noexcept
List the items in this generator which differ from another generator.
Definition generator.hpp:431
generator< T, countedState > skip(long count) noexcept
Skips a certain number of items from the generator.
Definition generator.hpp:295
long count()
Get the total count of items that will be generated.
Definition generator.hpp:120
T reduce(const T &defaultValue, std::function< T(const T &, const T &)> reduceLambda)
Reduces the generator to a single value by applying a binary operation cumulatively to all yielded va...
Definition generator.hpp:241
generator< U, S > map(std::function< U(const T &)> mapLambda) noexcept
Applies a transformation function to each item that comes out of the generator.
Definition generator.hpp:189
generator< T, bool > chain(generator< T, U > &other) noexcept
Chains two generators together.
Definition generator.hpp:564
long consume()
Consume and discard all items from the generator.
Definition generator.hpp:147
generator< T, std::pair< bool, generator & > > zip(generator &other) noexcept
Zip this generator with another generator.
Definition generator.hpp:374
generatorIter< T, S > end() const noexcept override
End iterator (end of the range)
Definition generator.hpp:100
auto flatten() noexcept
Break up a chunked generator into its constituent generated items.
Definition generator.hpp:486
std::optional< T > next()
Get the next item from the generator.
Definition generator.hpp:111
generatorIter< T, S > begin() const noexcept override
Begin iterator (start of the range)
Definition generator.hpp:92
generator< T, countedState > limit(long count) noexcept
Limits the number of items that the generator will std::optional.
Definition generator.hpp:317
generator< std::pair< long, T >, std::pair< long, generator< T, S > > > enumerate() noexcept
Enumerate the items in this generator.
Definition generator.hpp:404
array< T > take(int count)
Take a certain number of items from the generator.
Definition generator.hpp:163
generator< std::pair< T, std::optional< T > >, std::pair< std::optional< T >, generator > > peek() noexcept
Allow peeking at the next item in the generator as items are generated.
Definition generator.hpp:532
generator< std::pair< T, U >, generator< U, S2 > > pair(generator< U, S2 > &other) noexcept
Pair items from this generator with those of another generator.
Definition generator.hpp:346
A base interface for all objects that can be iterated over.
Definition iterable.hpp:10
generator< dereference< T >, const_iterator_value< T > > generatorFrom(const T &list)
Create a generator from an arbitrary iterable.
Definition generator.hpp:587
Utility template definitions to allow for simpler type restrictions.