place_fill 関数の追加(VBAHaskell)

VBAHaskellにplace_fill という関数を追加して Haskell_2_stdFun.bas モジュールに置いた。

1次元配列内の指定した複数の位置に関数もしくは定数値を適用して、その場所に値を埋めるものだ。 埋めたあと配列そのものをmoveして返す。

' 配列の特定位置に関数/値を適用する(値を埋めてmoveして返す)
Function place_fill(ByRef vec As Variant, _               👈 対象配列
                    ByRef fun As Variant, _               👈  適用する関数または定数
                    ByRef indice As Variant, _            👈 インデックス
                    Optional ByRef souce As Variant       👈 ソース配列
                ) As Variant

これまでは配列全体を処理する関数がほとんどで、こういう関数はなかった。 関数にはインデックスまたはソース配列の各要素が代入され、模式的には for ( i ∈ indice ) vec(i) = fun(i) もしくは for ( i ∈ indice ) vec(i) = fun(source(i)) というループとなる。ただし fun が関数でなかった場合はそれ自身が値として代入される。「インデックスまたはソース配列」というのは、sourceが省略された場合はindex自身がソース配列となるという意味だ。また、sourcevec自身を代入することもできる。

基本的な使い方はこうだ。

' 長さ10の配列の(2, 5, 8)の位置に定数を置く
printM place_fill(repeat(0, 10), 1, Array(2,5,8))
  0  0  1  0  0  1  0  0  1  0

' 長さ10の配列の(2, 5, 8)の位置に文字列の一部分を置く
printM place_fill(repeat("-", 10), p_left("abcdefghijk"), Array(2,5,8))
  -  -  ab  -  -  abcde  -  -  abcdefgh  -



これでFizzBuzzを書くとこうなる。 あまり短くはならないが、素直なコードになる。

m = place_fill(iota(0,100), "Fizz", mapF(p_mult(3), iota(1, 33)))
m = place_fill(m, "Buzz", mapF(p_mult(5), iota(1, 20)))
m = place_fill(m, "FizzBuzz", mapF(p_mult(15), iota(1, 6)))
printM m, -100
  1  2  Fizz  4  Buzz  Fizz  7  8  Fizz  Buzz  11  Fizz  13  14  FizzBuzz  16  17  Fizz  19  Buzz  Fizz  22  23  Fizz  Buzz  26  Fizz  28  29  FizzBuzz  31  32  Fizz  34  Buzz  Fizz  37  38  Fizz  Buzz  41  Fizz  43  44  FizzBuzz  46  47  Fizz  49  Buzz  Fizz  52  53  Fizz  Buzz  56  Fizz  58  59  FizzBuzz  61  62  Fizz  64  Buzz  Fizz  67  68  Fizz  Buzz  71  Fizz  73  74  FizzBuzz  76  77  Fizz  79  Buzz  Fizz  82  83  Fizz  Buzz  86  Fizz  88  89  FizzBuzz  91  92  Fizz  94  Buzz  Fizz  97  98  Fizz  Buzz


misc_mathに実装した素数一覧を出力する関数と組み合わせるとこういう表示もできる。

printM place_fill(iota(0, 30), "☆", primeNumbers(30))
  0  1  ☆  ☆  468  9  101214  15  161820  21  2224  25  26  27  2830


☆の前後にその素数を付けて表示するためには、文字連結関数を引数にすればいい。

printM place_fill(iota(0,30), p_str_cat("☆"), primeNumbers(30))
  0  123  45  67  8  9  1011  1213  14  15  1617  1819  20  21  2223  24  25  26  27  2829  30

printM place_fill(iota(0,30), p_str_cat(, "☆"), primeNumbers(30))
  0  1  234  56  78  9  10  1112  1314  15  16  1718  1920  21  22  2324  25  26  27  28  2930


この関数の実装は以下の通り。

' 配列vecの指定位置に関数/値を適用する(値を埋めてmoveして返す)
Function place_fill(ByRef vec As Variant, _
                    ByRef fun As Variant, _
                    ByRef indice As Variant, _
                    Optional ByRef souce As Variant) As Variant
    Dim i As Long
    ' souceまたはindex(souce 省略時)を埋め込む
    If is_bindFun(fun) Then
        Dim tmp As Variant
        If IsMissing(souce) Then    ' = index
            tmp = mapF(fun, indice)
        Else
            tmp = mapF(fun, subV(souce, indice))
        End If
        For i = LBound(indice) To UBound(indice) Step 1
            Call swapVariant(vec(indice(i)), tmp(i))
        Next i
    Else    ' 単一の値を埋め込む
        For i = LBound(indice) To UBound(indice) Step 1
            Call assignVar(vec(indice(i)), fun)
        Next i
    End If
    Call swapVariant(place_fill, vec)
End Function

github.com

http://home.b07.itscom.net/m-yamada/VBA/