libzed 1.11.4
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#include <optional>
6
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>> {
72protected:
75
77 std::function<const std::optional<T>(S &)> lambda;
78
80 struct countedState {
82 long count;
85 };
86
87public:
93 generator(const S &initial, std::function<const std::optional<T>(S &)> lambda) : state(initial), lambda(lambda) {}
94
102
108 return generatorIter<T, S>(lambda, state, true);
109 }
110
118 inline virtual std::optional<T> next() {
119 return lambda(state);
120 }
121
127 long count() {
128 long count = 0;
129 for (auto _ : *this) {
130 count++;
131 }
132 return count;
133 }
134
142 for (auto i : *this) {
143 result.push(i);
144 }
145 return result;
146 }
147
152 inline long consume() {
153 return count();
154 }
155
167
168 for (int i = 0; i < count; i++) {
169 auto item = next();
170 if (!item.has_value()) {
171 break;
172 }
173 result.push(item.value());
174 }
175
176 return result;
177 }
178
189 template <typename U>
190 generator<U, S> map(std::function<U(const T &)> mapLambda) noexcept {
191 auto lambda = this->lambda;
192
193 return generator<U, S>(state, [lambda, mapLambda](S &state) -> std::optional<U> {
194 auto item = lambda(state);
195 if (!item.has_value()) {
196 return {};
197 } else {
198 return mapLambda(item.value());
199 }
200 });
201 }
202
213 generator filter(std::function<bool(const T &)> filterLambda) noexcept {
214 auto lambda = this->lambda;
215
217 auto val = lambda(state);
218 while (val.has_value()) {
219 if (filterLambda(val.value())) {
220 return val;
221 }
222 val = lambda(state);
223 }
224
225 return val;
226 });
227 }
228
242 T reduce(std::function<T(const T &, const T &)> reduceLambda, const T &defaultValue = {}) {
243 auto result = lambda(state);
244
245 if (!result.has_value()) {
246 return defaultValue;
247 }
248
249 auto value = result.value();
250
251 while (true) {
252 result = lambda(state);
253 if (!result.has_value()) {
254 break;
255 }
256 value = reduceLambda(value, result.value());
257 }
258
259 return value;
260 }
261
273 generator &forEach(std::function<void(const T &)> newLambda) noexcept {
274 auto lambda = this->lambda;
275 this->lambda = [lambda, newLambda](S &state) {
276 auto item = lambda(state);
277 if (item.has_value()) {
278 newLambda(item.value());
279 }
280 return item;
281 };
282
283 return *this;
284 }
285
297 auto lambda = this->lambda;
298
300 for (long i = 0; i < state.count; i++) {
301 auto item = lambda(state.state);
302 if (!item.has_value()) {
303 return item;
304 }
305 }
306 state.count = 0;
307
308 return lambda(state.state);
309 });
310 }
311
319 auto lambda = this->lambda;
320
322 if (state.count <= 0) {
323 return std::optional<T>();
324 }
325
326 auto item = lambda(state.state);
327 if (!item.has_value()) {
328 return item;
329 }
330
331 state.count--;
332
333 return item;
334 });
335 }
336
346 template <typename U, typename S2>
348 typedef std::pair<T, U> pair_type;
349
351 auto item1 = next();
352 if (!item1.has_value()) {
353 return std::optional<pair_type>();
354 }
355
356 auto item2 = otherGen.next();
357 if (!item2.has_value()) {
358 return std::optional<pair_type>();
359 }
360
361 return std::optional<pair_type>({item1.value(), item2.value()});
362 });
363 }
364
376 return generator<T, std::pair<bool, generator &>>({false, other}, [this](std::pair<bool, generator &> &state) {
377 state.first = !state.first;
378
379 // Draw from first generator
380 if (state.first) {
381 auto item = next();
382 if (item.has_value()) {
383 return item;
384 }
385 return state.second.next();
386 }
387
388 // Draw from second generator
389 auto item = state.second.next();
390 if (item.has_value()) {
391 return item;
392 }
393 return next();
394 });
395 }
396
405 generator<std::pair<long, T>, std::pair<long, generator<T, S>>> enumerate() noexcept {
406 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>> {
407 auto item = state.second.next();
408 if (!item.has_value()) {
409 return {};
410 }
411 return std::pair<long, T>{state.first++, item.value()};
412 });
413 }
414
433 return generator<T, std::pair<generator, std::optional<T>>>({other, other.next()}, [this](std::pair<generator, std::optional<T>> &state) -> std::optional<T> {
434 while (true) {
435 auto item1 = next();
436 if (!item1.has_value()) {
437 return {};
438 }
439
440 if (!state.second.has_value()) {
441 return item1.value(); // Yield the item from this generator, as the other generator is done
442 }
443
444 if (item1.value() != state.second.value()) {
445 return item1.value(); // Yield the item from this generator, as it is different
446 }
447
448 // Move to the next item in the other generator
449 state.second = state.first.next();
450 }
451 });
452 }
453
465 return generator<array<T>, generator>(*this, [chunkSize](generator &state) -> std::optional<array<T>> {
467 for (long i = 0; i < chunkSize; i++) {
468 auto item = state.next();
469 if (!item.has_value()) {
470 if (chunk.length() == 0) {
471 return {}; // No more items, end the generator
472 }
473 break; // The chunk has data, yield it
474 }
475 chunk.push(item.value());
476 }
477 return chunk; // Return the current chunk
478 });
479 }
480
488 struct unchunkData {
489 std::optional<T> data;
490 std::optional<iterator_value<T>> iter;
492 };
493
494 auto unchunkFn = [](unchunkData &state) -> std::optional<dereference<T>> {
495 // If the generator has not been initialized,
496 // or there is no more data in the chunk,
497 // then try to generate another chunk.
498 if (!state.data || (state.iter.value() == state.data.value().end())) {
499 // If there's nothing left, we're done.
500 auto chunkData = state.gen.next();
501 if (!chunkData) {
502 return {};
503 }
504
505 auto &val = chunkData.value();
506 // Make sure we didn't just get an empty iterator.
507 if (val.begin() == val.end()) {
508 return {};
509 }
510
511 state.data = chunkData;
512 state.iter = state.data.value().begin();
513 }
514
515 // At this point the generator has been initialized, and current chunk has data in it,
516 // so spit out the next value on the chunk.
517
518 return *state.iter.value()++;
519 };
520
521 return generator<dereference<T>, unchunkData>({{}, {}, *this}, unchunkFn);
522 }
523
534 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>>> {
535 const auto prevValue = state.first;
536 auto &gen = state.second;
537
538 if (!prevValue.has_value()) {
539 // No more items, end the generator
540 return {};
541 }
542 auto nextValue = gen.next();
543 state.first = nextValue;
544
545 return std::pair<T, std::optional<T>>(prevValue.value(), nextValue);
546 });
547 }
548
564 template <typename U>
566 return generator<T, std::pair<generator, bool>>({*this, false}, [&other](std::pair<generator, bool> &state) {
567 if (!state.second) {
568 auto item = state.first.next();
569 if (!item.has_value()) {
570 state.second = true;
571 } else {
572 return item;
573 }
574 }
575
576 return other.next();
577 });
578 }
579
583 template <typename U>
585 return generator<T, std::pair<std::pair<generator, bool>, generator<T, U>>>({{*this, false}, other}, [&other](std::pair<std::pair<generator, bool>, generator<T, U>> &state) {
586 if (!state.first.second) {
587 auto item = state.first.first.next();
588 if (!item.has_value()) {
589 state.first.second = true;
590 } else {
591 return item;
592 }
593 }
594
595 return state.second.next();
596 });
597 }
598
608 generator until(std::function<bool(T)> predicate) noexcept {
609 auto lambda = this->lambda;
610
611 return generator(state, [lambda, predicate](S &state) {
612 auto val = lambda(state);
613 if (val.has_value() && predicate(val.value())) {
614 return std::optional<T>{};
615 }
616 return val;
617 });
618 }
619
628 generator until(const T &sentinel) noexcept {
629 auto lambda = this->lambda;
630
631 return generator(state, [lambda, sentinel](S &state) {
632 auto val = lambda(state);
633 if (val.has_value() && val.value() == sentinel) {
634 return std::optional<T>{};
635 }
636 return val;
637 });
638 }
639
645 template <typename U>
649
655 template <typename U>
657 return generator<T, std::pair<std::pair<generator, bool>, generator<T, U>>>({{*this, false}, other}, [&other](std::pair<std::pair<generator, bool>, generator<T, U>> &state) {
658 if (!state.first.second) {
659 auto item = state.first.first.next();
660 if (!item.has_value()) {
661 state.first.second = true;
662 } else {
663 return item;
664 }
665 }
666
667 return state.second.next();
668 });
669 }
670
676 template <typename U>
677 inline generator<U, S> operator|(std::function<U(T)> mapLambda) noexcept {
678 return map<U>(mapLambda);
679 }
680
686 template <typename U>
687 inline generator<U, S> operator|(U (*mapLambda)(T)) noexcept {
688 return map<U>(mapLambda);
689 }
690
696 inline generator operator&&(std::function<T(const T &)> filterLambda) noexcept {
697 return filter(filterLambda);
698 }
699
714 inline T operator>>(std::function<T(const T &, const T &)> reduceLambda) {
715 return reduce(reduceLambda);
716 }
717
723 inline generator operator!=(std::function<bool(T)> predicate) {
724 return until(predicate);
725 }
726
733 return until(sentinel);
734 }
735};
736
743template <typename T>
746 if (iter != list.end()) {
747 auto ret = *iter;
748 ++iter; // Move to the next item
749 return ret;
750 }
751
752 return {};
753 });
754}
755
762template <typename T>
763generator<dereference<T>, std::pair<T, iterator_value<T>>> generatorFrom(const T &&list) {
764 return generator<dereference<T>, std::pair<T, iterator_value<T>>>({list, list.begin()}, [](std::pair<T, iterator_value<T>> &state) -> std::optional<dereference<T>> {
765 if (state.second != state.first.end()) {
766 auto ret = *state.second;
767 ++state.second; // Move to the next item
768 return ret;
769 }
770
771 return {};
772 });
773}
774
781template <typename T>
782generator<T, std::pair<array<T>, long>> generatorFrom(std::initializer_list<T> list) {
783 return generator<T, std::pair<array<T>, long>>({list, 0}, [](std::pair<array<T>, long> &state) -> std::optional<T> {
784 if (state.second < state.first.length()) {
785 return state.first[state.second++];
786 }
787
788 return {};
789 });
790}
791
799template <typename K, typename V>
800generator<std::pair<K, V>, typename std::map<K, V>::const_iterator> generatorFrom(const std::map<K, V> &map) {
801 return generator<std::pair<K, V>, typename std::map<K, V>::const_iterator>(map.begin(), [&map](auto &iter) -> std::optional<std::pair<K, V>> {
802 if (iter != map.end()) {
803 auto ret = *iter;
804 ++iter; // Move to the next item
805 return ret;
806 }
807
808 return {};
809 });
810}
811
819template <typename K, typename V>
820generator<std::pair<K, V>, std::pair<typename std::map<K, V>::const_iterator, std::map<K, V>>> generatorFrom(std::map<K, V> &&map) {
821 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>> {
822 if (state.first != state.second.end()) {
823 auto ret = *state.first;
824 ++state.first; // Move to the next item
825 return ret;
826 }
827
828 return {};
829 });
830}
831
832} // namespace core
833} // namespace z
A wrapper for std::vector.
Definition array.hpp:38
void increase(int newSize) noexcept
Increase the space allocated for this array.
Definition array.hpp:155
int push(const T &object) noexcept
Add an object to the array.
Definition array.hpp:205
int length() const noexcept override
Get the length of the array.
Definition array.hpp:1041
T * begin() const noexcept override
Get pointer to the beginning of the array.
Definition array.hpp:627
T * end() const noexcept override
Get pointer to the end of the array.
Definition array.hpp:639
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< array< T >, generator > chunk(long chunkSize) noexcept
Get chunks of items from the generator.
Definition generator.hpp:464
generator< T, std::pair< generator, bool > > chain(generator< T, U > &other) noexcept
Chains two generators together.
Definition generator.hpp:565
virtual std::optional< T > next()
Get the next item from the generator.
Definition generator.hpp:118
T reduce(std::function< T(const T &, const T &)> reduceLambda, const T &defaultValue={})
Reduces the generator to a single value by applying a binary operation cumulatively to all yielded va...
Definition generator.hpp:242
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:273
generator(const S &initial, std::function< const std::optional< T >(S &)> lambda)
Constructor with an initial state.
Definition generator.hpp:93
array< T > collect()
Concatenate all generator elements into an array.
Definition generator.hpp:140
generator< U, S > operator|(std::function< U(T)> mapLambda) noexcept
Definition generator.hpp:677
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:432
T operator>>(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:714
generator< T, std::pair< generator, bool > > operator+(generator< T, U > &other) noexcept
Definition generator.hpp:646
generator operator&&(std::function< T(const T &)> filterLambda) noexcept
Definition generator.hpp:696
generator< T, countedState > skip(long count) noexcept
Skips a certain number of items from the generator.
Definition generator.hpp:296
generator filter(std::function< bool(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:213
S state
The state data of this generator.
Definition generator.hpp:74
generator< T, std::pair< std::pair< generator, bool >, generator< T, U > > > chain(generator< T, U > &&other) noexcept
Chains two generators together.
Definition generator.hpp:584
long count()
Consume and discard all items from the generator, getting only the number of items generated.
Definition generator.hpp:127
generator< U, S > operator|(U(*mapLambda)(T)) noexcept
Definition generator.hpp:687
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:190
long consume()
Definition generator.hpp:152
generator< T, std::pair< std::pair< generator, bool >, generator< T, U > > > operator+(generator< T, U > &&other) noexcept
Definition generator.hpp:656
generator< T, std::pair< bool, generator & > > zip(generator &other) noexcept
Zip this generator with another generator.
Definition generator.hpp:375
generatorIter< T, S > end() const noexcept override
End iterator (end of the range)
Definition generator.hpp:107
auto flatten() noexcept
Break up a chunked generator into its constituent generated items.
Definition generator.hpp:487
generator until(const T &sentinel) noexcept
End the generator when the given value is yielded from the generator.
Definition generator.hpp:628
generatorIter< T, S > begin() const noexcept override
Begin iterator (start of the range)
Definition generator.hpp:99
generator until(std::function< bool(T)> predicate) noexcept
End the generator when the given predicate returns true.
Definition generator.hpp:608
generator operator!=(const T &sentinel)
Definition generator.hpp:732
generator operator!=(std::function< bool(T)> predicate)
Definition generator.hpp:723
generator< T, countedState > limit(long count) noexcept
Limits the number of items that the generator will std::optional.
Definition generator.hpp:318
generator< std::pair< long, T >, std::pair< long, generator< T, S > > > enumerate() noexcept
Enumerate the items in this generator.
Definition generator.hpp:405
std::function< const std::optional< T >(S &)> lambda
The function that gets run every time an item is generated.
Definition generator.hpp:77
array< T > take(int count)
Take a certain number of items from the generator.
Definition generator.hpp:164
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:533
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:347
A base interface for all objects that can be iterated over.
Definition iterable.hpp:10
A sentinel that stops a numeric generator when the value reaches a certain point.
Definition sentinel.hpp:10
generator< dereference< T >, const_iterator_value< T > > generatorFrom(const T &list)
Create a generator from an arbitrary iterable.
Definition generator.hpp:744
A specialized state for generator::enumerate()
Definition generator.hpp:80
long count
The current index.
Definition generator.hpp:82
S state
The state data of the original generator.
Definition generator.hpp:84
Utility template definitions to allow for simpler type restrictions.