VBAHaskellの紹介　その6　（foldl）

（この記事ははてなブログ　http://mmyymmdd.hatenablog.com/ の再掲です）

2015-04-14
VBAHaskellで実装している**foldl**は本物のHaskellと同様に、「2引数関数」、「初期値」、「対象リスト」の3つの引数を取る左畳み込み関数だ。VBAでラップはしておらず、[Haskell_0_declareモジュール](https://github.com/mYmd/VBA/blob/master/Haskell_0_declare.bas)に宣言しているC++ API [^1] をそのまま使う。

```vbnet
Declare Function foldl Lib "mapM.dll" ( _
                              ByRef   pCallback As Variant, _
                              ByRef   init As Variant, _
                              ByRef   matrix As Variant, _
                              Optional ByVal   axis    As Long = 1) As Variant
```
 「2引数関数」である pCallbackには前回 [VBAHaskellの紹介 その5（関数のシグネチャ）](http://qiita.com/mmYYmmdd/items/a89754da3717b7c1e40d)で解説した**make_funPointer**で関数ポインタ化した構造を渡す。このとき引数は束縛しない。

例えば1から10までの整数の和を取る場合は次のように書けばよい。[^2]

```vbnet
' 1から10 までの整数の和を求める
foldl(p_plus, 0, iota(1, 10))
```
これも含め同様の関数を列挙すると以下の８つある。

|            |                                         |
|:-----------|:----------------------------------------|
|foldl       |特定の軸に沿った左畳み込み（初期値指定あり） |
|foldr       |特定の軸に沿った右畳み込み（初期値指定あり） |
|foldl1      |特定の軸に沿った左畳み込み（初期値=先頭要素）|
|foldr1      |特定の軸に沿った右畳み込み（初期値=先頭要素）|
|scanl       |特定の軸に沿った左scan（初期値指定あり）    |
|scanr       |特定の軸に沿った右scan（初期値指定あり）    |
|scanl1      |特定の軸に沿った左scan（初期値=先頭要素）   |
|scanr1      |特定の軸に沿った右scan（初期値=先頭要素）   |

個々の機能の説明は省略するが、「特定の軸に沿った」とは何か？これはVBAの配列には2次元以上のものがあることが関係している。2次元の場合は縦方向と横方向では別の処理になるのだが、例をあげた方が早いと思う。

**`m = makeM(3,5,repeat(1,15)) : printM m`**   [^3]  ' [3 * 5] の配列
  1 1 1 1 1
  1 1 1 1 1
  1 1 1 1 1
**`printM foldl(p_plus, 0, m, 1)`** ' 1は省略可
  3 3 3 3 3
**`printM foldl(p_plus, 0, m, 2)`**
  5 5 5

4番目の引数を1にすると 縦に加算、2にすると横に加算された1次元配列が出力値となる。３次元の場合も同様だが出力は2次元配列になる。 4次元以上には対応していない。[^4]

この引数となる関数は算術的な演算だけではない。**applyFun** [^5] という「関数適用関数」を引数にすれば、任意個の関数の合成ができるのだ。

```vbnet
 ' 関数適用関数　これは要するに func(param) を呼び出している
 Function applyFun(ByRef param As Variant, ByRef func As Variant) As Variant
```

これを関数ポインタ化した **p_applyFun** を使って次のようにすると、初期値 init に配列形式で与えた1変数関数を左から順次適用して結果を出力する。

```vbnet
 foldl(p_applyFun,  init,  関数配列)
```
そしてこの **foldl**と**p_applyFun**の組み合わせをひとつのパターンとして関数にしてしまうこともできるのだ。[^6]

 サンプル[^7] にあるロジスティック漸化式、フィボナッチ数列、Newton法による多項式の求根などはこれを使っている。 これらの場合は関数配列と言っても同一関数のリピートである。 VBAでこんなことができるなんて、けっこう胸アツなんじゃないかと思う。

[VBAHaskellの紹介　その7　（bind1stとbind2nd）](http://qiita.com/mmYYmmdd/items/27d9fa8befa38b0de9c0)
[VBAHaskellの紹介　その5　（関数のシグネチャ）](http://qiita.com/mmYYmmdd/items/a89754da3717b7c1e40d)
[VBAHaskellの紹介　その4　（Find）](http://qiita.com/mmYYmmdd/items/9299e7cf862b4c8db29e)
[VBAHaskellの紹介　その3　（FizzBuzz）](http://qiita.com/mmYYmmdd/items/93040f07121d95898161)
[VBAHaskellの紹介　その2　（合成関数）](http://qiita.com/mmYYmmdd/items/1e528ad68ad2c4e3de29)
[VBAHaskellの紹介　その1　（最初はmapF）](http://qiita.com/mmYYmmdd/items/c731edf943acc0a0ebe9)
ソースコード：https://github.com/mYmd/VBA

[^1]: 遺憾ながらコンパイル時処理はしていない
[^2]: plusとp_plusは[Haskell_2_stdFun](https://github.com/mYmd/VBA/blob/master/Haskell_2_stdFun.bas)モジュールに定義
[^3]: printMはデバッグウィンドウに配列を出力するユーティリティ
[^4]: C++側で任意次元のSafeArray構造体をハンドリングする方法がわからないし、利用機会もあまりないと思っている。
[^5]: [Haskell_1_Coreモジュール](https://github.com/mYmd/VBA/blob/master/Haskell_1_Core.bas)
[^6]: [Haskell_1_Coreモジュール](https://github.com/mYmd/VBA/blob/master/Haskell_1_Core.bas)のfoldl_Funsなど
[^7]: [test_module](https://github.com/mYmd/VBA/blob/master/test_module.bas)のSub vbaUnit