Rust で学ぶメソッドチェーン


# メソッドチェーンってなに

メソッドチェーンとは、その名前の通り、メソッドをつなげたものです。
例えば次のようなものです。

```rust
let arr = array.method1().method2().method3();
```

実際は以下のような使われ方をします。

```rust
use itertools::Itertools;

fn main() {
    let arr = vec![0, 0, 0, 0, 0]
        .iter()
        .enumerate()
        .map(|(idx, _)| idx + 1)
        .filter(|v| *v > 2)
        .sorted()
        .collect::<Vec<_>>()
    ;

    println!("{:?}", arr); // -> [3, 4, 5]
}
```

何をしているかわからなくて抵抗感があるかもしれませんが、慣れるととても書きやすい書き方です。
ここでは、そんなメソッドチェーンを書けるようになることを目指します。

Rust ではイテレータを利用すると、自然とメソッドチェーンになります。また、他の言語でメソッドチェーンが使われる場面もRust のイテレータと似た書き方になります。そのため、メソッドチェーンの説明としてイテレータを紹介します。

# 実際のメソッド

よく使うメソッドをいくつか挙げます。最低限これらを知っていればなんとかなるまであります。
要素数と要素の形(型)に注目すると少しわかりやすくなるので、要素数と要素の形についても書きますね。

基本的に Rust について書きますが、多くの言語にも似たものが実装されているので、気になる方は調べてみてください。

## `iter()` と `collect()`

イテレータで使うメソッドの中でも特に重要なのが `iter()` と `collect()` です。これらはイテレータを書くたびに現れます。

簡単に書くと「`iter()` で開始して `collect()` で終了」という使われ方をします。
 
イテレータの処理は高速化などの理由から、専用の中間表現を使います。この中間表現への変換が `iter()` です。また、処理が終わったイテレータを中間表現から元に戻すのが `collect()` です。

(※言語によっては中間表現がないものもあって、そういう場合は `iter()` と `collect()` に相当するものもありません)

それぞれもうちょっと詳しく書きますね。

### `iter()`

`iter()` は要素に対するイテレータ(反復子)を生成しています。上に書いた中間表現のことです。これ単体ではなにも起こりません。

具体的には、処理中は `Iter` や `IntoIter` のような型になっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1748702/b4651a4e-0d3f-b892-0285-2137acb9f85f.png)
`iter()` のほかに `into_iter()` や `iter_mut()` がありますが、その違いはここでは触れません。

### `collect()`

`collect()` は中間表現としてのイテレータを [コレクション](https://doc.rust-lang.org/std/collections/index.html)[^1]に変換します。コレクション以外の、数値などを返す場合は `collect()` は不要です。

`collect::<Vec<i32>>()` や `collect::<String>()` などのように、型を指定して使います。
[`itertools::Itertools` トレイト](https://docs.rs/itertools/0.10.0/itertools/trait.Itertools.html#method.collect_tuple) の `collect_vec()` や `collect_tuple()` も便利です。

## `map()`

要素数は変わらずに各要素を自由に変えます。
すべての要素に同じ処理がされます。

```rust
.map(|val| {
    let val = val * 3;
    (val + 4) > 10
})

// [1, 2, 3] -> [false, false, true]
```

- 数値を受け取って3倍にするとか、
- 文字列を受け取って先頭の3文字だけ返すとか、
- 数値を受け取って10以上なら true, 10未満なら false を返すとか、

などができます。2文以上の式を使うこともよくあります(長くなりすぎて読みにくくなることもよくあります、、)。

Rust の `map()` では、返す型はすべての要素で一致させる必要があります。つまり、`数値を受け取って 10以上なら true, 10未満なら "low" を返す` といったことはできません。
他の言語では、型が一致しなくても大丈夫なこともあります。

https://doc.rust-lang.org/std/iter/struct.Map.html

## `filter()`

要素数が変わって、各要素はそのままです。
`boolean` を返す式を書いて、 `true` になる要素を抽出します。

```rust
.filter(|&val| val % 2 == 1)

// [1, 2, 3] -> [1, 3]
```

- 数値を受け取って、奇数を選ぶとか、
- 文字列を受け取って、3文字以上のものだけを選ぶとか、
- 数値のタプルを受け取って、和が10以上のものを選ぶとか、

などができます。

https://doc.rust-lang.org/std/iter/struct.Filter.html

## `enumerate()`

要素数は変わらず、各要素がタプルに変換されます。
各要素について、 `(前から何番目か, 元の要素)` というタプルになります。

```rust
.enumerate()

// [1, 2, 3] -> [(0, 1), (1, 2), (2, 3)]
```

https://doc.rust-lang.org/std/iter/struct.Enumerate.html

## `sum()`

要素数が 1 になって、数値を返します。
各要素の和を返します。

```rust
.sum()

// [1, 2, 3] -> 6
```

このメソッドを使う場合は `collect()` が不要になります。

数値を扱う型のみに使えます。気になる方は以下をご覧ください。

https://doc.rust-lang.org/std/iter/trait.Sum.html

## `sorted()`

要素数は変わらず、各要素も変わりません。
要素をソートします。

```rust
.sorted()

// [3, 1, 2] -> [1, 2, 3]
```

このメソッドを使うには [`itertools::Itertools` トレイト](https://docs.rs/itertools/0.10.0/itertools/trait.Itertools.html#method.collect_tuple) が必要です。

各要素が比較可能なときに使えます。降順ソートや、自作メソッドによる特殊なソートをする場合は `sorted()` の前後に `map()` などを使うと実現できます。

https://docs.rs/itertools/0.10.1/itertools/fn.sorted.html

## `fold()`

要素数が変わって、各要素も変わります。
返すものを指定できる、自由度の高いメソッドです。

```rust
.fold(0, |acc, val| acc + val)
// [1, 2, 3] -> 6 (sum に相当)

.fold(Vec::<(usize, i32)>::new(), |mut acc, &val| {
    acc.push((acc.len(), val));
    acc
})
// [1, 2, 3] -> [(0, 1), (1, 2), (2, 3)] (enumerate に相当)

.fold(Vec::<i32>::new(), |mut acc, &val| {
    acc.push(val);
    acc.push(val);
    acc
})
// [1, 2, 3] -> [1, 1, 2, 2, 3, 3] (要素数を2倍にする)
```


他の言語では `reduce` と呼ばれることもあります (Rust の `reduce()` は別のメソッドです)。

`map()` も `filter()` も `enumerate()` も、他のメソッドも、これを使えば実現できます。
自由な反面、可読性が犠牲になるので、特別な理由がなければ `map()` や `filter()` などを使いたいです。

https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.fold

# 読んでみる

一通り紹介したところで、冒頭に書いた以下のプログラムを読んでみましょう。

```rust
use itertools::Itertools;

fn main() {
    let arr = vec![0, 0, 0, 0, 0]
        .iter()
        .enumerate()
        .map(|(idx, _)| idx + 1)
        .filter(|&v| v > 2)
        .sorted()
        .collect::<Vec<_>>()
    ;

    println!("{:?}", arr); // -> [3, 4, 5]
}
```

コメントをつけます

```rust
use itertools::Itertools;

fn main() {
    let arr = vec![0, 0, 0, 0, 0]
        .iter()                  // イテレータに変換する
        .enumerate()             // (先頭から何番目か, 元の要素) のタプルに変換する
        .map(|(idx, _)| idx + 1) // 「先頭から何番目か」+ 1 を得る。元の要素は破棄される。
        .filter(|&v| v > 2)      // 2より大きいものを抽出する
        .sorted()                // ソートする
        .collect::<Vec<_>>()     // Vec に変換する
    ;

    println!("{:?}", arr); // -> [3, 4, 5]
}
```

値を追います

```rust
use itertools::Itertools;

fn main() {
    let arr = vec![0, 0, 0, 0, 0]
        .iter()                  // [0, 0, 0, 0, 0]
        .enumerate()             // [(0, 0), (1, 0), (2, 0), (3, 0), (4, 0)]
        .map(|(idx, _)| idx + 1) // [1, 2, 3, 4, 5]
        .filter(|&v| v > 2)      // [3, 4, 5]
        .sorted()                // [3, 4, 5]
        .collect::<Vec<_>>()     // [3, 4, 5]
    ;

    println!("{:?}", arr); // -> [3, 4, 5]
}
```

# 書いてみる

少しずつでも読めるようになりましたら、次は書いてみましょう。実際に書くと理解度がとてもあがります。

- 配列にある偶数の個数を数えるとか、
- 配列をソートした後、ソート前の位置を示すとか、

などをしてみるといいと思います。

おまけで、(宣伝も入っているのですが、)私は AtCoder が好きなので、イテレータを使って解ける AtCoder の問題を紹介します。

https://atcoder.jp/contests/abc222/tasks/abc222_b
https://atcoder.jp/contests/abc218/tasks/abc218_b
https://atcoder.jp/contests/abc219/tasks/abc219_c (ちょっと難しい)

書けるようになりましたら、次は使えるメソッドを増やしましょう。この記事がよくまとまっていたのでリンクを貼っておきます。

https://qiita.com/lo48576/items/34887794c146042aebf1

----

ここまで読んでいただきありがとうございます。この記事がメソッドチェーン(イテレータ)を理解する助けになりましたら幸いです。

# 参考サイト

https://doc.rust-jp.rs/book-ja/ch13-02-iterators.html

https://doc.rust-lang.org/std/iter/trait.Iterator.html

https://docs.rs/itertools/0.10.1/itertools/


[^1]: コレクションは `Vec` や `HashSet` などのことです。