Vega
Vega

Reputation: 2929

Normalise ASCII numbers to digit numbers

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

Answers (3)

L. F.
L. F.

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);
}

(playground)

Upvotes: 0

Shepmaster
Shepmaster

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

huon
huon

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

Related Questions