如何在不创建闭包的情况下调用多参数函数?

时间:2018-12-16 21:28:45

标签: functional-programming rust iterator currying

我在Rust中进行2018 Advent of Code (Day 2, Part 1)解决方案时遇到了这个问题。

要解决的问题:

取正好具有两个相同字母的字符串的计数,乘以正好具有三个相同字母的字符串的计数。

输入

abcdega 
hihklmh 
abqasbb
aaaabcd
  • 第一个字符串abcdega已重复a两次。
  • 第二个字符串hihklmh已重复h三次。
  • 第三个字符串abqasbb重复了a两次,而b重复了三遍,因此两者都算在内。
  • 第四个字符串aaaabcd包含一个重复4次的字母(不是23),因此不算在内。

所以结果应该是:

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

Rust Playground

请考虑第21行。函数letter_counts仅接受一个参数,因此我可以在与期望参数类型匹配的元素上使用语法:.map(letter_counts)。这对我来说真的很好!我喜欢不必创建闭包:.map(|id| letter_counts(id))。我发现两者都是可读的,但是对我来说,没有闭包的旧版本更干净。

现在考虑行2223。在这里,我必须使用语法:.filter(|id| has_repeated_letter(3, id)),因为has_repeated_letter函数接受两个参数。我真的很想做.filter(has_repeated_letter(3))

当然,我可以使函数取一个元组,映射到一个元组并仅使用一个参数...但这似乎是一个糟糕的解决方案。我宁愿只是创建闭包。

Rust允许您忽略 only 参数。如果编译器具有接受n-1参数的函数的所有其他n参数,为什么让编译器更难舍弃 last 参数。

我觉得这样可以使语法更加整洁,并且与Rust所喜欢的惯用功能样式更加匹配。

我当然不是编译器专家,但是实现此行为似乎很简单。如果我的想法不正确,那么我想了解更多有关为什么的想法。

1 个答案:

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

但是,我同意这不是好处的评论:

  1. 输入的内容不少:

    a.map(curry(add, 2));
    a.map(|v| add(v, 2));
    
  2. curry函数非常有限:它选择使用FnOnce,但是FnFnMut也有用例。它仅适用于具有两个参数的函数。

但是,I have used是其他项目中的这种高阶函数把戏,其中添加的代码量要大得多。