2つの表を比較する

また人様のネタを頂戴する。

整数からなる列がふたつあり、それぞれ 表A、表B とする。それぞれ片側にしかない数字を抽出しようというものだ。

vbaHaskellで短く書くとこうなる。ただし値ではなく、ある/ないの結果を 1 / 0 で表示した。

ab = unZip(mapF(p_equal_range(表B), 表A))
ba = unZip(mapF(p_equal_range(表A), 表B))
printM zipWith(p_less, ab(0), ab(1))
printM zipWith(p_less, ba(0), ba(1))

equal_rangelower_boundupper_bound の結果を同時に出力する関数で、それぞれソート済み配列の中から検索対象の値が存在する「先頭」および「最後の次」の場所を求めるものだ。
(モジュールはここ → https://github.com/mYmd/VBA/blob/master/Haskell_5_sort.bas
言い換えると目的とする値は [ lower_bound, upper_bound ) の中にあり、その個数は upper_bound - lower_bound で求められる。存在するかどうかだけなら、lower_bound < lower_bound が成り立つかどうかで判定できるので less 関数を使って計算している。

上のコードを少し冗長に書いてみる。

' //ふたつの表を表示してみる
printM 表A
  1  2  3  5  6  6  6  7  13  14  15  19  26  30  30
printM 表B
  1  4  5  7  7  8  9  10  10  11  12  13  17  17  20  20  20  24  27  30
' //AからBを見たときの equal_range
equal_range_from_A_to_B = mapF(p_equal_range(表B), 表A)
' //BからAを見たときの equal_range
equal_range_from_B_to_A = mapF(p_equal_range(表A), 表B)

' //equal_range_from_A_to_Bを表示してみる(説明は後からの手書き)
for each z in equal_range_from_A_to_B : printM z: next z
  0  1              <=  1は表B(0)にある   (0 < 1だから)
  1  1              <=  2は表Bにない      (1 = 1だから)
  1  1              <=  3は表Bにない
  2  3              <=  5は表B(2)にある
  3  3              <=  6は表Bにない
  3  3              <=  6は表Bにない
  3  3              <=  6は表Bにない
  3  5              <=  7は表B(3), B(4)にある
  11  12            <= 13は表B(11)にある
  12  12            <= 14は表Bにない
  12  12            <= 15は表Bにない
  14  14            <= 19は表Bにない
  18  18            <= 26は表Bにない
  19  20            <= 30は表B(19)にある
  19  20            <= 30は表B(19)にある
' //equal_range_from_B_to_Aを表示してみる(説明は後からの手書き)
for each z in equal_range_from_B_to_A : printM z: next z
  0  1              <=  1は表A(0)にある
  3  3              <=  4は表Aにない
  3  4              <=  5は表A(3)にある
  7  8              <=  7は表A(7)にある
  7  8              <=  7は表A(7)にある
  8  8              <=  8は表Aにない
  8  8              <=  9は表Aにない
  8  8              <= 10は表Aにない
  8  8              <= 10は表Aにない
  8  8              <= 11は表Aにない
  8  8              <= 12は表Aにない
  8  9              <= 13は表A(8)にある
  11  11            <= 17は表Aにない
  11  11            <= 17は表Aにない
  12  12            <= 20は表Aにない
  12  12            <= 20は表Aにない
  12  12            <= 20は表Aにない
  12  12            <= 24は表Aにない
  13  13            <= 27は表Aにない
  13  15            <= 30は表A(13), A(14)にある

' //結果を表示してみる
ab = unzip(equal_range_from_A_to_B)
printM zipWith(p_less, ab(0), ab(1))
  1  0  0  1  0  0  0  1  1  0  0  0  0  1  1
ba = unzip(equal_range_from_B_to_A)
printM zipWith(p_less, ba(0), ba(1))
  1  0  1  1  1  0  0  0  0  0  0  1  0  0  0  0  0  0  0  1

なお、unZip 関数はジャグ配列をほどいて以下のように再構成する関数である。

(
 (0, 1),
 (1, 1),
 (1, 1),
 (2, 3),
 (3, 3),
 (3, 3),
 (3, 3),
 (3, 5),
 (11, 12),
 (12, 12),
 (12, 12),
 (14, 14),
 (18, 18),
 (19, 20),
 (19, 20),
)

unZip ↓

(
 (0, 1, 1, 2, 3, 3, 3, 3, 11, 12, 12, 14, 18, 19, 19),
 (0, 1, 1, 3, 3, 3, 3, 5, 12, 12, 12, 14, 18, 20 ,20)
)

上の表Aと表Bはランダム数値を生成するモジュールで作った。
VBAHaskellの紹介 その25(乱数生成:メルセンヌ・ツイスタ mt19937) - Qiita

' //ランダムな整数を作ってソートしておく
表A = uniform_int_dist(15, 1, 30) : permutate 表A, sortIndex(表A)   ' //[1~30]から15個
表B = uniform_int_dist(20, 1, 30) : permutate 表B, sortIndex(表B)   ' //[1~30]から20個