将 ASCII 数字规范化为数字

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

我不需要像“[49, 50, 51, 52, 53, 54] 的乘积是 15312500000” 这样的输出,我需要括号中的正常数字和乘积的标准化结果。 尝试使用 - b'0' 减去 48 以获得第 5 行中的正常数字不起作用,即

a * ((b as u64) -b'0')

(a - b'0') * (b as u64)

似乎我在这里遗漏了一些东西,例如我不知道 fold() 中的 'a' 和 'b' 值到底是什么。谁能启发我? :)

查看the signature of fold,我们可以看到它有两个参数:

fn fold<B, F>(self, init: B, f: F) -> B
   where F: FnMut(B, Self::Item) -> B

init,它是某种任意类型 B,和 f,它是一个闭包,它接受一个 B 值和一个来自迭代器的元素,为了计算一个新的 B 值。整个函数returns一个B。这些类型强烈暗示发生了什么:闭包 f 在迭代器的连续元素上重复调用,将计算的 B 值传递给下一个 f 调用。检查 the implementation 证实了这个怀疑:

let mut accum = init;
for x in self {
    accum = f(accum, x);
}
accum

它遍历迭代器,将累积的状态传递到闭包中以计算下一个状态。

首先,让我们将类型放在 fold 调用中:

let product = sequence.iter().fold(1, |a: u64, &b: &u8| a * (b as u64));

即我们要的B类型是u64(这就是我们最终的产品),迭代器的item类型是&u8,引用一个字节。

现在,我们可以手动内联 fold 的定义来计算 product 以尝试阐明所需的行为(我现在忽略规范化):

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;

简化:

let mut product = 1;
for &b in sequence.iter() {
    product = product * (b as u64)
}

希望这可以使需要发生的事情更清楚:b 遍历每个字节,因此它是需要调整的值,将 ASCII 编码值降低到预期的 0..10 范围.

所以,你是对的:

a * ((b as u64) -b'0')

但是,详细说明编译失败,类型错误:b'0' has type u8, but b as u64 as type u64, and it's not -u64u8 一起使用是合法的。将规范化移动到 u64 演员表之前将确保它正常工作,因为那时你要减去 b(这是一个 u8)和一个 u8:

product * (b - b'0') as u64

总而言之,fold 可能看起来更清晰(并且实际有效):

let product = sequence.iter()
    .fold(1, |prod, &byte| prod * (byte - b'0') as u64);

(很抱歉在 IRC 上给了你这么混乱的代码。)

作为 fold 的替代方法,您可以使用 map and MultiplicativeIterator::product。我发现这两个步骤有助于更清楚地了解正在发生的事情。

#![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);
    }
}

您甚至可以选择将调整大小从 u8 拆分为 u64:

sequence.iter().map(|v| v - b'0').map(|v| v as u64).product();

如今,另一种方法是 product + to_digit: (itertools 用于打印迭代器的内容)

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)