LoginSignup
0
0

More than 5 years have passed since last update.

VBAHaskellの紹介 その17(大きな関数オブジェクト)

Last updated at Posted at 2015-06-17

VBAHaskellに、1次元配列の部分列を作成するsubVという関数を追加した。1
機能的にはsubM関数の1次元限定バージョンに過ぎないが、こちらは2変数なので関数オブジェクト化ができる利点がある。
ここで書きたいのは、その実装方法とdll側に合理化を行ったことだ。

m = iota(1, 10000000)                  ' 大きな配列
printM subV(m, Array(2,4,6,8))         ' m(2), m(4), m(6), m(8) 
  3  5  7  9

上の通り、配列とインデックスを指定するとその位置の要素を取り出してくるだけなので、subVを普通にループで実装することに問題はない。が、ループを避けて以下のように実装した。

'1次元配列の部分配列を作成する
Public Function subV(vec As Variant, ByRef index As Variant) As Variant
    Dim fn As Variant
    fn = p_getNth       ' getNth はN番目の要素を取り出す関数
    swap2nd fn, vec     ' 第2引数をswapによって対象配列に束縛する
    subV = mapF(fn, index)   ' indexに対してmapする
    swap2nd fn, vec     ' swapした対象配列を戻す
End Function
    Public Function p_subV(略

基本的には単一の要素を取得するgetNth関数をmapFで繰り返し適用しているだけだが、問題がある。引数vecに渡される対象配列が巨大な場合に、そのコピーを生成してしまったらすごく非効率になるのだ。
まず、fn = p_getNth(, vec)と普通に書いてしまうと、vecのコピーが発生し、fnは巨大な関数オブジェクトになる。効率の悪化を避けるためには上に書いたように、いったん裸の関数オブジェクトとして軽く生成しておいて、あとから第2引数にvecをswap渡ししてやればいい。
swapとはAPI関数swapVariantおよびそれを使ったswap1stswap2nd関数2 のことで、巨大な配列でもほとんど無視できるコストで交換できるものだ。ただし目的の処理が終わったらもう一度swapして戻してやらないと、対象配列が消えてしまう。

    fn = p_getNth       ' 裸の関数オブジェクト
    swap2nd fn, vec     ' 第2引数をあとからswapによって束縛

次にmapFの中身だが、C++dllの中のいちばん根っこのところ3 でVariant変数を2回もコピーしていた。Variantの実体ではなくポインタで十分なところをポインタに直したところ、上に書いたsubVが十分高速に動くようになった。
関数オブジェクトの大きさを気にしなくて済むとなると、できることが増えるかもしれない。

VBAHaskellの紹介 その16(ラムダ式?の生成)
VBAHaskellの紹介 その1(最初はmapF)


ソースコード
https://github.com/mYmd/VBA
dllバイナリ:
https://github.com/mYmd/VBA/blob/master/bin/mapM (32bit-Office用)
https://github.com/mYmd/VBA/blob/master/bin/mapM64 (64bit-Officey用)
VBAコード添付済みExcelブック
VBAHaskellほぼ全部入り.xlsm

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0