ResultやOptionが要素型の場合のiteratorの捌き方

# ResultやOptionが要素型の場合のiteratorの捌き方

`Iterator<Result<T, E>>`とか`Iterator<Option<T>>`の扱い方がやっぱりむずいな、と思ったので、`Result`や`Option`が要素型の場合のiteratorの捌き方をまとめてみた。


## 枠組み

1. 計算手法は2つ+αかな。
  + Projection Operation(射影)
  + Aggregation Operation(集計)
  + Scan operation(走査[^scan])

[^scan]: 集計するが、Aggregation Operationと違って途中経過も返す

2. 各要素が以下のデータ型であるような[^failure]iteratorを取り扱う。
  + Option
  + Result

[^failure]: 失敗可能性の文脈を持つモナド(rustにはモナド公式にないけど)

3. 計算が失敗したときの振る舞いも大きく分けて2つ。
  + 終了する
  + 継続する

で、(1)×(2)×(3)を組み合わせたときの処理が、rustで関数プログラミング的にどう実装されるのか例を通じてまとめたい。

進め方としては、1.の各計算手法に対して例を取り上げる。2.に関しては、`Option`や`Result`が登場する例として、前者では、`std::collection::HashMap`のgetメソッドを、後者では、文字列のパース`str.parse::<T>`を取り扱う。その中で、3.でそれぞれ失敗したときの取扱いについて、それぞれ実装する。

各節独立に読めます[^all_scan]！

[^all_scan]: すべての問題、すべての失敗時の対処方法で、scanつかってゴリ押しすることはできますけど、記事的にハンサになりそうですし、必要そうな場面にしか使わないことにしました。

## 環境

```bash
rustc --version
rustc 1.19.0 (0ade33941 2017-07-17)
```

----

## projection

### `Option<A>`の場合

(お題): あるHashMapが用意されていて、ある配列の各要素をkeyとしたときのvalueを求めたい.例えば、以下のようなHashMapがあったとき、

```rust
use std::collections::HashMap;

// create sample hashmap (assume that it's large) {'a':1, 'b':2...}
let hashmap = (b'a'..b'z')
        .map(|s| s as char)
        .zip(1..)
        .collect::<HashMap<_,_>>();
```

`['b', 'a', 'd', 'g', 'v']` に関しては、`[2,1,4,7,22]`が答えとなる。今回は例として、

```rust:sample.rs
// もちろん0は英字小文字でないので失敗する。
let v = vec!['b', 'a', '0', 'g', 'v'];
```

について、失敗したときの実装を各種試みたい[^option]。

[^option]: 本記事ではOption型に関しては、後の節の`aggregation`でも`scan`でも、こんなような問題設定を考えます。ちょっと冗長ですけど。

+ 失敗したら計算を終了させる場合

```rust
/* 失敗したら計算不可とする場合。 */
// Out: None
v.iter()
    .map(|s| hashmap.get(s))
    .collect::<Option<Vec<_>>>();

// Out: Some([2,1]) はじめの2つは成功するので
v.iter()
    .map(|s| hashmap.get(s))
    .take(2)
    .collect::<Option<Vec<_>>>();


/* 失敗する直前までの結果を返す */
// Out: [2,1] 
// scanの第一引数は状態。今回は前回までの状態を保存する必要が無いので、unit型とした
v.iter()
    .scan((), |_, x| hashmap.get(x))
    .collect::<Vec<_>>();
```

+ 失敗しても計算を継続する場合

```rust
/* 失敗した場合でも、Noneを返したうえで、計算を継続させる */
// Out: [2,1,None,7,22]
v.iter()
    .map(|s| hashmap.get(s))
    .collect::<Vec<_>>();


/* 失敗した場合、失敗したものを無視して、計算を継続させる */
// Out: [2,1,7,22]
v.iter()
    .filter_map(|s| hashmap.get(s))
    .collect::<Vec<_>>();

// もしくは、flat_mapを使っても良い
// Out: [2,1,7,22]
v.iter()
    .flat_map(|s| hashmap.get(s))
    .collect::<Vec<_>>();

/* 失敗した場合、-1をsetした上で、計算を継続させる */
// Out: [2,1,-1,7,22]
let DEFAULT_VALUE = -1;
let m: Vec<i32> = v.iter()
    .map(|s| *(hashmap.get(s).unwrap_or(&DEFAULT_VALUE)))
    .collect();
```


### `Result<T,E>`の場合


(お題): 配列の各要素(文字列)を整数として読み取って、1足した結果を出力とする[^excep].例えば、
`["1","2","3", "4"]`だったら、`[2,3,4,5]: Vec<i32>`のように出力したい。今回は例として、

```rust
let v = vec!["1","2","3b", "4", "5"];
```

について、失敗したときの実装を各種試みたい。(`Option<A>`の場合と似たような感じになるが、一応。)


[^result]: 本記事ではResult型に関しては、後の節の`aggregation`でも`scan`でも、こんなような問題設定を考えます。ちょっと冗長ですけど。



+ 失敗したら計算を終了させる場合


```rust
/* 失敗したら計算不可とする場合。 */
// Out: Err(std::num::ParseIntError { kind: InvalidDigit }));    
v.iter()
    .map(|s|
        s.parse::<i32>()
            .map(|s| s+1))
    .collect::<Result<Vec<_>, _>>();

// Out: Ok([2,3]) 先頭の要素2つは成功するので。
v.iter()
    .map(|s| 
        s.parse::<i32>()
            .map(|s| s+1))
    .take(2)
    .collect::<Result<Vec<_>, _>>();


/* 失敗する直前までの結果を返す */
// Out: [2,3]
v.iter()
    .scan((), |_,&x|
        x.parse::<i32>()
            .map(|t| t+1)
            .ok())
    .collect::<Vec<_>>();
```

+ 失敗しても計算を継続する場合

```rust
/* 失敗した場合でも、Err値を返したうえで、計算を継続させる */
// Out: [Ok(2), Ok(3), Err(ParseIntError { kind: InvalidDigit }), Ok(5), Ok(6)]
v.iter()
    .map(|s|
        s.parse::<i32>()
            .map(|s| s+1))
    .collect::<Vec<_>>();


/* 失敗した場合、失敗したものを無視して、計算を継続させる */
// Out: [2,3,5,6]
v.iter().
    flat_map(|s| 
        s.parse::<i32>())
    .map(|s| s+1)
    .collect::<Vec<_>>();

// もしくはfilter_mapを使う
v.iter()
    .filter_map(|s| 
        s.parse::<i32>()
            .ok())
    .map(|s| s+1)
    .collect::<Vec<_>>();


/* 失敗した場合、-1をsetした上で、計算を継続させる */
// Out: [2,3,-1,5,6]
let DEFAULT_VALUE = -1;
v.iter()
    .map(|s|
        s.parse::<i32>()
            .unwrap_or(DEFAULT_VALUE))
    .collect::<Vec<_>>();
```

[^excep]: ただし、`std::i32::MAX (2147483647)`を超えないものとする。




-----

こんな感じで、`aggregation`や`scan`の場合も進んでいきたいと思います。

## aggregation

### `Option<A>`の場合

(お題): あるHashMapが用意されていて、ある配列の各要素をkeyとしたときのvalueの総和を求めたい。例えば、以下のようなHashMapがあったとき、

```rust
use std::collections::HashMap;

// create sample hashmap (assume that it's large) {'a':1, 'b':2...}
let hashmap = (b'a'..b'z')
        .map(|s| s as char)
        .zip(1..)
        .collect::<HashMap<_,_>>();
```

`['b', 'a', 'd', 'g', 'v']` に関しては、`36`が答えとなる(`[2,1,4,7,22]`なので)。今回は例として、

```rust:sample.rs
// もちろん0は英字小文字でないので失敗する。
let v = vec!['b', 'a', '0', 'g', 'v'];
```

について、失敗したときの実装を各種試みたい。

----


+ 失敗したら計算を終了させる場合

```rust
/* 失敗したら計算不可とする場合。 */
// option型に関しては残念ながら、sumは用意されていないので、foldする。
// Out: None
v.iter()
    .map(|s| hashmap.get(s))
    .fold(Some(0), |s, x|
        // Note) 関数(この場合はstd::ops::Addの`add` method)のリフトとかできればスッキリかけると思いますが、
        // ちょっとわからんので、and_then以下で泥臭く書きました。
        s.and_then(|ss| 
            x.map(|xx| xx+ss)));
);

// もしくは、result型にはsumメソッドがあるので、こうするのも良いかも。
// ok_orには何でもいいが、unit( `()` )をつけてる
// Out: None 
v.iter()
    .map(|s| 
        hashmap.get(s)
            // Result<i32, ()>に変換
            .ok_or(()))
    .sum::<Result<i32, _>>()
    .ok();
```


+ 失敗しても計算を継続する場合

```rust
/* 失敗した値に対しては無視して、計算を継続する */
// sumのi32は戻り値の型を推論するのに必要。
// Out: 32 
v.iter()
    .flat_map(|s| 
        hashmap.get(s))
    .sum::<i32>();

// 途中結果が気になったら、間にinspectをはさもう。
v.iter()
    .flat_map(|s|
        hashmap.get(s))
    // `2, 1, 7, 22,`が中間結果として出力される：
    .inspect(|s| 
        print!("{:?}, ", s))
    .sum::<i32>();
```

### `Result<T,E>`の場合

(お題): 配列の各要素(文字列)を整数として読み取った後、総和を計算したい。例えば、`["1","2","3", "4"]`だったら、`10`が答えとなるようにしたい。今回は例として、

```rust
let v = vec!["1","2","3b", "4", "5"];
```

について、失敗したときの実装を各種試みたい。(これも、`Option<A>`の場合と似たような感じになるが、一応。)

-----

+ 失敗したら計算を終了させる場合

```rust
/* 失敗したら計算不可とする場合。 */
// Out: Err(ParseIntError { kind: InvalidDigit })
// Result のi32はsumの型を決定するのに必要。
v.iter()
    .map(|s| 
        s.parse::<i32>())
    .sum::<Result<i32, _>>();

// Out: Ok(3) (2個目まではparse失敗しないので1+2)
v.iter()
    .map(|s| s.parse::<i32>())
    .take(2)
    .sum::<Result<i32, _>>();
```

+ 失敗しても計算を継続する場合

```rust
/* 失敗した値に対しては無視して、計算を継続する */
// Out: 12 (=1+2+4+5 なので)
v.iter()
    .flat_map(|s| 
        s.parse::<i32>())
    .sum::<i32>();
```


-----


## scan

### `Option<A>`の場合

(お題): あるHashMapが用意されていて、ある配列の各要素をkeyとしたときのvalueの総和を求めたい。ただし、aggregateの場合と違って、総和計算の途中結果も格納する。例えば、以下のようなHashMapがあったとき、

```rust
use std::collections::HashMap;

// create sample hashmap (assume that it's large) {'a':1, 'b':2...}
let hashmap = (b'a'..b'z')
        .map(|s| s as char)
        .zip(1..)
        .collect::<HashMap<_,_>>();
```

`['b', 'a', 'd', 'g', 'v']` に関しては、[2,3,7,14,36]が答えとなる。(`[2, 2+1, 2+1+4, 2+1+4+7, 2+1+4+7+22]`なので)。今回は例として、

```rust:sample.rs
// もちろん0は英字小文字でないので失敗する。
let v = vec!['b', 'a', '0', 'g', 'v'];
```

について、失敗したときの実装を各種試みたい。

----


+ 失敗したら計算を終了させる場合

```rust
/* 失敗した場合、Noneを返す。成功したときはリストを返す */
// Out: None
v.iter()
    .map(|s| hashmap.get(&s))
    .scan(Some(0), |s, x| {
        *s = x.and_then(|&xx| 
            s.map(|ss| 
                ss+xx));
        Some(*s)
    })
    // Result<Vec<_>,_>と指定するのがポイント
    .collect::<Option<Vec<_>>>();

// Out: Some([2, 3]) // 二つ目までは正常にscanできる
v.iter()
    .map(|s| hashmap.get(&s))
    .scan(Some(0), |s, x| {
        *s = x.and_then(|&xx| 
            s.map(|ss| 
                ss+xx));
        Some(*s)
    })
    .take(2)
    .collect::<Option<Vec<_>>>();


/* 失敗する直前までの結果を返す */
// Out: [2,3]
v.iter()
    .map(|s| hashmap.get(&s))
    .scan(Some(0), |s, x| {
        *s = x.and_then(|&xx|
            s.map(|ss|
                ss+xx));
        *s
    })
    .collect::<Vec<_>>();


/* 失敗した場合、失敗した要素から先の要素はNoneが返される */
// Out: [Some(2), Some(3), None, None, None]
v.iter()
    .map(|s| hashmap.get(s))
    // sはResult型でmoveされるので、cloneする必要がある。
    .scan(Some(0), |s, x| {
        *s = x.and_then(|&xx|
            s.map(|ss|
                ss+xx));
        Some(*s)
    })
    .collect::<Vec<_>>();
```

+ 失敗しても計算を継続する場合


```rust
/* 失敗した場合、結果を据え置きのち、継続する */
// Out: [2, 3, 3, 10, 32]
v.iter()
    .map(|s| hashmap.get(&s))
    .scan(0, |s, x| {
        *s = x.map(|&xx| xx+*s)
            // ここで、失敗したときに前の状態を維持する
            .unwrap_or(*s);
        Some(*s)
    })
    .collect::<Vec<_>>();

// もしくは、map_orを使えばもっと簡潔に書ける。
// Out: [2, 3, 3, 10, 32]
v.iter()
    .map(|s| hashmap.get(&s))
    .scan(0, |s, x| {
        *s = x.map_or(*s, |&xx| 
            xx+*s);
        Some(*s)
    })
    .collect::<Vec<_>>();


/* 失敗した結果を完全に無視して、継続する */
// Out: [2, 3, 10, 32]
v.iter()
    .flat_map(|s| hashmap.get(&s))
    .scan(0, |s,&x| {
        *s = x+(*s);
        Some(*s)
    }).collect::<Vec<_>>();
```



### `Result<T,E>`の場合

(お題): 配列の各要素(文字列)を整数として読み取って、配列を順番に足し上げた結果を出力する。ただし、今回は、途中経過も見えるようにする。例えば、
`["1","2","3", "4"]`だったら、`[1,3,6,10]: Vec<i32>`のように出力したい。
今回は例として、

```rust
let v = vec!["1","2","3b", "4", "5"];
```

について、失敗したときの実装を各種試みたい。(`Option<A>`の場合と似たような感じになるが、一応。)

----


+ 失敗したら計算を終了させる場合

```rust
/* 失敗した場合、Errを返す。成功したときはリストを返す */
// Out: Err(ParseIntError { kind: InvalidDigit })
v.iter()
    .map(|s| s.parse::<i32>())
    .scan(Ok(0), |s, x| {
        *s = x.and_then(|xx| 
            // sはResult型でmoveされるので、cloneする必要がある。
            s.clone()
                .map(|ss| ss+xx)); 
        Some(s.clone())
    })
    // Result<Vec<_>,_>と指定するのがポイント
    .collect::<Result<Vec<_>,_>>();

// Out: Ok([1, 3])
v.iter()
    .map(|s| s.parse::<i32>())
    .scan(Ok(0), |s, x| {
        *s = x.and_then(|xx|
            s.clone()
                .map(|ss| ss+xx));
        Some(s.clone())
    })
    .take(2)
    .collect::<Result<Vec<_>,_>>();


/* 失敗する直前までの結果を返す */
// Out: [1, 3]
v.iter()
    .map(|s| s.parse::<i32>())
    .scan(Ok(0), |s, x| {
        *s = x.and_then(|xx| 
            s.clone()
                .map(|ss| ss+xx));
        s.clone()
            .ok()
    })
    .collect::<Vec<_>>();

/* 失敗した場合、失敗した要素から先の要素はErrを返す */
// Out: [Ok(1), Ok(3), Err(ParseIntError { kind: InvalidDigit }), Err(ParseIntError { kind: InvalidDigit }), Err(ParseIntError { kind: InvalidDigit })]
v.iter()
    .map(|s| s.parse::<i32>())
    // sはResult型でmoveされるので、cloneする必要がある。
    .scan(Ok(0), |s, x| {
        *s = x.and_then(|xx| 
            s.clone()
                .map(|ss| ss+xx)); 
        Some(s.clone())
    })
    .collect::<Vec<_>>();
```

+ 失敗しても計算を継続する場合

```rust
/* 失敗した場合、結果を据え置きのち、継続する */
// Out: [1,3,3,7,12]
v.iter()
    .map(|s| s.parse::<i32>()
        .ok())
    .scan(0, |s, x| {
        *s = x.map_or(*s, |xx| 
            xx+(*s)); 
        Some(*s)
    })
    .collect::<Vec<_>>();


/* 失敗した結果を完全に無視して、継続する */
// Out: [1,3,7,12]
v.iter()
    .flat_map(|s| 
        s.parse::<i32>())
    .scan(0, |s,x| {
        *s = x+(*s);
        Some(*s)
    }).collect::<Vec<_>>();
```


## まとめ
+ ResultやOptionが要素型の場合のiteratorの捌き方をまとめてみた[^scan]。
+ 射影と集計の処理は似たような感じだが、走査の書き方がむずい(使用頻度は少ないものの)

## 感想

+ もっと簡潔に書ける部分がありそうだな、と思いつつ自分のrustに関するライブラリ知識が足りないので、コメントとか頂けるとうれしいです。

+ [参考文献の3](https://www.amazon.co.jp/Scala関数型デザイン-プログラミング-―Scalazコントリビューターによる関数型徹底ガイド-impress-gear/dp/4844337769/ref=sr_1_1?s=books&ie=UTF8&qid=1509109822&sr=1-1&keywords=Scala関数型デザイン%26プログラミング)を以前に読んだことがあって、なにこれ、むずっ:sweat_drops:と思ったけど、今になって役立ってる感ある。

+ iterator自体の使い方としてはここまで網羅できていれば、ほぼ十分な感じはありますが、実践で使っていると、意外と高階関数の引数に突っ込むクロージャー周りで詰まることが多いです[^move]。こちらはちょっとまだ使いこなせていない感があるので、次回書きたいです。

+ `collect`関数一つで`Iterator<Result<T, E>>`から`Result<Vec<T>, E>`にも、`Vec<Result<T>, E>`にもconvertできるのは、慣れればめっちゃ便利(FromIteratorがきちんと定義されてるからなんだけど)。

[^move]: 一回ムーブして所有権なくなってる変数をもう一回使ってるやん、的な。

## 参考

1. http://qnighy.hatenablog.com/entry/2017/06/14/220000 
2. https://qiita.com/tatsuya6502/items/cd41599291e2e5f38a4a
3. https://www.amazon.co.jp/Scala関数型デザイン-プログラミング-―Scalazコントリビューターによる関数型徹底ガイド-impress-gear/dp/4844337769/ref=sr_1_1?s=books&ie=UTF8&qid=1509109822&sr=1-1&keywords=Scala関数型デザイン%26プログラミング 第一部はまさに、本記事の内容とかぶる。