2323#pragma once
2424#include < algorithm>
2525#include < cassert>
26+ #include < functional>
2627#include < type_traits>
2728#include < vector>
2829#include < iterator>
30+ #include < memory>
31+ #include < utility>
2932#include " index_range.h"
3033#include " optional.h"
3134#ifdef PARALLEL_ALGORITHM_AVAILABLE
@@ -36,6 +39,193 @@ namespace fcpp {
3639 template <class T , class Compare >
3740 class set ;
3841
42+ template <typename T>
43+ class vector ;
44+
45+ // A lightweight wrapper representing a deferred vector pipeline, enabling fluent and functional
46+ // programming while avoiding intermediate vector materialization.
47+ //
48+ // Member functions are non-mutating and keep extending the pipeline. Terminal functions such as
49+ // `get` and `reduce` execute the stored operations.
50+ template <typename T>
51+ class lazy_vector
52+ {
53+ public:
54+ lazy_vector ()
55+ : m_operation([](const std::function<void (const T&)>&) {})
56+ , m_capacity_hint(0 )
57+ {
58+ }
59+
60+ // Creates a lazy vector by copying the provided std::vector as an owned source.
61+ explicit lazy_vector (const std::vector<T>& vector)
62+ : m_capacity_hint(vector.size())
63+ {
64+ auto source = std::make_shared<std::vector<T>>(vector);
65+ m_operation = [source](const std::function<void (const T&)>& consumer) {
66+ std::for_each (source->begin (), source->end (), consumer);
67+ };
68+ }
69+
70+ // Creates a lazy vector by moving the provided std::vector as an owned source.
71+ explicit lazy_vector (std::vector<T>&& vector)
72+ : m_capacity_hint(vector.size())
73+ {
74+ auto source = std::make_shared<std::vector<T>>(std::move (vector));
75+ m_operation = [source](const std::function<void (const T&)>& consumer) {
76+ std::for_each (source->begin (), source->end (), consumer);
77+ };
78+ }
79+
80+ // Creates a lazy vector by referring to an existing std::vector source.
81+ // The referenced vector must outlive this lazy vector.
82+ explicit lazy_vector (const std::vector<T>* vector)
83+ : m_capacity_hint(vector->size ())
84+ {
85+ m_operation = [vector](const std::function<void (const T&)>& consumer) {
86+ std::for_each (vector->begin (), vector->end (), consumer);
87+ };
88+ }
89+
90+ // Creates a lazy vector by directly providing the deferred operation.
91+ // This constructor is mostly useful for composing lazy_vector instances.
92+ lazy_vector (std::function<void (const std::function<void (const T&)>&)> operation, size_t capacity_hint)
93+ : m_operation(std::move(operation))
94+ , m_capacity_hint(capacity_hint)
95+ {
96+ }
97+
98+ // Performs the functional `map` algorithm lazily. The transform is not applied until
99+ // a terminal operation, such as `get` or `reduce`, is called.
100+ //
101+ // example:
102+ // const fcpp::vector<int> input_vector({ 1, 3, -5 });
103+ // const auto output_vector = input_vector
104+ // .lazy()
105+ // .map<std::string>([](const auto& element) {
106+ // return std::to_string(element);
107+ // })
108+ // .get();
109+ //
110+ // outcome:
111+ // output_vector -> fcpp::vector<std::string>({ "1", "3", "-5" })
112+ #ifdef CPP17_AVAILABLE
113+ template <typename U, typename Transform, typename = std::enable_if_t <std::is_invocable_r_v<U, Transform, T>>>
114+ #else
115+ template <typename U, typename Transform>
116+ #endif
117+ [[nodiscard]] lazy_vector<U> map (Transform&& transform) const
118+ {
119+ const auto previous = m_operation;
120+ const auto capacity_hint = m_capacity_hint;
121+ typename std::decay<Transform>::type transform_copy (std::forward<Transform>(transform));
122+ return lazy_vector<U>(
123+ [previous, transform_copy](const std::function<void (const U&)>& consumer) mutable {
124+ previous ([&consumer, &transform_copy](const T& element) {
125+ consumer (transform_copy (element));
126+ });
127+ },
128+ capacity_hint);
129+ }
130+
131+ // Performs the functional `map` algorithm lazily.
132+ // See also `map` for more documentation.
133+ #ifdef CPP17_AVAILABLE
134+ template <typename U, typename Transform, typename = std::enable_if_t <std::is_invocable_r_v<U, Transform, T>>>
135+ #else
136+ template <typename U, typename Transform>
137+ #endif
138+ [[nodiscard]] lazy_vector<U> mapped (Transform&& transform) const
139+ {
140+ return map<U>(std::forward<Transform>(transform));
141+ }
142+
143+ // Performs the functional `filter` algorithm lazily, in which all elements which match
144+ // the given predicate are kept. The predicate is not applied until a terminal operation,
145+ // such as `get` or `reduce`, is called.
146+ //
147+ // example:
148+ // const fcpp::vector<int> numbers({ 1, 3, -5, 2, -1, 9, -4 });
149+ // const auto filtered_numbers = numbers
150+ // .lazy()
151+ // .filter([](const auto& element) {
152+ // return element >= 1.5;
153+ // })
154+ // .get();
155+ //
156+ // outcome:
157+ // filtered_numbers -> fcpp::vector<int>({ 3, 2, 9 })
158+ #ifdef CPP17_AVAILABLE
159+ template <typename Filter, typename = std::enable_if_t <std::is_invocable_r_v<bool , Filter, T>>>
160+ #else
161+ template <typename Filter>
162+ #endif
163+ [[nodiscard]] lazy_vector filter (Filter&& predicate_to_keep) const
164+ {
165+ const auto previous = m_operation;
166+ const auto capacity_hint = m_capacity_hint;
167+ typename std::decay<Filter>::type predicate_copy (std::forward<Filter>(predicate_to_keep));
168+ return lazy_vector (
169+ [previous, predicate_copy](const std::function<void (const T&)>& consumer) mutable {
170+ previous ([&consumer, &predicate_copy](const T& element) {
171+ if (predicate_copy (element)) {
172+ consumer (element);
173+ }
174+ });
175+ },
176+ capacity_hint);
177+ }
178+
179+ // Performs the functional `filter` algorithm lazily.
180+ // See also `filter` for more documentation.
181+ #ifdef CPP17_AVAILABLE
182+ template <typename Filter, typename = std::enable_if_t <std::is_invocable_r_v<bool , Filter, T>>>
183+ #else
184+ template <typename Filter>
185+ #endif
186+ [[nodiscard]] lazy_vector filtered (Filter&& predicate_to_keep) const
187+ {
188+ return filter (std::forward<Filter>(predicate_to_keep));
189+ }
190+
191+ // Performs the functional `reduce` (fold/accumulate) algorithm, by returning the result of
192+ // accumulating all the values in this lazy vector to an initial value.
193+ //
194+ // example:
195+ // const fcpp::vector<int> numbers({ 1, 3, -5, 2, -1, 9, -4 });
196+ // const auto sum = numbers
197+ // .lazy()
198+ // .filter([](const auto& element) {
199+ // return element > 0;
200+ // })
201+ // .reduce(0, [](const int& partial_sum, const int& number) {
202+ // return partial_sum + number;
203+ // });
204+ //
205+ // outcome:
206+ // sum -> 15
207+ #ifdef CPP17_AVAILABLE
208+ template <typename U, typename Reduce, typename = std::enable_if_t <std::is_invocable_r_v<U, Reduce, U, T>>>
209+ #else
210+ template <typename U, typename Reduce>
211+ #endif
212+ U reduce (const U& initial, Reduce&& reduction) const
213+ {
214+ auto result = initial;
215+ m_operation ([&result, &reduction](const T& element) {
216+ result = reduction (result, element);
217+ });
218+ return result;
219+ }
220+
221+ // Materializes this lazy vector to a functional vector, executing all stored operations.
222+ [[nodiscard]] vector<T> get () const ;
223+
224+ private:
225+ std::function<void (const std::function<void (const T&)>&)> m_operation;
226+ size_t m_capacity_hint;
227+ };
228+
39229 // A lightweight wrapper around std::vector, enabling fluent and functional
40230 // programming on the vector itself, rather than using the more procedural style
41231 // of the standard library algorithms.
@@ -1429,6 +1619,13 @@ namespace fcpp {
14291619 return *this ;
14301620 }
14311621
1622+ // Starts a lazy pipeline. The returned lazy vector defers following map/filter
1623+ // transformations until a terminal operation, such as get() or reduce(), is called.
1624+ [[nodiscard]] lazy_vector<T> lazy () const
1625+ {
1626+ return lazy_vector<T>(&m_vector);
1627+ }
1628+
14321629 // Returns the begin iterator, useful for other standard library algorithms
14331630 [[nodiscard]] typename std::vector<T>::iterator begin ()
14341631 {
@@ -1685,4 +1882,15 @@ namespace fcpp {
16851882 assert (index <= size ());
16861883 }
16871884 };
1885+
1886+ template <typename T>
1887+ [[nodiscard]] vector<T> lazy_vector<T>::get() const
1888+ {
1889+ std::vector<T> materialized;
1890+ materialized.reserve (m_capacity_hint);
1891+ m_operation ([&materialized](const T& element) {
1892+ materialized.push_back (element);
1893+ });
1894+ return vector<T>(std::move (materialized));
1895+ }
16881896}
0 commit comments