VBAHaskellの改修まとめ

最近VBAHaskellにいくつか変更をしたのでまとめの記事を書きます。

    VBAHaskell全関数リファレンス → VBAHaskell_reference

1. API関数self_zipWithを追加しました

次のようなシグネチャの関数をHaskell_0_declare.basの一番下に追加しました。

' 1次元配列の離れた要素間で2項操作を適用する
Declare PtrSafe Function self_zipWith Lib "mapM.dll" ( _
                                ByRef pCallback As Variant, _
                            ByRef vec As Variant, _
                      ByVal shift As Long) As Variant

名前から想像できると思いますが、2つの配列の要素間に関数を適用する zipWith と同じようなことを、一つの配列の中で行うものです。
3番目の引数shiftに与えた数値が「いくつ要素をずらしながら処理を適用するのか」を表し、正負どちらの方向にずらしても1周して戻ってきます。

たとえば 関数fと 配列a(a(0)~a(9)の範囲とします)を引数にするさい、
self_zipWith(f, a, 1)とすると
f(a(0), a(1)), f(a(1), a(2)), f(a(2), a(3)), ... , f(a(8), a(9)), f(a(9), a(0))
が、
self_zipWith(f, a, -1)とすると
f(a(0), a(9)), f(a(1), a(0)), f(a(2), a(1)), ... , f(a(8), a(7)), f(a(9), a(8))
が戻り値になります。

printM self_zipWith(p_plus, iota(1, 10), 5)
  7  9  11  13  15  7  9  11  13  15

元の配列は変更されません。
次の項目である関数get_uniqueや以前からあった関数adjacent_opの効率を高めるためにこれを作りました。

2. get_unique 関数を追加しました

misc_utility.bas に、1次元配列の重複した要素を削除する関数get_uniqueを追加しました。

Public Function get_unique(ByRef vec As Variant, _
                           Optional ByRef comp As Variant) As Variant

第2引数compは等値条件を表します。隣どうしの要素をcompで比較した結果が1になるものを「重複」と判定します。離れた場所にある要素は比較しないので、完全に重複を削除するためにはソート済であることが前提になります。
第2引数を省略するとp_equalを使った場合と同じになります。元の配列は変更されません。

a = uniform_int_dist(20, 5, 15)       ' [5, 15]の範囲のランダム整数を20個生成
permutate a, sortIndex(a)             ' 昇順ソート
printM a
  7  7  8  8  9  10  10  10  10  10  11  11  12  14  14  14  15  15  15  15
printM get_unique(a)
  7  8  9  10  11  12  14  15
' ↑ 重複要素が削除されている

2次元以上の場合は以下のようにジャグ配列にする必要があります。

a = zip(uniform_int_dist(20, 5, 7), uniform_int_dist(20, 101, 103))    ' ジャグ配列
permutate a, sortIndex_pred(a, p_less_dic)                             ' 辞書順に昇順ソート
printM_  a
  5  101
  5  101
  5  101
  5  102
  5  103
  5  103
  6  101
  6  101
  6  101
  6  102
  6  102
  6  102
  6  103
  7  101
  7  102
  7  102
  7  103
  7  103
  7  103
  7  103
printM_  get_unique(a, p_equal_dic)
  5  101
  5  102
  5  103
  6  101
  6  102
  6  103
  7  101
  7  102
  7  103

3. その他の変更

  • equal_dic (=) 関数とnotEqual_dic (<>)関数を追加しました。
    dic は dictional の略で、辞書式比較のことを示します。二つの1次元配列の要素を順に見ていって等値かそうでないかを返します。列に対する大小関係を判定する関数less_dic (<),less_equal_dic (<=),greater_dic (>),greater_equal_dic (>=)はもともと存在していましたが、この2つを新たに追加しました。
    Haskell_5_sort.bas

  • p_not関数とp_imply関数を追加しました。
    p_notはいわゆる論理Not、p_implyは「AならばB」の「ならば」に相当するものです。いずれも関数オブジェクトのみです。
    「AならばB」は not A と B のいずれかが真の時に成り立つ命題です。
    misc_utility.bas

  • p_Trim関数を追加しました。
    VBA組み込みのTrim, LTrim, RTrim関数をひとまとめにしたもので、第2引数0または省略時1-1がそれぞれに対応します。
    misc_utility.bas

printM_  mapF(p_str_cat(p_str_cat("0"), "1"), mapF(p_Trim, Array("  AB C  ", "  EFGH  ", "  WXYZ  ")))
0AB C1
0EFGH1
0WXYZ1
  • A_overlap_B 関数を追加しました。
    1次元配列として表された集合AとBに対して、共通部分と非共通部分を示すフラグを出力します。
Function A_overlap_B(ByRef a As Variant, _
                     ByRef b As Variant, _
                     Optional ByRef comp As Variant) As Variant

第3引数のcompは大小関係を表す述語で、それぞれの集合はそれによってソート済みという前提です。

a = uniform_int_dist(20, 0, 20)     '  0から20までの正数乱数(20個)
b = uniform_int_dist(20, 10, 30)    ' 10から30までの正数乱数(20個)
permutate a, sortIndex(a)         ' a をソート
permutate b, sortIndex(b)         ' b をソート

x = A_overlap_B(a, b)      ' 第3引数省略時は p_less が使われる

printM catR(a, x(0))       ' x(0) はAの各要素がBに属しているかのフラグ
  0  1  3  4  8  8  9  10  11  11  11  13  14  15  17  17  17  18  19  20
  0  0  0  0  0  0  0   1   0   0   0   1   0   1   1   1   1   1   0   1
printM catR(b, x(1))       ' x(1) はBの各要素がAに属しているかのフラグ
  10  12  13  13  15  15  17  17  17  18  18  20  20  21  24  25  25  26  27  29
   1   0   1   1   1   1   1   1   1   1   1   1   1   0   0   0   0   0   0   0

misc_utility.bas