Как вызвать функцию с несколькими аргументами, не создавая замыкание? - PullRequest
0 голосов
/ 17 декабря 2018

Я сталкивался с этим во время решения 2018 Advent of Code (День 2, Часть 1) в Rust.

Проблема, которую необходимо решить:

Возьмите количество строк, в которых есть ровно две одинаковые буквы, умноженное на количество строк, в которых ровно три одинаковых буквыписьмо.

INPUT

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

Rust Playground

Рассмотрим строку 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)) вместо этого.

Конечно, я мог бы заставить функцию взять кортеж вместо этого, отобразить на кортеж и использовать только один аргумент ... но это кажется ужасным решением.Я бы предпочел просто создать закрытие.

Опущение аргумента only - это то, что Rust позволяет вам делать.Почему компилятору было бы сложнее позволить вам пропустить аргумент last при условии, что у него есть все остальные n-1 аргументы для функции, которая принимает n аргументы.

Мне кажется, что это сделает синтаксис намного чище, и он будет намного лучше соответствовать идиоматическому функциональному стилю, который предпочитает Rust.

Я, конечно, не эксперт в компиляторах, но реализация такого поведения кажется простой.Если мое мышление неверно, я хотел бы узнать больше о том, почему это так.

1 Ответ

0 голосов
/ 17 декабря 2018

Нет, вы не можете передать функцию с несколькими аргументами в качестве неявного замыкания.

В некоторых случаях вы можете выбрать curry для уменьшения 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, но Fn и FnMut такжеесть варианты использования.Это относится только к функции с двумя аргументами.

Однако Я использовал этот трюк функции высшего порядка в других проектах, где количество добавляемого кода намного больше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...