diff --git a/benches/par_rayon.rs b/benches/par_rayon.rs index 03de37825..e8c4cfef3 100644 --- a/benches/par_rayon.rs +++ b/benches/par_rayon.rs @@ -131,7 +131,7 @@ fn rayon_add(bench: &mut Bencher) { let c = Array2::::zeros((ADDN, ADDN)); let d = Array2::::zeros((ADDN, ADDN)); bench.iter(|| { - par_azip!(mut a, b, c, d in { + par_azip!((a in &mut a, b in &b, c in &c, d in &d) { *a += b.exp() + c.exp() + d.exp(); }); }); diff --git a/src/parallel/zipmacro.rs b/src/parallel/zipmacro.rs index a61ae88d4..99c3807f8 100644 --- a/src/parallel/zipmacro.rs +++ b/src/parallel/zipmacro.rs @@ -13,10 +13,12 @@ /// This is a version of the [`azip`] macro that requires the crate feature /// `rayon` to be enabled. /// +/// See the [`azip`] macro for more details about the macro syntax! +/// /// This example: /// /// ```rust,ignore -/// par_azip!(mut a, b, c in { *a = b + c }) +/// par_azip!((a in &mut a, &b in &b, &c in &c) { *a = b + c }) /// ``` /// /// Is equivalent to: @@ -47,60 +49,13 @@ /// // Compute a simple ternary operation: /// // elementwise addition of b and c, stored in a /// -/// par_azip!(mut a, b, c in { *a = b + c }); +/// par_azip!((a in &mut a, &b in &b, &c in &c) *a = b + c); /// /// assert_eq!(a, &b + &c); /// } /// ``` macro_rules! par_azip { - // Build Zip Rule (index) - (@parse [index => $a:expr, $($aa:expr,)*] $t1:tt in $t2:tt) => { - $crate::par_azip!(@finish ($crate::Zip::indexed($a)) [$($aa,)*] $t1 in $t2) - }; - // Build Zip Rule (no index) - (@parse [$a:expr, $($aa:expr,)*] $t1:tt in $t2:tt) => { - $crate::par_azip!(@finish ($crate::Zip::from($a)) [$($aa,)*] $t1 in $t2) - }; - // Build Finish Rule (both) - (@finish ($z:expr) [$($aa:expr,)*] [$($p:pat,)+] in { $($t:tt)*}) => { - use $crate::parallel::prelude::*; - #[allow(unused_mut)] - ($z) - $( - .and($aa) - )* - .par_apply(|$($p),+| { - $($t)* - }) - }; - // parsing stack: [expressions] [patterns] (one per operand) - // index uses empty [] -- must be first - (@parse [] [] index $i:pat, $($t:tt)*) => { - $crate::par_azip!(@parse [index =>] [$i,] $($t)*); - }; - (@parse [$($exprs:tt)*] [$($pats:tt)*] mut $x:ident ($e:expr) $($t:tt)*) => { - $crate::par_azip!(@parse [$($exprs)* $e,] [$($pats)* mut $x,] $($t)*); - }; - (@parse [$($exprs:tt)*] [$($pats:tt)*] mut $x:ident $($t:tt)*) => { - $crate::par_azip!(@parse [$($exprs)* &mut $x,] [$($pats)* mut $x,] $($t)*); - }; - (@parse [$($exprs:tt)*] [$($pats:tt)*] , $($t:tt)*) => { - $crate::par_azip!(@parse [$($exprs)*] [$($pats)*] $($t)*); - }; - (@parse [$($exprs:tt)*] [$($pats:tt)*] ref $x:ident ($e:expr) $($t:tt)*) => { - $crate::par_azip!(@parse [$($exprs)* $e,] [$($pats)* $x,] $($t)*); - }; - (@parse [$($exprs:tt)*] [$($pats:tt)*] ref $x:ident $($t:tt)*) => { - $crate::par_azip!(@parse [$($exprs)* &$x,] [$($pats)* $x,] $($t)*); - }; - (@parse [$($exprs:tt)*] [$($pats:tt)*] $x:ident ($e:expr) $($t:tt)*) => { - $crate::par_azip!(@parse [$($exprs)* $e,] [$($pats)* &$x,] $($t)*); - }; - (@parse [$($exprs:tt)*] [$($pats:tt)*] $x:ident $($t:tt)*) => { - $crate::par_azip!(@parse [$($exprs)* &$x,] [$($pats)* &$x,] $($t)*); - }; - (@parse [$($exprs:tt)*] [$($pats:tt)*] $($t:tt)*) => { }; ($($t:tt)*) => { - $crate::par_azip!(@parse [] [] $($t)*); - } + $crate::azip!(@build par_apply $($t)*) + }; } diff --git a/src/zip/zipmacro.rs b/src/zip/zipmacro.rs index 235d93270..ea616a05e 100644 --- a/src/zip/zipmacro.rs +++ b/src/zip/zipmacro.rs @@ -101,32 +101,30 @@ /// ``` #[macro_export] macro_rules! azip { - // Indexed with a single producer and no trailing comma. - ((index $index:pat, $first_pat:pat in $first_prod:expr) $body:expr) => { - $crate::Zip::indexed($first_prod).apply(|$index, $first_pat| $body) + // Indexed with a single producer + // we allow an optional trailing comma after the producers in each rule. + (@build $apply:ident (index $index:pat, $first_pat:pat in $first_prod:expr $(,)?) $body:expr) => { + $crate::Zip::indexed($first_prod).$apply(|$index, $first_pat| $body) }; - // Indexed with more than one producer and no trailing comma. - ((index $index:pat, $first_pat:pat in $first_prod:expr, $($pat:pat in $prod:expr),*) $body:expr) => { + // Indexed with more than one producer + (@build $apply:ident (index $index:pat, $first_pat:pat in $first_prod:expr, $($pat:pat in $prod:expr),* $(,)?) $body:expr) => { $crate::Zip::indexed($first_prod) $(.and($prod))* - .apply(|$index, $first_pat, $($pat),*| $body) + .$apply(|$index, $first_pat, $($pat),*| $body) }; - // Indexed with trailing comma. - ((index $index:pat, $($pat:pat in $prod:expr),+,) $body:expr) => { - azip!((index $index, $($pat in $prod),+) $body) + // Unindexed with a single producer + (@build $apply:ident ($first_pat:pat in $first_prod:expr $(,)?) $body:expr) => { + $crate::Zip::from($first_prod).$apply(|$first_pat| $body) }; - // Unindexed with a single producer and no trailing comma. - (($first_pat:pat in $first_prod:expr) $body:expr) => { - $crate::Zip::from($first_prod).apply(|$first_pat| $body) - }; - // Unindexed with more than one producer and no trailing comma. - (($first_pat:pat in $first_prod:expr, $($pat:pat in $prod:expr),*) $body:expr) => { + // Unindexed with more than one producer + (@build $apply:ident ($first_pat:pat in $first_prod:expr, $($pat:pat in $prod:expr),* $(,)?) $body:expr) => { $crate::Zip::from($first_prod) $(.and($prod))* - .apply(|$first_pat, $($pat),*| $body) + .$apply(|$first_pat, $($pat),*| $body) }; - // Unindexed with trailing comma. - (($($pat:pat in $prod:expr),+,) $body:expr) => { - azip!(($($pat in $prod),+) $body) + // catch-all rule + (@build $($t:tt)*) => { compile_error!("Invalid syntax in azip!()") }; + ($($t:tt)*) => { + $crate::azip!(@build apply $($t)*) }; } diff --git a/tests/azip.rs b/tests/azip.rs index 84bb7a28a..885db85b7 100644 --- a/tests/azip.rs +++ b/tests/azip.rs @@ -49,6 +49,16 @@ fn test_azip2_3() { assert!(a != b); } +#[test] +fn test_azip_syntax_trailing_comma() { + let mut b = Array::::zeros((5, 5)); + let mut c = Array::::ones((5, 5)); + let a = b.clone(); + azip!((b in &mut b, c in &mut c, ) swap(b, c)); + assert_eq!(a, c); + assert!(a != b); +} + #[test] #[cfg(feature = "approx")] fn test_azip2_sum() { diff --git a/tests/par_azip.rs b/tests/par_azip.rs index da1bbe72c..e5dc02c4e 100644 --- a/tests/par_azip.rs +++ b/tests/par_azip.rs @@ -1,5 +1,6 @@ #![cfg(feature = "rayon")] +#[cfg(feature = "approx")] use itertools::enumerate; use ndarray::parallel::prelude::*; use ndarray::prelude::*; @@ -9,7 +10,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; fn test_par_azip1() { let mut a = Array::zeros(62); let b = Array::from_elem(62, 42); - par_azip!(mut a in { *a = 42 }); + par_azip!((a in &mut a) { *a = 42 }); assert_eq!(a, b); } @@ -17,7 +18,7 @@ fn test_par_azip1() { fn test_par_azip2() { let mut a = Array::zeros((5, 7)); let b = Array::from_shape_fn(a.dim(), |(i, j)| 1. / (i + 2 * j) as f32); - par_azip!(mut a, b in { *a = b; }); + par_azip!((a in &mut a, &b in &b, ) *a = b ); assert_eq!(a, b); } @@ -33,7 +34,7 @@ fn test_par_azip3() { *elt = i as f32; } - par_azip!(mut a (&mut a[..]), b (&b[..]), mut c (&mut c[..]) in { + par_azip!((a in &mut a[..], &b in &b[..], c in &mut c[..]) { *a += b / 10.; *c = a.sin(); }); @@ -48,7 +49,7 @@ fn test_zip_dim_mismatch_1() { let mut d = a.raw_dim(); d[0] += 1; let b = Array::from_shape_fn(d, |(i, j)| 1. / (i + 2 * j) as f32); - par_azip!(mut a, b in { *a = b; }); + par_azip!((a in &mut a, &b in &b) { *a = b; }); } #[test] @@ -59,7 +60,7 @@ fn test_indices_1() { } let count = AtomicUsize::new(0); - par_azip!(index i, elt (&a1) in { + par_azip!((index i, &elt in &a1) { count.fetch_add(1, Ordering::SeqCst); assert_eq!(elt, i); });