Functional programming 如何在不创建闭包的情况下调用多参数函数?
我是在铁锈中做溶液时遇到这个问题的 要解决的问题: 取两个字母完全相同的字符串数乘以三个字母完全相同的字符串数 输入 abcdega hihklmh abqasbb AABCDFunctional programming 如何在不创建闭包的情况下调用多参数函数?,functional-programming,rust,iterator,currying,Functional Programming,Rust,Iterator,Currying,我是在铁锈中做溶液时遇到这个问题的 要解决的问题: 取两个字母完全相同的字符串数乘以三个字母完全相同的字符串数 输入 abcdega hihklmh abqasbb AABCD 第一个字符串abcdega重复了a两次 第二个字符串hihklmh重复了h三次 第三个字符串abqasbb重复了a两次,而b重复了三次,因此它对这两个字符串都有效 第四个字符串aaaabcd包含一个重复4次的字母(不是2次,或3次),因此它不计数 因此,结果应该是: 2包含双字母(第一个和第三个)的字符串乘以2包含
- 第一个字符串
重复了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行21
。函数letter\u counts
只接受一个参数,因此我可以对与预期参数类型匹配的元素使用语法:.map(letter\u counts)
。这对我真的很好!我喜欢不必创建闭包:.map(|id | letter_counts(id))
。我发现两者都可读,但没有闭包的前一个版本对我来说要干净得多
现在考虑行<代码> 22 <代码>和<代码> 23 < /代码>。这里,我必须使用语法:
.filter(|id | has _repeated _letter(3,id))
,因为has _repeated _letter
函数包含两个参数。我真的想做.filter(重复字母(3))
当然,我可以让函数取一个元组,映射到一个元组,只使用一个参数。。。但这似乎是一个糟糕的解决方案。我宁愿创建一个闭包
只忽略参数是Rust允许您做的事情。如果编译器为一个接受n
参数的函数提供了所有其他n-1
参数,那么编译器为什么更难让您省略最后一个参数呢
我觉得这样做会使语法更清晰,并且更适合Rust喜欢的惯用函数式风格
我当然不是编译器专家,但实现这种行为似乎很简单。如果我的想法是错误的,我想知道更多的原因 否,不能将具有多个参数的函数作为隐式闭包传递 在某些情况下,您可以选择使用来减少函数的算术性。例如,这里我们将
add
函数从两个参数减少为一个:
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));
}
curry
功能非常有限:它选择使用FnOnce
,但是Fn
和FnMut
也有用例。它只适用于具有两个参数的函数但是,在其他项目中,这种高阶函数技巧的添加量要大得多。您要做的是“curry”或“partial function application”,这在Rust中不直接支持。编写lambda实际上是实现这一点的惯用方法。想象一个函数
fn foo(a:i32,b:i32){…}
,然后将其称为fn main(){foo(4);}
,根据您的建议,此代码将是合法的,但不起任何作用。IMHO说,在真正需要的时候编写一个lambda,比如|x | foo(4,x)
,要合理得多。此外,您还可以轻松编写|x | foo(x,4)
或任何其他具有任意数量参数的变体,而不影响语言的其余部分。当然,您可以编写一个宏,该宏接受这些参数并扩展为lambda,但是,应用的优点是什么!(HaspRealthEdType,3)过<代码> x x Hasz重复字母(3,x)< /代码>?你的问题基本上可以归结为“我可以做这个[一个参数示例],我如何做这个[两个参数示例]”(代码的出现基本上与问题完全无关)这将使其他人更难阅读这个问题,当他们有一个潜在的复制品时,他们会知道它是否与他们相关。
a.map(curry(add, 2));
a.map(|v| add(v, 2));