読者です 読者をやめる 読者になる 読者になる

VBAHaskellの紹介 その6 (foldl)

VBA

VBAHaskellで実装しているfoldlは本物のHaskellと同様に、「2引数関数」、「初期値」、「対象リスト」の3つの引数を取る左畳み込み関数だ。VBAでラップはしておらず、Haskell_0_declareモジュールに宣言しているC++ API *1 をそのまま使う。

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(関数のシグネチャ)で解説したmake_funPointerで関数ポインタ化した構造を渡す。このとき引数は束縛しない。

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

' 1から10 までの整数の和を求める

foldl(p_plus, 0, iota(1, 10))   ' = 55 *2

これも含め同様の関数を列挙すると以下の8つある。

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次元配列が出力値となる。3次元の場合も同様だが出力は2次元配列になる。 4次元以上には対応していない。*4

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

 ' 関数適用関数 これは要するに func(param) を呼び出している

 Function applyFun(ByRef param As Variant, ByRef func As Variant) As Variant

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

 foldl(p_applyFun,  init,  関数配列 )

そしてこの foldlとp_applyFunの組み合わせをひとつのパターンとして関数にしてしまうこともできるのだ。*6

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

 

VBAHaskellの紹介 その5 (関数のシグネチャ
http://mmyymmdd.hatenablog.com/entry/2015/04/12/205359

github.com

*1:遺憾ながらコンパイル時処理はしていない

*2:plusとp_plusはHaskell_2_stdFunモジュールに定義

*3:printMはデバッグウィンドウに配列を出力するユーティリティ

*4:C++側で任意次元のSafeArray構造体をハンドリングする方法がわからないし、利用機会もあまりないと思っている。

*5:Haskell_1_Coreモジュール

*6:Haskell_1_Coreモジュールのfoldl_Funsなど

*7: test_moduleのSub vbaUnit