VBAHaskellの紹介 その6 (foldl)
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