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}