|
1 | | -#[macro_export] |
2 | 1 | /// Array zip macro: lock step function application across several arrays and |
3 | 2 | /// producers. |
4 | 3 | /// |
|
7 | 6 | /// This example: |
8 | 7 | /// |
9 | 8 | /// ```rust,ignore |
10 | | -/// azip!(mut a, b, c in { *a = b + c }) |
| 9 | +/// azip!((a in &mut a, &b in &b, &c in &c) *a = b + c); |
11 | 10 | /// ``` |
12 | 11 | /// |
13 | 12 | /// Is equivalent to: |
14 | 13 | /// |
15 | 14 | /// ```rust,ignore |
16 | 15 | /// Zip::from(&mut a).and(&b).and(&c).apply(|a, &b, &c| { |
17 | | -/// *a = b + c; |
| 16 | +/// *a = b + c |
18 | 17 | /// }); |
19 | | -/// |
20 | 18 | /// ``` |
21 | 19 | /// |
22 | | -/// Explanation of the shorthand for captures: |
23 | | -/// |
24 | | -/// + `mut a`: the producer is `&mut a` and the variable pattern is `mut a`. |
25 | | -/// + `b`: the producer is `&b` and the variable pattern is `&b` (same for `c`). |
| 20 | +/// The syntax is either |
26 | 21 | /// |
27 | | -/// The syntax is `azip!(` *[* `index` *pattern* `,`*] capture [*`,` *capture [*`,` *...] ]* `in {` *expression* `})` |
28 | | -/// where the captures are a sequence of pattern-like items that indicate which |
29 | | -/// arrays are used for the zip. The *expression* is evaluated elementwise, |
30 | | -/// with the value of an element from each producer in their respective variable. |
| 22 | +/// `azip!((` *pat* `in` *expr* `,` *[* *pat* `in` *expr* `,` ... *]* `)` *body_expr* `)` |
31 | 23 | /// |
32 | | -/// More capture rules: |
| 24 | +/// or, to use `Zip::indexed` instead of `Zip::from`, |
33 | 25 | /// |
34 | | -/// + `ref c`: the producer is `&c` and the variable pattern is `c`. |
35 | | -/// + `mut a (expr)`: the producer is `expr` and the variable pattern is `mut a`. |
36 | | -/// + `b (expr)`: the producer is `expr` and the variable pattern is `&b`. |
37 | | -/// + `ref c (expr)`: the producer is `expr` and the variable pattern is `c`. |
| 26 | +/// `azip!((index` *pat* `,` *pat* `in` *expr* `,` *[* *pat* `in` *expr* `,` ... *]* `)` *body_expr* `)` |
38 | 27 | /// |
39 | | -/// Special rule: |
40 | | -/// |
41 | | -/// + `index i`: Use `Zip::indexed` instead. `i` is a pattern -- it can be |
42 | | -/// a single variable name or something else that pattern matches the index. |
43 | | -/// This rule must be the first if it is used, and it must be followed by |
44 | | -/// at least one other rule. |
| 28 | +/// The *expr* are expressions whose types must implement `IntoNdProducer`, the |
| 29 | +/// *pat* are the patterns of the parameters to the closure called by |
| 30 | +/// `Zip::apply`, and *body_expr* is the body of the closure called by |
| 31 | +/// `Zip::apply`. You can think of each *pat* `in` *expr* as being analogous to |
| 32 | +/// the `pat in expr` of a normal loop `for pat in expr { statements }`: a |
| 33 | +/// pattern, followed by `in`, followed by an expression that implements |
| 34 | +/// `IntoNdProducer` (analogous to `IntoIterator` for a `for` loop). |
45 | 35 | /// |
46 | 36 | /// **Panics** if any of the arrays are not of the same shape. |
47 | 37 | /// |
|
68 | 58 | /// |
69 | 59 | /// // Example 1: Compute a simple ternary operation: |
70 | 60 | /// // elementwise addition of b and c, stored in a |
71 | | -/// azip!(mut a, b, c in { *a = b + c }); |
| 61 | +/// azip!((a in &mut a, &b in &b, &c in &c) *a = b + c); |
72 | 62 | /// |
73 | 63 | /// assert_eq!(a, &b + &c); |
74 | 64 | /// |
75 | 65 | /// // Example 2: azip!() with index |
76 | | -/// azip!(index (i, j), b, c in { |
| 66 | +/// azip!((index (i, j), &b in &b, &c in &c) { |
77 | 67 | /// a[[i, j]] = b - c; |
78 | 68 | /// }); |
79 | 69 | /// |
|
87 | 77 | /// assert_eq!(a, &b * &c); |
88 | 78 | /// |
89 | 79 | /// |
90 | | -/// // Since this function borrows its inputs, captures must use the x (x) pattern |
91 | | -/// // to avoid the macro's default rule that autorefs the producer. |
| 80 | +/// // Since this function borrows its inputs, the `IntoNdProducer` |
| 81 | +/// // expressions don't need to explicitly include `&mut` or `&`. |
92 | 82 | /// fn borrow_multiply(a: &mut M, b: &M, c: &M) { |
93 | | -/// azip!(mut a (a), b (b), c (c) in { *a = b * c }); |
| 83 | +/// azip!((a in a, &b in b, &c in c) *a = b * c); |
94 | 84 | /// } |
95 | 85 | /// |
96 | 86 | /// |
97 | | -/// // Example 4: using azip!() with a `ref` rule |
| 87 | +/// // Example 4: using azip!() without dereference in pattern. |
98 | 88 | /// // |
99 | 89 | /// // Create a new array `totals` with one entry per row of `a`. |
100 | 90 | /// // Use azip to traverse the rows of `a` and assign to the corresponding |
101 | 91 | /// // entry in `totals` with the sum across each row. |
102 | 92 | /// // |
103 | | -/// // The row is an array view; use the 'ref' rule on the row, to avoid the |
104 | | -/// // default which is to dereference the produced item. |
105 | | -/// let mut totals = Array1::zeros(a.nrows()); |
106 | | -/// |
107 | | -/// azip!(mut totals, ref row (a.genrows()) in { |
108 | | -/// *totals = row.sum(); |
109 | | -/// }); |
| 93 | +/// // The row is an array view; it doesn't need to be dereferenced. |
| 94 | +/// let mut totals = Array1::zeros(a.rows()); |
| 95 | +/// azip!((totals in &mut totals, row in a.genrows()) *totals = row.sum()); |
110 | 96 | /// |
111 | 97 | /// // Check the result against the built in `.sum_axis()` along axis 1. |
112 | 98 | /// assert_eq!(totals, a.sum_axis(Axis(1))); |
113 | 99 | /// } |
114 | 100 | /// |
115 | 101 | /// ``` |
| 102 | +#[macro_export] |
116 | 103 | macro_rules! azip { |
117 | | - // Build Zip Rule (index) |
118 | | - (@parse [index => $a:expr, $($aa:expr,)*] $t1:tt in $t2:tt) => { |
119 | | - $crate::azip!(@finish ($crate::Zip::indexed($a)) [$($aa,)*] $t1 in $t2) |
120 | | - }; |
121 | | - // Build Zip Rule (no index) |
122 | | - (@parse [$a:expr, $($aa:expr,)*] $t1:tt in $t2:tt) => { |
123 | | - $crate::azip!(@finish ($crate::Zip::from($a)) [$($aa,)*] $t1 in $t2) |
124 | | - }; |
125 | | - // Build Finish Rule (both) |
126 | | - (@finish ($z:expr) [$($aa:expr,)*] [$($p:pat,)+] in { $($t:tt)*}) => { |
127 | | - #[allow(unused_mut)] |
128 | | - ($z) |
129 | | - $( |
130 | | - .and($aa) |
131 | | - )* |
132 | | - .apply(|$($p),+| { |
133 | | - $($t)* |
134 | | - }) |
135 | | - }; |
136 | | - // parsing stack: [expressions] [patterns] (one per operand) |
137 | | - // index uses empty [] -- must be first |
138 | | - (@parse [] [] index $i:pat, $($t:tt)*) => { |
139 | | - $crate::azip!(@parse [index =>] [$i,] $($t)*); |
140 | | - }; |
141 | | - (@parse [$($exprs:tt)*] [$($pats:tt)*] mut $x:ident ($e:expr) $($t:tt)*) => { |
142 | | - $crate::azip!(@parse [$($exprs)* $e,] [$($pats)* mut $x,] $($t)*); |
143 | | - }; |
144 | | - (@parse [$($exprs:tt)*] [$($pats:tt)*] mut $x:ident $($t:tt)*) => { |
145 | | - $crate::azip!(@parse [$($exprs)* &mut $x,] [$($pats)* mut $x,] $($t)*); |
| 104 | + // Indexed with a single producer and no trailing comma. |
| 105 | + ((index $index:pat, $first_pat:pat in $first_prod:expr) $body:expr) => { |
| 106 | + $crate::Zip::indexed($first_prod).apply(|$index, $first_pat| $body) |
146 | 107 | }; |
147 | | - (@parse [$($exprs:tt)*] [$($pats:tt)*] , $($t:tt)*) => { |
148 | | - $crate::azip!(@parse [$($exprs)*] [$($pats)*] $($t)*); |
| 108 | + // Indexed with more than one producer and no trailing comma. |
| 109 | + ((index $index:pat, $first_pat:pat in $first_prod:expr, $($pat:pat in $prod:expr),*) $body:expr) => { |
| 110 | + $crate::Zip::indexed($first_prod) |
| 111 | + $(.and($prod))* |
| 112 | + .apply(|$index, $first_pat, $($pat),*| $body) |
149 | 113 | }; |
150 | | - (@parse [$($exprs:tt)*] [$($pats:tt)*] ref $x:ident ($e:expr) $($t:tt)*) => { |
151 | | - $crate::azip!(@parse [$($exprs)* $e,] [$($pats)* $x,] $($t)*); |
| 114 | + // Indexed with trailing comma. |
| 115 | + ((index $index:pat, $($pat:pat in $prod:expr),+,) $body:expr) => { |
| 116 | + azip!((index $index, $($pat in $prod),+) $body) |
152 | 117 | }; |
153 | | - (@parse [$($exprs:tt)*] [$($pats:tt)*] ref $x:ident $($t:tt)*) => { |
154 | | - $crate::azip!(@parse [$($exprs)* &$x,] [$($pats)* $x,] $($t)*); |
| 118 | + // Unindexed with a single producer and no trailing comma. |
| 119 | + (($first_pat:pat in $first_prod:expr) $body:expr) => { |
| 120 | + $crate::Zip::from($first_prod).apply(|$first_pat| $body) |
155 | 121 | }; |
156 | | - (@parse [$($exprs:tt)*] [$($pats:tt)*] $x:ident ($e:expr) $($t:tt)*) => { |
157 | | - $crate::azip!(@parse [$($exprs)* $e,] [$($pats)* &$x,] $($t)*); |
| 122 | + // Unindexed with more than one producer and no trailing comma. |
| 123 | + (($first_pat:pat in $first_prod:expr, $($pat:pat in $prod:expr),*) $body:expr) => { |
| 124 | + $crate::Zip::from($first_prod) |
| 125 | + $(.and($prod))* |
| 126 | + .apply(|$first_pat, $($pat),*| $body) |
158 | 127 | }; |
159 | | - (@parse [$($exprs:tt)*] [$($pats:tt)*] $x:ident $($t:tt)*) => { |
160 | | - $crate::azip!(@parse [$($exprs)* &$x,] [$($pats)* &$x,] $($t)*); |
| 128 | + // Unindexed with trailing comma. |
| 129 | + (($($pat:pat in $prod:expr),+,) $body:expr) => { |
| 130 | + azip!(($($pat in $prod),+) $body) |
161 | 131 | }; |
162 | | - (@parse [$($exprs:tt)*] [$($pats:tt)*] $($t:tt)*) => { }; |
163 | | - ($($t:tt)*) => { |
164 | | - $crate::azip!(@parse [] [] $($t)*); |
165 | | - } |
166 | 132 | } |
0 commit comments