Rust でベクタに格納されている要素を隣の要素と比較して処理する例を見ました。
メソッド windows
を使える場合ならそれでよいのですが、もっと汎用的にならないだろうか、つまりイテレータがあれば大丈夫という形で書けないだろうかと考えたときに Scheme (やその類似言語のいくつかでも使われていると思う) でのイディオムを連想しました。 開始位置をずらして map
で辿るやり方です。 (以下のコードは R7RS を想定しています。)
(import (scheme base)
(scheme write))
(define v '("a" "a" "b" "c"))
(define (mark-repeated-string v)
(cons (car v)
(map (lambda (str1 str2)
(if (equal? str1 str2)
(string-append str1 "!")
str1))
(cdr v)
v)))
(write (mark-repeated-string v))
Scheme の map
は可変長なので入力要素の数を自由にできます。 Rust 的に言えば要するに zip
の能力も持った map
だと考えて良いでしょう。
以上の考え方をもとにして Rust で書いてみるとこうなりました。
fn mark_repeated_string<'a, T>(v: &'a T) -> Vec<String>
where
&'a T: IntoIterator<Item = &'a String>,
{
let mut iter = v.into_iter();
let first = iter.next();
first
.into_iter()
.map(ToString::to_string)
.chain(iter.zip(v.into_iter()).map(|(str1, str2)| {
if str1 == str2 {
format!("{}!", str1)
} else {
str1.clone()
}
}))
.collect::<Vec<String>>()
}
fn main() {
// 入力が Vec の場合
let v = vec!["a", "a", "b", "c"]
.into_iter()
.map(ToString::to_string)
.collect::<Vec<_>>();
println!("{:?}", mark_repeated_string(&v)); // ["a", "a!", "b", "c"]
// 入力が LinkedList の場合
let v = v.into_iter().collect::<std::collections::LinkedList<_>>();
println!("{:?}", mark_repeated_string(&v)); // ["a", "a!", "b", "c"]
}
所有権管理の都合もあって思ったよりごちゃついてしまいましたがより制約の少ない (汎用性の高い) 形で実装することに成功しました。
Rust 的な習慣に不慣れなのでこれが妥当な実装方法なのか私には判断がつかないのですが……。