List comprehensions: Difference between revisions
Content added Content deleted
SqrtNegInf (talk | contribs) m (→{{header|Sidef}}: Fix link: Perl 6 --> Raku) |
No edit summary |
||
Line 2,169: | Line 2,169: | ||
[9,12,15] |
[9,12,15] |
||
[12,16,20]</pre> |
[12,16,20]</pre> |
||
=={{header|Rust}}== |
|||
Rust doesn't have comprehension-syntax, but it has powerful lazy-iteration mechanisms topped with a powerful macro system, as such you can implement comprehensions on top of the language fairly easily. |
|||
=== Iterator === |
|||
First using the built-in iterator trait, we can simply flat-map and then filter-map: |
|||
<lang rust>fn pyth(n: u32) -> impl Iterator<Item = [u32; 3]> { |
|||
(1..=n).flat_map(move |x| { |
|||
(x..=n).flat_map(move |y| { |
|||
(y..=n).filter_map(move |z| { |
|||
if x.pow(2) + y.pow(2) == z.pow(2) { |
|||
Some([x, y, z]) |
|||
} else { |
|||
None |
|||
} |
|||
}) |
|||
}) |
|||
}) |
|||
}</lang> |
|||
* Using <code>flat_map</code> we can map and flatten an iterator. |
|||
* Use <code>filter_map</code> we can return an <code>Option</code> where <code>Some(value)</code> is returned or <code>None</code> isn't. Technically we could also use <code>flat_map</code> instead, because <code>Option</code> implements <code>IntoIterator</code>. |
|||
* Using the <code>impl Trait</code> syntax, we return the <code>Iterator</code> generically, because it's statically known. |
|||
* Using the <code>move</code> syntax on the closure, means the closure will take ownership of the values (copying them) which is what we wan't because the captured variables will be returned multiple times. |
|||
=== Comprehension Macro === |
|||
Using the above and <code>macro_rules!</code> we can implement comprehension with a reasonably sized macro: |
|||
<lang rust>macro_rules! comp { |
|||
($e:expr, for $x:pat in $xs:expr $(, if $c:expr)?) => {{ |
|||
$xs.filter_map(move |$x| if $($c &&)? true { Some($e) } else { None }) |
|||
}}; |
|||
($e:expr, for $x:pat in $xs:expr $(, for $y:pat in $ys:expr)+ $(, if $c:expr)?) => {{ |
|||
$xs.flat_map(move |$x| comp!($e, $(for $y in $ys),+ $(, if $c)?)) |
|||
}}; |
|||
}</lang> |
|||
The way to understand a Rust macro is it's a bit like regular expressions. The input matches a type of token, and expands it into the block, for example take the follow pattern: |
|||
<lang rust>($e:expr, for $x:pat in $xs:expr $(, if $c:expr)?)</lang> |
|||
# matches an <code>expr</code> expression, defines it to <code>$e</code> |
|||
# matches the tokens <code>, for</code> |
|||
# matches a <code>pat</code> pattern, defines it to <code>$x</code> |
|||
# matches the tokens <code>in</code> |
|||
# matches an <code>expr</code> expression, defines it to <code>$xs</code> |
|||
# matches <code>$(..)?</code> optional group |
|||
## patches tokens <code>, if</code> |
|||
## matches an <code>expr</code> expression, defines it to <code>$c</code> |
|||
This makes the two following blocks equivalent: |
|||
<lang rust>comp!(x, for x in 0..10, if x != 5)</lang> |
|||
<lang rust>(0..10).filter_map(move |x| { |
|||
if x != 5 && true { |
|||
Some(x) |
|||
} else { |
|||
None |
|||
} |
|||
})</lang> |
|||
The most interesting part of <code>comp!</code> is that it's a recursive macro (it expands within itself), and that means it can handle any number of iterators as inputs. |
|||
=== Iterator Comprehension === |
|||
The pythagorean function could as such be defined as the following: |
|||
<lang rust>fn pyth(n: u32) -> impl Iterator<Item = [u32; 3]> { |
|||
comp!( |
|||
[x, y, z], |
|||
for x in 1..=n, |
|||
for y in x..=n, |
|||
for z in y..=n, |
|||
if x.pow(2) + y.pow(2) == z.pow(2) |
|||
) |
|||
}</lang> |
|||
=={{header|Scala}}== |
=={{header|Scala}}== |