いろんな型が混じった配列を任意の比較基準でソートしたい。例えば、
Object型はその他の型より常に大きい。
Object型どうし・・・TypeNameの値で比較(文字列としての比較となる)
それ以外・・・・・・VarTypeの値で比較
という基準で比較するとしましょう。
比較用の述語をVBAHaskellの作法でこのように書きます。
Function lessByName(ByRef a As Variant, ByRef b As Variant) As Variant
If IsObject(a) Then
If IsObject(b) Then
lessByName = IIf(TypeName(a) < TypeName(b), 1, 0)
Else
lessByName = 0
End If
ElseIf IsObject(b) Then
lessByName = 1
Else
lessByName = IIf(VarType(a) < VarType(b), 1, 0)
End If
End Function
Function p_lessByName(Optional ByRef firstParam As Variant, Optional ByRef secondParam As Variant) As Variant
p_lessByName = make_funPointer(AddressOf lessByName, firstParam, secondParam)
End Function
以下のように適当に配列を作ってみます。
a = Array(0, 1, 2, 3, 1.3, "ABC")
Set a(1)= Application
Set a(3)= Application.Windows
printM a
0 Application 2 Windows 1.3 ABC
ちなみにprintM
は要素がObject型の場合はそのTypeNameを表示する仕様です。
さて、TypeNameとVarTypeの値はそれぞれこうなっています。
?TypeName(a(0)), VarType(a(0))
Integer 2
?TypeName(a(1)), VarType(a(1))
Application 8
?TypeName(a(2)), VarType(a(2))
Integer 2
?TypeName(a(3)), VarType(a(3))
Windows 9
?TypeName(a(4)), VarType(a(4))
Double 5
?TypeName(a(5)), VarType(a(5))
String 8
最初に定義した比較用の述語を与えてソートインデックスを計算します。
s = sortIndex_pred(a, p_lessByName)
printM s
0 2 4 5 1 3
これで実際にソートして出力してみます。
permutate a, s
printM a
0 2 1.3 ABC Application Windows
一瞬 2 < 1.3
で??と思いますが、比較基準に従っています。(Integer:2 < Double:5)
VBAHaskellのソートは安定ソートではないですが、この場合はたまたま元の順番が保たれました。
(一般に安定ソートの需要って高いんでしょうか?)
リンク
Qiita VBAHaskellの紹介 その1 (最初はmapF)
ソースコード(Github)
VBAHaskellの関数リファレンス
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