VBAHaskellでの関数定義

##はじめに
これは、[Visual Basic Advent Calendar 2016](http://qiita.com/advent-calendar/2016/vb)の16日目の記事です。

これまで2回Advent Calendar 2016に紹介記事を書かせて頂きましたが、2回ともデモっぽい内容だったので、今回はちゃんとVBAHaskellにおける関数の作り方について書きたいと思います。

##関数にできるもの
VBAHaskellでは、関数本体とそれとペアになる関数オブジェクトを定義して、ゆるふわな関数型っぽいプログラムを書くことができます。しかしその使い方ができる関数のパターンは一つだけです。

```vbnet
' VBAHaskellで基本的に定義できる たったひとつの関数パターン
Function f(ByRef a As Variant, ByRef b As Variant) As Variant
' ただし、第二引数は Optional にすることはできる
Function g(ByRef a As Variant, Optional ByRef b As Variant) As Variant
```
これだけですが、以下の3つの方法で用途を広げられます。

##配列関数の引数にする
`plus`と、そのペアである`p_plus`は単に足し算するだけの関数ですが、`mapF`や`zipWith`、`fold/scan`系の関数の引数にすれば大量のデータを処理できます。

```vbnet
' ふたつの配列の各要素を加える
printM zipWith(p_plus, iota(0, 9), iota(30000, 30009))
  30000  30002  30004  30006  30008  30010  30012  30014  30016  30018
```
`zipWith`は二つの配列から新たに配列を作成する関数ですが、`p_plus`がその第1引数になっています。API内でループして`0+30000, 1+30001, ..., 9+30009`という配列を作ってます。引き算にしたければ`p_minus`を、掛け算にしたければ`p_mult`を指定します。

```vbnet
' 自然数を順次足していく
printM scanl1(p_plus, iota(0, 100))
  0  1  3  6  10  15  21  28  36  45  55  66  78  91  105  120  136  153  171  190  210  231  253  276  300  325  351  378  406  435  465  496  528  561  595  630  666  703  741  780  820  861  903  946  990  1035  1081  1128  1176  1225  1275  1326  1378  1431  1485  1540  1596  1653  1711  1770  1830  1891  1953  2016  2080  2145  2211  2278  2346  2415  2485  2556  2628  2701  2775  2850  2926  3003  3081  3160  3240  3321  3403  3486  3570  3655  3741  3828  3916  4005  4095  4186  4278  4371  4465  4560  4656  4753  4851  4950  5050
```
同じく`p_plus`を使ってますが、端からどんどん処理して履歴を出す`scanl1`に使うと上のようになります。

##関数を合成する
`less`と`p_less`は大小関係 `"<"` 表しますが、文字列の長さを求める`p_len`と組み合わせることによって、配列を「文字列の長さ順」にソートすることができます。

```vbnet
' String の配列
s = Array("Zbkn", "Afkbpirthrit", "123", "Kdgndi")
' 文字列の長さ順にしたときの順位 ord を出力する
ord = sortIndex_pred(s, p_less(p_len, p_len))
printM ord
  2  0  3  1
' Ord 順に並べる
printM  subV(s, ord)
  123  Zbkn  Kdgndi  Afkbpirthrit
```
上の例では、`p_less(p_len, p_len)` がソートに使う比較関数となり、デフォルト引数が隠れているので実際にはこんな構造です。
　　**p_less(p_len( _ :sunny: _ ), p_len( _ :stuck_out_tongue: _ ))**
これにふたつの文字列`s1`と`s2`を代入すると `Len(s1) < Len(s2)` となって、文字列長の比較になります。個々の関数は単機能でも合成すれば複雑なものを作れるので、目的ごとにいちいち `Function` プロシージャを書いて関数を定義せずにすみます。たとえば文字列長で降順ソートしたければ`p_greater`を使えばいいです。

```vbnet
' String の配列
s = Array("Zbkn", "Afkbpirthrit", "123", "Kdgndi")
' 文字列の長さ順（降順）にしたときの順位 ord を出力する
ord = sortIndex_pred(s, p_greater(p_len, p_len))
printM ord
  1  3  0  2
' Ord 順に並べる
printM  subV(s, ord)
  Afkbpirthrit  Kdgndi  Zbkn  123
```


##引数、戻り値を配列にする
自分自身が配列を引数にとることで、多数の要素を相手にした処理をすることができます。戻り値を配列にするのも同様です。
たとえば`filterR`という関数は、第1引数に対象配列、第2引数に<0/1>フラグからなる配列を指定することによって、配列をフィルタリングをします。

```vbnet
'ベクトル・配列の（行の）フィルタリング
'Flgは 0/1
Public Function filterR(ByRef data As Variant, ByRef flg As Variant) As Variant
```
下は整数列から3の倍数だけ抽出する単純な例ですが、フラグを作るのに関数合成を使っています。

```vbnet
a = iota(0, 30)               ' 0から30までの整数列
f = p_equal(p_mod(, 3), 0)    ' (x Mod 3) = 0 を表す関数
printM filterR(a, mapF(f, a))
  0  3  6  9  12  15  18  21  24  27  30
```

##関数オブジェクトの作り方
Optional引数のないいちばん身近な例は足し算です。
この関数は、Haskell_2_stdFunモジュール（[Github](https://github.com/mYmd/VBA/blob/master/Haskell_2_stdFun.bas)、もしくは[ここ](http://home.b07.itscom.net/m-yamada/VBA/Haskell_2_stdFun.bas)）にあります。

```vbnet
'加算
Function plus(ByRef a As Variant, ByRef b As Variant) As Variant
    plus = a + b
End Function

    Function p_plus(Optional ByRef firstParam As Variant, Optional ByRef secondParam As Variant) As Variant
        p_plus = make_funPointer(AddressOf plus, firstParam, secondParam)
    End Function
```

`Function plus` は最初に述べたパターンを守って普通に定義したVBA関数です。
`p_plus`の方は`plus`のペアであることを明確にするために`p_plus`という名前にしていますが、`AddressOf`の引数が`plus`になっていることが本質です。
自分で新しく関数とその関数オブジェクトを作成するときは、これをコピペして関数名だけ変えればOKです。`make_funPointer`がすべての仕事をやっています。

第二引数がOptionalな場合は、それが全くのダミーであるかそうでないかでパターンが分かれます。
次の指数関数 `expN` は組み込みの`exp`そのまんまですが、第二引数は名前の通りダミーで、あってもなくても無影響です。`IsMissing`の判定もしていません。この場合は、最初のパターンと同じで、`make_funPointer`で関数オブジェクトを作ります。

```vbnet
'指数関数
Function expN(ByRef a As Variant, ByRef dummy As Variant) As Variant
    expN = Exp(a)
End Function
    Function p_exp(Optional ByRef firstParam As Variant, Optional ByRef secondParam As Variant) As Variant
        p_exp = make_funPointer(AddressOf expN, firstParam, secondParam)
    End Function
```
対数関数の場合は第二引数がなければ自然対数、あればその数を底にするため、`IsMissing`で判定をしています。こういう関数に対しては`make_funPointer`の代わりに`make_funPointer_with_2nd_Default`を使います。

```vbnet
'対数関数
Function logN(ByRef a As Variant, Optional ByRef base As Variant) As Variant
    If IsMissing(base) Then
        logN = Log(a)
    Else
        logN = Log(a) / Log(base)
    End If
End Function
    Function p_log(Optional ByRef firstParam As Variant, Optional ByRef secondParam As Variant) As Variant
        p_log = make_funPointer_with_2nd_Default(AddressOf logN, firstParam, secondParam)
    End Function
```
`make_funPointer`と`make_funPointer_with_2nd_Default`がやっていることはAPIとの絡みもあるので複雑ですが、どちらを使うかの基準は上に述べた通り単純なので、`p_plus`か`p_log`のどちらかをコピペするという方針で十分です。

* * *

リンク

[Qiita　VBAHaskellの紹介　その1　（最初はmapF）](http://qiita.com/mmYYmmdd/items/c731edf943acc0a0ebe9)
[ソースコード(Github)](https://github.com/mYmd/VBA)
[VBAHaskellの関数リファレンス](http://home.b07.itscom.net/m-yamada/vh_Manual/VBAHaskell_reference.htm)
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](https://github.com/mYmd/VBA/blob/master/bin/VBAHaskell%E3%81%BB%E3%81%BC%E5%85%A8%E9%83%A8%E5%85%A5%E3%82%8A.xlsm)