Во-первых, я исправил ваш императивный код до
- убрать крайне неэффективный индексный доступ в итераторы символов,
- удалите сбой, если
s1
длиннее, но в остальном равно s2
, и замените его тем же поведением "игнорировать хвост более длинной строки", которое ваш код демонстрирует наоборот,
- используйте
str
вместо String
, поскольку почти никогда не существует веской причины для передачи &String
в функцию и
- исправлены некоторые незначительные проблемы со стилем; в частности добавление точек с запятой к возвращаемым значениям, но с использованием выражения хвостового возврата без возврата Это более идиоматичный Rust.
Это выглядит так:
fn has_one_difference(s1: &str, s2: &str) -> bool {
let mut found_one_difference = false;
for (c1, c2) in s1.chars().zip(s2.chars()) {
if c1 != c2 {
if found_one_difference {
return false;
} else {
found_one_difference = true
}
}
}
found_one_difference
}
Теперь для функциональной версии я бы просто написал итератор и посмотрел, смогу ли я вызвать next()
дважды:
fn has_one_difference_functional(s1: &str, s2: &str) -> bool {
// An iterator over different char pairs.
let mut iter = s1.chars().zip(s2.chars())
.filter(|(c1, c2)| c1 != c2);
// First call to next() must succeed (one difference), second must fail.
iter.next().is_some() && iter.next().is_none()
}
Это не полностью функционально, но я думаю, что в целом это лучшее сочетание краткости и читабельности. Простая полностью функциональная версия вызовет count()
для составного итератора и сравнит его с 1, но это не является коротким замыканием и, следовательно, менее эффективным, чем необходимо. Более эффективная версия может быть написана с try_fold
, но она теряет читабельность из-за сложности, поэтому я рассмотрю ее только для has_n_differences
функции.