iter_comprehensions/
lib.rs

1/*
2 * MIT License
3 *
4 * Copyright (c) 2019-2024 Frank Fischer <frank-fischer@shadow-soft.de>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25//! Compact iterator and vector comprehensions.
26//!
27//! This crate implements a few macros with generating comprehension
28//! expressions.
29//!
30//! 1. `comprehension!` for generating a sequence of index tuples
31//! 2. `map!` for generating a sequence of expressions
32//! 3. `vec!` for constructing vectors
33//! 4. `sum!` for computing the sum of some values
34//! 5. `product!` for computing the product of some values
35//!
36//! The macro `comprehension!` can be used to generate a sequence of elements using
37//! generating sequences and conditional filters.
38//!
39//! ```no_test
40//! comprehension!(i1 in RANGE1, COND1, ..., ik in RANGEk)
41//! ```
42//!
43//! where RANGE* are iterators (in fact, everything implementing `IntoIterator`)
44//! and each COND* is a boolean condition. Each `RANGE` and `COND` term can use
45//! the variables declared in preceding range expressions.
46//!
47//! The macro `map!` adds an additional expression that computes a value
48//! depending on the indices:
49//!
50//! ```no_test
51//! map!(i1 in RANGE1, COND1, ..., ik in RANGEk, EXPR)
52//! map!(EXPR; i1 in RANGE1, COND1, ..., ik in RANGEk)
53//! ```
54//!
55//! # Example
56//! The expression $\\{ 5i + j : i \\in \\{0, \\ldots, 4\\}, j \\in \\{0,
57//! \\ldots, 4\\}, i < j \\}$ is equivalent to the following form
58//!
59//! ```
60//! use iter_comprehensions::map;
61//! assert_eq!(map!(5*i + j; i in 0..5, j in 0..5, i < j).collect::<Vec<_>>(),
62//!            vec![1, 2, 3, 4, 7, 8, 9, 13, 14, 19]);
63//! ```
64//!
65//! The analogous syntax can be used to create vectors:
66//!
67//! ```
68//! use iter_comprehensions::vec;
69//! assert_eq!(vec![i; i in 0..5], vec![0,1,2,3,4]);
70//! assert_eq!(vec![(i,j); i in 0..3, j in 0..3, i < j],
71//!            vec![(0,1), (0,2), (1,2)]);
72//! ```
73//!
74//! Computing a sum of values:
75//!
76//! ```
77//! use iter_comprehensions::{sum, vec};
78//! assert_eq!(sum!(i; i in 1..10, i % 2 == 1), 25);
79//! let S = vec![i; i in 1..10];
80//! assert_eq!(sum!(i in S, i % 2 == 1, i), 25);
81//! ```
82//!
83//! Computing a product of values:
84//!
85//! ```
86//! use iter_comprehensions::product;
87//! assert_eq!(product!(i; i in 1..=5), 120);
88//! assert_eq!(product!(i in 1..=5, i), 120);
89//! ```
90//!
91//! It is also possible to use tuple destructuring binds in
92//! RANGE expressions:
93//! ```
94//! use iter_comprehensions::sum;
95//! assert_eq!(sum!(i*j; (i,j) in [(1,2), (3,4), (5,6)]), 44);
96//! assert_eq!(sum!((i,j) in [(1,2), (3,4), (5,6)], i*j), 44);
97//! ```
98
99
100use std::iter::{Product, Sum};
101
102#[doc(hidden)]
103#[macro_export(local_inner_macros)]
104macro_rules! _comprehension_iter {
105    ($r:tt; $e:expr) => { Some(std::iter::once($e)) };
106    ($r:tt; $i:tt movein $range:expr, $($rest:tt)+ ) => {
107        Some($range
108             .into_iter()
109             .zip(std::iter::repeat($r))
110             .filter_map(move |($i, $r)| _comprehension_iter!(($i, $r); $($rest)*)).flatten())
111    };
112    ($r:tt; $i:tt in $range:expr, $($rest:tt)+ ) => {
113        Some($range
114             .into_iter()
115             .zip(std::iter::repeat($r))
116             .filter_map(|($i, $r)| _comprehension_iter!(($i, $r); $($rest)*)).flatten())
117    };
118    ($r:tt; $cond:expr, $($rest:tt)*) => {
119        if $cond {
120            _comprehension_iter!($r; $($rest)*)
121        } else {
122            None
123        }
124    };
125}
126
127#[doc(hidden)]
128#[macro_export(local_inner_macros)]
129macro_rules! _comprehension {
130    (($($vars:tt),*); ($($clauses:tt)*); $i:tt in $range:expr) => {
131        _comprehension!(($($vars,)* $i); ($($clauses)* ($i in $range));)
132    };
133
134    (($($vars:tt),*); ($($clauses:tt)*); move $i:tt in $range:expr) => {
135        _comprehension!(($($vars,)* $i); ($($clauses)* ($i movein $range));)
136    };
137
138    (($($vars:tt),*); ($($clauses:tt)*); $i:tt in $range:expr, $($rest:tt)*) => {
139        _comprehension!(($($vars,)* $i); ($($clauses)* ($i in $range)); $($rest)*)
140    };
141
142    (($($vars:tt),*); ($($clauses:tt)*); move $i:tt in $range:expr, $($rest:tt)*) => {
143        _comprehension!(($($vars,)* $i); ($($clauses)* ($i movein $range)); $($rest)*)
144    };
145
146    (($($vars:tt),*); ($($clauses:tt)*); $cond:expr) => {
147        _comprehension!(($($vars),*); ($($clauses)* ($cond));)
148    };
149
150    (($($vars:tt),*); ($($clauses:tt)*); $cond:expr, $($rest:tt)*) => {
151        _comprehension!(($($vars),*); ($($clauses)* ($cond)); $($rest)*)
152    };
153
154    (($($vars:tt),*); ($(($($clauses:tt)+))*);) => {
155        _comprehension_iter!((); $($($clauses)+,)* ($($vars),*)).unwrap()
156    };
157}
158
159/// A comprehension for constructing an iterator.
160///
161/// This macro generates a sequence of elements (i.e. an `Iterator`) using
162/// generating sequences and conditional filters. The iterator receives a tuple
163/// consisting of the variables in the range expressions.
164///
165/// # Example
166/// ```
167/// use iter_comprehensions::comprehension;
168/// let mut result = vec![];
169/// for (i,j) in comprehension!(i in 0..5, j in i+1..5, (j + i) % 2 == 0) {
170///     result.push((i,j));
171/// }
172/// assert_eq!(result, vec![(0, 2), (0, 4), (1, 3), (2, 4)]);
173/// ```
174#[macro_export(local_inner_macros)]
175macro_rules! comprehension {
176    ($($rest:tt)*) => {
177        _comprehension!((); (); $($rest)*)
178    }
179}
180
181#[doc(hidden)]
182#[macro_export(local_inner_macros)]
183macro_rules! _map {
184    (($($vars:tt),*); ($($clauses:tt)*); $i:tt in $range:expr, $($rest:tt)*) => {
185        _map!(($($vars,)* $i); ($($clauses)* ($i in $range)); $($rest)*)
186    };
187
188    (($($vars:tt),*); ($($clauses:tt)*); $cond:expr, $($rest:tt)*) => {
189        _map!(($($vars),*); ($($clauses)* ($cond)); $($rest)*)
190    };
191
192    (($var:tt); ($(($($clauses:tt)+))*); $e:expr) => {
193        _comprehension_iter!((); $($($clauses)+,)* $var).unwrap().map(|$var| $e)
194    };
195
196    (($($vars:tt),*); ($(($($clauses:tt)+))*); $e:expr) => {
197        _comprehension_iter!((); $($($clauses)+,)* ($($vars),*)).unwrap().map(|($($vars),*)| $e)
198    };
199}
200
201/// A map comprehension.
202///
203/// A sequence of generating expressions and boolean conditions for generating a
204/// sequence of values. The two possible macro calls
205/// ```no_test
206/// map!(EXPR; i1 in RANGE1, COND2, ..., ik in RANGEk)
207/// map!(i1 in RANGE1, COND2, ..., ik in RANGEk, EXPR)
208/// ```
209/// is roughly the same as
210/// ```no_test
211/// comprehensions!(i1 in RANGE1, COND2, ..., ik in RANGEk).map(|(i1, i2, ..., ik)| EXPR)
212/// ```
213///
214/// # Example
215///
216/// ```
217/// use iter_comprehensions::map;
218/// let mut it = map!(4*i + j; i in 0..5, j in 0..5, i < j);
219/// assert_eq!(it.next(), Some(1));
220/// assert_eq!(it.next(), Some(2));
221/// assert_eq!(it.next(), Some(3));
222/// assert_eq!(it.next(), Some(4));
223/// assert_eq!(it.next(), Some(6));
224/// assert_eq!(it.next(), Some(7));
225/// assert_eq!(it.next(), Some(8));
226/// assert_eq!(it.next(), Some(11));
227/// assert_eq!(it.next(), Some(12));
228/// assert_eq!(it.next(), Some(16));
229/// assert_eq!(it.next(), None);
230/// ```
231
232#[macro_export(local_inner_macros)]
233macro_rules! map {
234    ($e:expr; $($rest:tt)*) => {
235        map!($($rest)*, $e)
236    };
237    ($($rest:tt)*) => {
238        _map!((); (); $($rest)*)
239    }
240}
241
242/// Extension of `vec!` with comprehensions.
243///
244/// This is a short form for `map!(...).collect::<Vec<_>>()`.
245///
246/// For convenience the `vec!` macro also supports the syntax of the
247/// `std::vec!`, i.e. `vec![0, 1, 2]` and `vec![x; 5]`.
248///
249/// # Example
250///
251/// ```
252/// use iter_comprehensions::vec;
253/// assert_eq!(vec!(4*i + j; i in 0..5, j in 0..5, i < j),
254///            std::vec![1, 2, 3, 4, 6, 7, 8, 11, 12, 16]);
255/// ```
256#[macro_export(local_inner_macros)]
257macro_rules! vec {
258    ($e:expr; $i:tt in $range:expr) => {
259        map!($e; $i in $range).collect::<Vec<_>>()
260    };
261    ($e:expr; $i:tt in $range:expr, $($rest:tt)*) => {
262        map!($e; $i in $range, $($rest)*).collect::<Vec<_>>()
263    };
264    ($($rest:tt)*) => {
265        std::vec!($($rest)*)
266    };
267}
268
269/// A sum expression.
270///
271/// The two possible macro calls
272/// ```no_test
273/// sum!(EXPR; i1 in RANGE1, COND2, ..., ik in RANGEk)
274/// sum!(i1 in RANGE1, COND2, ..., ik in RANGEk, EXPR)
275/// ```
276/// are roughly equivalent to
277/// ```no_test
278/// map!(EXPR; i1 in RANGE1, COND2, ..., ik in RANGEk).sum()
279/// map!(i1 in RANGE1, COND2, ..., ik in RANGEk, EXPR).sum()
280/// ```
281///
282/// # Example
283///
284/// The following expression corresponds to the mathematical expression
285/// $\\sum_{i=1}^{10} i$.
286///
287/// ```
288/// use iter_comprehensions::sum;
289/// assert_eq!(sum!(i in 1..=10, i), 55);
290/// assert_eq!(sum!(i; i in 1..=10), 55);
291/// ```
292///
293/// It is also possible to specify the return type of the sum in case it
294/// cannot be inferred automatically.
295///
296/// ```
297/// use iter_comprehensions::sum;
298/// assert_eq!(sum!(usize : i; i in 1..=10), 55);
299/// ```
300#[macro_export(local_inner_macros)]
301macro_rules! sum {
302    ($t:ty : $e:expr; $($rest:tt)*) => { sum!($t : $($rest)*, $e) };
303    ($t:ty : $($rest:tt)*) => { map!($($rest)*).sum::<$t>() };
304    ($e:expr; $($rest:tt)*) => { sum!($($rest)*, $e) };
305    ($($rest:tt)*) => { $crate::sumiter(map!($($rest)*)) };
306}
307
308/// A product expression.
309///
310/// The two possible macro calls
311/// ```no_test
312/// product!(EXPR; i1 in RANGE1, COND2, ..., ik in RANGEk)
313/// product!(i1 in RANGE1, COND2, ..., ik in RANGEk, EXPR)
314/// ```
315/// are roughly equivalent to
316/// ```no_test
317/// map!(EXPR; i1 in RANGE1, COND2, ..., ik in RANGEk).product()
318/// map!(i1 in RANGE1, COND2, ..., ik in RANGEk, EXPR).product()
319/// ```
320///
321/// # Example
322///
323/// The following expression corresponds to the mathematical expression
324/// $\\prod_{i=1}^5 i$.
325///
326/// ```
327/// use iter_comprehensions::product;
328/// assert_eq!(product!(i in 1..=5, i), 120);
329/// assert_eq!(product!(i; i in 1..=5), 120);
330/// ```
331///
332/// It is also possible to specify the return type of the product in case it
333/// cannot be inferred automatically.
334/// ```
335/// use iter_comprehensions::product;
336/// assert_eq!(product!(usize : i; i in 1..=5), 120);
337/// ```
338#[macro_export(local_inner_macros)]
339macro_rules! product {
340    ($t:ty : $e:expr; $($rest:tt)*) => { product!($t : $($rest)*, $e) };
341    ($t:ty : $($rest:tt)*) => { map!($($rest)*).product::<$t>() };
342    ($e:expr; $($rest:tt)*) => { product!($($rest)*, $e) };
343    ($($rest:tt)*) => { $crate::productiter(map!($($rest)*)) };
344}
345
346#[doc(hidden)]
347pub fn sumiter<T, I>(it: I) -> T
348where
349    T: Sum<T>,
350    I: Iterator<Item = T>,
351{
352    it.sum()
353}
354
355#[doc(hidden)]
356pub fn productiter<T, I>(it: I) -> T
357where
358    T: Product<T>,
359    I: Iterator<Item = T>,
360{
361    it.product()
362}
363
364#[cfg(test)]
365mod tests {
366    #[test]
367    fn test_comprehension() {
368        let mut it = comprehension!(k in 0..3);
369        assert_eq!(it.next(), Some(0));
370        assert_eq!(it.next(), Some(1));
371        assert_eq!(it.next(), Some(2));
372        assert_eq!(it.next(), None);
373
374        let mut it = comprehension!(i in 0..3, j in 0..3);
375        assert_eq!(it.next(), Some((0, 0)));
376        assert_eq!(it.next(), Some((0, 1)));
377        assert_eq!(it.next(), Some((0, 2)));
378        assert_eq!(it.next(), Some((1, 0)));
379        assert_eq!(it.next(), Some((1, 1)));
380        assert_eq!(it.next(), Some((1, 2)));
381        assert_eq!(it.next(), Some((2, 0)));
382        assert_eq!(it.next(), Some((2, 1)));
383        assert_eq!(it.next(), Some((2, 2)));
384        assert_eq!(it.next(), None);
385
386        let mut it = comprehension!(i in 0..3, i % 2 == 0, j in 0..3);
387        assert_eq!(it.next(), Some((0, 0)));
388        assert_eq!(it.next(), Some((0, 1)));
389        assert_eq!(it.next(), Some((0, 2)));
390        assert_eq!(it.next(), Some((2, 0)));
391        assert_eq!(it.next(), Some((2, 1)));
392        assert_eq!(it.next(), Some((2, 2)));
393        assert_eq!(it.next(), None);
394
395        let mut it = comprehension!(i in 0..3, i % 2 == 0, j in 0..3, i + j < 4);
396        assert_eq!(it.next(), Some((0, 0)));
397        assert_eq!(it.next(), Some((0, 1)));
398        assert_eq!(it.next(), Some((0, 2)));
399        assert_eq!(it.next(), Some((2, 0)));
400        assert_eq!(it.next(), Some((2, 1)));
401        assert_eq!(it.next(), None);
402
403        assert_eq!(
404            comprehension!(i in 0..3, j in 0..3, k in 0..2)
405                .map(|(i, j, k)| i + j + k)
406                .collect::<Vec<_>>(),
407            vec![0, 1, 1, 2, 2, 3, 1, 2, 2, 3, 3, 4, 2, 3, 3, 4, 4, 5]
408        );
409    }
410
411    #[test]
412    fn test_map() {
413        let mut it = map!(k in 0..3, 2 * k);
414        assert_eq!(it.next(), Some(0));
415        assert_eq!(it.next(), Some(2));
416        assert_eq!(it.next(), Some(4));
417        assert_eq!(it.next(), None);
418
419        assert_eq!(map!(i; i in 0..5).collect::<Vec<_>>(), vec![0, 1, 2, 3, 4]);
420        assert_eq!(map!(i; i in 0..5, i % 2 == 0).collect::<Vec<_>>(), vec![0, 2, 4]);
421        assert_eq!(
422            map!(i*2 + j; i in 0..5, j in 0..2).collect::<Vec<_>>(),
423            (0..10).collect::<Vec<_>>()
424        );
425        assert_eq!(
426            map!((i,j); i in 0..3, j in 0..3, i < j).collect::<Vec<_>>(),
427            vec![(0, 1), (0, 2), (1, 2)]
428        );
429        assert_eq!(
430            map!((i,j); i in 0..4, i % 2 == 0, j in 0..4, i < j).collect::<Vec<_>>(),
431            vec![(0, 1), (0, 2), (0, 3), (2, 3)]
432        );
433    }
434
435    #[test]
436    fn vector() {
437        assert_eq!(vec!(i; i in 0..5), vec![0, 1, 2, 3, 4]);
438    }
439
440    #[test]
441    fn sum() {
442        assert_eq!(sum!(i; i in 0..5), 10);
443        assert_eq!(sum!(i in 0..5, i), 10);
444    }
445
446    #[test]
447    fn product() {
448        assert_eq!(product!(i; i in 1..5), 24);
449        assert_eq!(product!(i in 1..5, i), 24);
450    }
451
452    #[test]
453    fn sumvec() {
454        let x = vec![1; 10];
455        assert_eq!(sum!(x[i]; i in 0..10), 10);
456        assert_eq!(sum!(i in 0..10, x[i]), 10);
457    }
458
459    #[test]
460    fn vec() {
461        assert_eq!(vec![i; i in 0..5], vec![0, 1, 2, 3, 4]);
462    }
463
464    #[test]
465    fn test_mut() {
466        let mut x = vec![0, 1, 2];
467        assert_eq!(
468            18 * 19 / 2 - 6,
469            sum!({x.push(42 + i + j); x.len()}; i in 0..5, j in 0..3)
470        );
471        assert_eq!(
472            x,
473            vec![0, 1, 2, 42, 43, 44, 43, 44, 45, 44, 45, 46, 45, 46, 47, 46, 47, 48]
474        )
475    }
476
477    #[test]
478    fn test_tuple() {
479        let pairs = [(1,2), (3,4), (5,6)];
480        let mut it = map!(format!("{}+{}", i, j); (i, j) in pairs);
481        assert_eq!(it.next(), Some("1+2".to_string()));
482        assert_eq!(it.next(), Some("3+4".to_string()));
483        assert_eq!(it.next(), Some("5+6".to_string()));
484        assert_eq!(it.next(), None);
485    }
486
487    #[test]
488    fn test_tuple2() {
489        let pairs = [(1,2), (3,4), (5,6)];
490        let mut it = map!((i, j) in pairs, format!("{}+{}", i, j));
491        assert_eq!(it.next(), Some("1+2".to_string()));
492        assert_eq!(it.next(), Some("3+4".to_string()));
493        assert_eq!(it.next(), Some("5+6".to_string()));
494        assert_eq!(it.next(), None);
495    }
496
497    #[test]
498    fn test_sum() {
499        let pairs = [(1,2), (3,4), (5,6)];
500        assert_eq!(44, sum!(i * j; (i,j) in pairs));
501        assert_eq!(44, sum!((i,j) in pairs, i * j));
502    }
503}