[炼手Rust]ISBN 校验器

152 阅读2分钟

Hi,大家好,这里是炼手Rust专栏,我是xiaoK,今天来看个编程挑战:ISBN 校验器。

2946652050_bafdd064ef_o.jpg

提问

ISBN-10 校验器用于验证图书标识码。这些标识码通常包含破折号,看起来像:3-598-21508-8。

ISBN-10 格式为 9 位数字(0 到 9)加一个校验字符(数字或仅 X)。如果校验字符是 X,则表示值 10。可以使用连字符也可以不使用连字符来表示,并且可以通过以下公式检查其有效性:

(d₁ * 10 + d2 * 9 + d₃ * 8 + d₄ * 7 + d₅ * 6 + d₆ * 5 + d₇ * 4 + d₈ * 3 + d₉ * 2 + d₁₀ * 1) mod 11 == 0

如果结果为0,则它是有效的ISBN-10,否则它是无效的。

例:

input:3-598-21508-8
output:true,因为 (3 * 10 + 5 * 9 + 9 * 8 + 8 * 7 + 2 * 6 + 1 * 5 + 5 * 4 + 0 * 3 + 8 * 2 + 8 * 1) mod 11 == 0 。

input:3-598-21507-A
ouptut:false,因为 A 不是一个可用字符。

input:3-598-2X507-9
output: false,因为 X 不能出现在非校验位(也就是最后一位)。

模板:

pub fn is_valid_isbn(isbn: &str) -> bool {
    todo!("Is {isbn:?} a valid ISBN number?");
}

分析

按照测试用例,我们给出ISBN-10 校验器的判断条件:

  • 除去连字符,只能有 10 位字符
  • 字符中只能出现数字或者 X
  • X 只能出现在最后一位
  • 校验和能被 11 整除

解决方案

pub fn is_valid_isbn(isbn: &str) -> bool {
    let valid_char = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'X'];
    let chars : Vec<char> = isbn.chars().filter(|s| *s != '-').collect();
    if chars.len() != 10 { return false }
    let mut sum = 0usize;
    for (index, char) in chars.iter().enumerate() {
        if let Some(v) = valid_char.iter().position(|r| *r == *char) {
            if v == 10 && index != 9 {
                return false
            }
            sum += (10 - index) * v;
        } else {
            return false
        }
    }

    sum % 11 == 0
}

Trick

  1. 如果需要得到某个值的下标位置,需要使用.iter().position(|r| *r == *char)方法。
  2. enumerate该方法可以同时获取到迭代器的下标和值。