我在Rust中进行2018 Advent of Code (Day 2, Part 1)解决方案时遇到了这个问题。
要解决的问题:
取正好具有两个相同字母的字符串的计数,乘以正好具有三个相同字母的字符串的计数。
输入
abcdega
hihklmh
abqasbb
aaaabcd
abcdega
已重复a
两次。 hihklmh
已重复h
三次。abqasbb
重复了a
两次,而b
重复了三遍,因此两者都算在内。aaaabcd
包含一个重复4
次的字母(不是2
或3
),因此不算在内。所以结果应该是:
2
包含双字母(第一个和第三个)的字符串乘以2
包含三字母(第二个和第三个)的字符串= 4
问题:
const PUZZLE_INPUT: &str =
"
abcdega
hihklmh
abqasbb
aaaabcd
";
fn letter_counts(id: &str) -> [u8;26] {
id.chars().map(|c| c as u8).fold([0;26], |mut counts, c| {
counts[usize::from(c - b'a')] += 1;
counts
})
}
fn has_repeated_letter(n: u8, letter_counts: &[u8;26]) -> bool {
letter_counts.iter().any(|&count| count == n)
}
fn main() {
let ids_iter = PUZZLE_INPUT.lines().map(letter_counts);
let num_ids_with_double = ids_iter.clone().filter(|id| has_repeated_letter(2, id)).count();
let num_ids_with_triple = ids_iter.filter(|id| has_repeated_letter(3, id)).count();
println!("{}", num_ids_with_double * num_ids_with_triple);
}
请考虑第21
行。函数letter_counts
仅接受一个参数,因此我可以在与期望参数类型匹配的元素上使用语法:.map(letter_counts)
。这对我来说真的很好!我喜欢不必创建闭包:.map(|id| letter_counts(id))
。我发现两者都是可读的,但是对我来说,没有闭包的旧版本更干净。
现在考虑行22
和23
。在这里,我必须使用语法:.filter(|id| has_repeated_letter(3, id))
,因为has_repeated_letter
函数接受两个参数。我真的很想做.filter(has_repeated_letter(3))
。
当然,我可以使函数取一个元组,映射到一个元组并仅使用一个参数...但这似乎是一个糟糕的解决方案。我宁愿只是创建闭包。
Rust允许您忽略 only 参数。如果编译器具有接受n-1
参数的函数的所有其他n
参数,为什么让编译器更难舍弃 last 参数。
我觉得这样可以使语法更加整洁,并且与Rust所喜欢的惯用功能样式更加匹配。
我当然不是编译器专家,但是实现此行为似乎很简单。如果我的想法不正确,那么我想了解更多有关为什么的想法。
答案 0 :(得分:4)
不,您不能将带有多个参数的函数作为隐式闭包传递。
在某些情况下,您可以选择使用currying来减少函数的 arity 。例如,在这里,我们将add
函数从2个参数减少为一个:
fn add(a: i32, b: i32) -> i32 {
a + b
}
fn curry<A1, A2, R>(f: impl FnOnce(A1, A2) -> R, a1: A1) -> impl FnOnce(A2) -> R {
move |a2| f(a1, a2)
}
fn main() {
let a = Some(1);
a.map(curry(add, 2));
}
但是,我同意这不是好处的评论:
输入的内容不少:
a.map(curry(add, 2));
a.map(|v| add(v, 2));
curry
函数非常有限:它选择使用FnOnce
,但是Fn
和FnMut
也有用例。它仅适用于具有两个参数的函数。
但是,I have used是其他项目中的这种高阶函数把戏,其中添加的代码量要大得多。