Reputation: 2929
Running example on play.rust-lang.org
fn main() {
show({
let number = b"123456";
for sequence in number.windows(6) {
let product = sequence.iter().fold(1, |a, &b| a * (b as u64));
println!("product of {:?} is {}", sequence, product);
}
});
}
Instead of having an output like "product of [49, 50, 51, 52, 53, 54] is 15312500000" I need the normal numbers in the brackets and the normalized result for the product.
Trying around with - b'0'
to subtract the 48 to get the normal digits in line 5 doesn't work, i.e.
a * ((b as u64) -b'0')
or
(a - b'0') * (b as u64)
Seems I'm missing something here, for example I have no idea what exactly are the 'a' and 'b' values in the fold(). Can anyone enlighten me? :)
Upvotes: 2
Views: 728
Reputation: 20579
Nowadays, an alternative is product
+ to_digit
: (itertools
was used to print the contents of the iterator)
use {itertools::Itertools, std::char};
fn main() {
let number = b"123456";
let sequence = number
.iter()
.map(|&c| u64::from(char::from(c).to_digit(10).expect("not a digit")));
let product: u64 = sequence.clone().product();
println!("product of {:?} is {}", sequence.format(", "), product);
}
Upvotes: 0
Reputation: 430921
As an alternative to fold
, you can use map
and MultiplicativeIterator::product
. I find that the two steps help make it clearer what is happening.
#![feature(core)]
use std::iter::MultiplicativeIterator;
fn main() {
let number = b"123456";
for sequence in number.windows(6) {
let product = sequence.iter().map(|v| (v - b'0') as u64).product();
println!("product of {:?} is {}", sequence, product);
}
}
You could even choose to split up the resizing from u8
to u64
:
sequence.iter().map(|v| v - b'0').map(|v| v as u64).product();
Upvotes: 0
Reputation: 102096
Looking at the signature of fold
, we can see that it takes two arguments:
fn fold<B, F>(self, init: B, f: F) -> B
where F: FnMut(B, Self::Item) -> B
init
, which is of some arbitrary type B
, and f
, which is a closure that takes a B
value and an element from the iterator, in order to compute a new B
value. The whole function returns a B
. The types are strongly suggestive of what happens: the closure f
is repeatedly called on successive elements of the iterator, passing the computed B
value into the next f
call. Checking the implementation confirms this suspicion:
let mut accum = init;
for x in self {
accum = f(accum, x);
}
accum
It runs through the iterator, passing the accumulated state into the closure in order to compute the next state.
First things first, lets put the type on the fold
call:
let product = sequence.iter().fold(1, |a: u64, &b: &u8| a * (b as u64));
That is, the B
type we want is u64
(that's what our final product will be), and the item type of the iterator is &u8
, a reference to a byte.
Now, we can manually inline the definition of fold
to compute product
to try to clarify the desired behaviour (I'm ignoring the normalisation for now):
let mut accum = 1;
for x in sequence.iter() {
accum = { // the closure
let a: u64 = accum;
let &b: &u8 = x;
a * b as u64
}
}
let product = accum;
Simplifying:
let mut product = 1;
for &b in sequence.iter() {
product = product * (b as u64)
}
Hopefully this makes it clearer what needs to happen: b
runs across each byte, and so it is the value that needs adjustment, to bring the ASCII encoded value down to the expected 0..10 range.
So, you were right with:
a * ((b as u64) -b'0')
However, the details mean that fails to compile, with a type error: b'0'
has type u8
, but b as u64
as type u64
, and it's not legal to use -
with u64
and u8
. Moving the normalisation to happen before the u64
cast will ensure this works ok, since then you're subtracting b
(which is a u8
) and a u8
:
product * (b - b'0') as u64
All in all, the fold
might look clearer (and actually work) as:
let product = sequence.iter()
.fold(1, |prod, &byte| prod * (byte - b'0') as u64);
(I apologise for giving you such confusing code on IRC.)
Upvotes: 3