VBAHaskellの紹介 その22（イテレータ）

(2016/3/7)
`Haskell_6_iterator` を廃止し、新しくクラスモジュール [vh_stdvec.cls](https://github.com/mYmd/VBA/blob/master/vh_stdvec.cls) を追加した。
イテレータはとても使いづらいものだったし、実際使っていなかった。クラス化した方がいいことはわかっていたが、VBAでのクラス作成は経験もないし気が進まず放置していた。実際作ってみたら簡単だったが、イテレータというよりvectorクラスになった。

[リファレンス](http://home.b07.itscom.net/m-yamada/vh_Manual/VBAHaskell_reference.htm) の「Vectorクラス」のところに機能一覧をまとめた。

* * *

「reverseをfoldlで実装する」というネタ記事 [VBAHaskellの紹介 その18（reverseをfoldで実装）](http://qiita.com/mmYYmmdd/items/96901d27d5997f50d3c0)で、

>・　最初に対象配列と同じ大きさの配列を作って、それに繰り返し操作をする。サイズは変えない。
・　fold関数内での返り値の取り回しを効率的にする（配列をコピーしない）。

という方針のもと、対象配列と同じサイズの作業用配列を作って値を書き込んでいくという実装にした。foldlに与えるアキュムレータ（初期値）として作業用配列を渡し、以下の関数を繰り返し呼ぶわけだ。

```vbnet
Function reverse_imple(ByRef a As Variant, ByRef b As Variant) As Variant
    ' a(0) : 書き込み位置
    ' a(1) : 書き込み対象配列
    a(1)(a(0)) = b      ' 値を書き込む
    a(0) = a(0) - 1     ' 書き込み位置を動かす
    swapVariant reverse_imple, a      ' コピーしないswapで返す
End Function
```
この、**『対象配列に対するアクセス位置を進めながら何かする』**という操作を一般化したいと考えて、VBA側に新しいモジュール `Haskell_6_iterator` を追加した。[^1]
対象とする配列（1次元）をもとにしてイテレータを生成すると、イテレータ自身が対象配列と現在位置、進行方向（正順／逆順）の情報を持った実体となり、現在位置を変えながら配列の要素を参照したり設定したりできる。

```vbnet
' 対象配列 m1, m2, m3, m4 があるものとする
it1 = make_iterator(m1)             ' m1をmoveした正順イテレータ
it2 = make_iterator(m2, false)      ' m2をコピーした正順イテレータ
it3 = reverse_iterator(m3)          ' m3をmoveした逆順イテレータ
it4 = reverse_iterator(m4, false)   ' m4をコピーした逆順イテレータ
' この時点でm1とm3はEmpty、 m2とm4は変更なし
it1 = iterator_set(it1, 999)
m1 = release_iterator(it1)
m3 = release_iterator(it3)
' この時点でm1とm3には元の配列が戻される（なにか操作されていれば操作後の値）
```
イテレータの内部構造は以下の通り単純な配列である。

|配列要素|　it(0)|　it(1)|　it(2)|
|:------|:----- |:-------- |:-------- |
|内容|対象配列|現在位置|進行方向(±1)|

典型的な使い方としては、`iterator_push` で値を設定しつつ移動していくなどがある。

```vbnet
m=Array(1, 2, 3, 4, 5)
it = reverse_iterator(m)       ' 逆順イテレータ
it = iterator_push(it, 999)
it = iterator_push(it, 888)
m = release_iterator(it)       ' mに配列を戻す
printM m
  1  2  3  888  999
```


**関数一覧**

|   関数                 |    内容   |        |
|:----------------------|:-------- |:-------- |
|make_iterator|1次元配列からイテレータの生成（デフォルトはmove） ||
|reverse_iterator|1次元配列から逆順イテレータの生成（同上）||
|release_iterator|イテレータの配列部分を戻して自身は解放||
|iterator_pos|現在のインデックス位置を取得する||
|iterator_step|インデックスの進行方向を取得する(1, -1)||
|iterator_advance|指しているインデックスを進める||
|iterator_moveTo|インデックスを任意の位置に動かす||
|iterator_get|現在のインデックスの位置の 値 を取得する||
|iterator_set|現在のインデックスの位置の 値 を設定する||
|iterator_push|現在のインデックス位置の値を設定してインデックスを進める||
|iterator_push_ex|範囲拡張しながらiterator_push||
|iterator_shrink|対象配列のインデックス範囲を現在位置まで切り詰める||
|iterator_range|対象配列のインデックス範囲を取得する||
|iterator_check|現在のインデックスが対象配列のインデックス範囲にあるか確認する||

これを使うと、reverse 関数はこのように書ける。アドホックなヘルパ関数 `reverse_imple` が必要なくなった。

```vbnet
'reverse関数の別バージョン
Function reverse3(ByRef a As Variant) As Variant
    Dim it As Variant
    it = reverse_iterator(repeat(Empty, sizeof(a)))  ' aと同サイズの配列
    it = foldl(p_iterator_push, it, a)         ' iterator_push の関数オブジェクト化
    reverse3 = release_iterator(it)
End Function
```
fold 系の関数と組み合わせて色々な使い方ができるのではないかと思う。
* * *
[VBAHaskellの紹介 その21（多倍長整数 bigInt）](http://qiita.com/mmYYmmdd/items/23ad7ce279f2828a9e39)
[VBAHaskellの紹介 その18（reverseをfoldで実装）](http://qiita.com/mmYYmmdd/items/96901d27d5997f50d3c0)
[VBAHaskellの紹介 その1（最初はmapF）](http://qiita.com/mmYYmmdd/items/c731edf943acc0a0ebe9)

[^1]: https://github.com/mYmd/VBA/blob/master/Haskell_6_iterator.bas