LoginSignup
1
0

隣接要素と比較する

Last updated at Posted at 2023-03-17

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 的な習慣に不慣れなのでこれが妥当な実装方法なのか私には判断がつかないのですが……。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0