(この記事ははてなブログ http://mmyymmdd.hatenablog.com/ の再掲です)
2015-04-12
VBAHaskellはVBAの関数を合成して独立したオブジェクトにしたり、Haskell的なリスト処理をするライブラリだが、それが可能なVBA関数のシグネチャは実質的に1種類しかない。
Function myFunction(ByRef a As Variant, ByRef b As Variant) As Variant
'または
Function myFunction(ByRef a As Variant, Optional ByRef b As Variant) As Variant
この関数をそのまま使うのではなく、一種の関数ポインタ的なものにして渡す必要があって、それをやるヘルパ関数が
make_funPointer
と make_funPointer_with_2nd_Default
だ。
たとえば Haskell_2_stdFunモジュールで最初に定義されている firstArg
関数はこうなっている。
Function firstArg(ByRef a As Variant, ByRef b As Variant) As Variant
firstArg = a
End Function
Function p_firstArg(Optional ByRef firstParam As Variant, _
Optional ByRef secondParam As Variant) As Variant
p_firstArg = make_funPointer(AddressOf firstArg, firstParam, secondParam)
End Function
firstArg
関数に付属する p_firstArg
が関数ポインタを作っており、その中で**make_funPointer
を呼んでいる。関数名は何でもよいが、p_関数名
** にだいたい統一している。新たに関数を定義したら、この p_firstArg
のコードをコピペして関数名のところだけ対象関数に合わせて修正すれば使えるようになる。1
make_funPointer
は4要素の配列を作っているだけで、その配列は(関数ポインタ、引数1、引数2、xxx)という構成になっている。2つある引数には他の関数をネストすることもできて、これによって関数合成が実現される。
C++API側ではこのネストした配列を受け取って頻繁に呼び出すことになるが、VBAのsafearray構造体の中身に毎回アクセスするのは効率が悪いと思われるので、再帰的なリンク構造を再構成してから呼び出すようにしている。具体的には VBA_NestFunc.hpp と VBA_NestFunc.cpp にある funcExpr_i::eval()
の再帰的な呼び出しがそれにあたる。
残念ながらここにはコンパイル時処理はない。
VBAHaskellの紹介 その6 (foldl)
VBAHaskellの紹介 その4 (Find)
VBAHaskellの紹介 その3 (FizzBuzz)
VBAHaskellの紹介 その2 (合成関数)
VBAHaskellの紹介 その1 (最初はmapF)
ソースコード:https://github.com/mYmd/VBA
dllバイナリ:http://home.b07.itscom.net/m-yamada/VBA/mapM.dll
-
ただし元の関数の第2引数がOptional宣言されていて、省略時の動作が定義されている場合は make_funPointer_with_2nd_Default の方を使う方がいい。その例は Haskell_2_stdFun にある対数関数 "logN" で、第2引数が対数の底を表しているが、省略時は自然対数になる。 ↩