Hi,大家好,这里是炼手Rust专栏,我是xiaoK,今天来看个编程挑战:ISBN 校验器。
提问
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
- 如果需要得到某个值的下标位置,需要使用
.iter().position(|r| *r == *char)方法。 enumerate该方法可以同时获取到迭代器的下标和值。