VBAHaskellの紹介　その3　（FizzBuzz）

2016-02-19
ユーティリティモジュールに追加した **p_try** という関数（一種の構文糖）によってFizzBuzzがかなり短く書けるようになった。

```vbnet
fn=p_try(p_mod(, 15), p_try(p_mod(, 5), p_try(p_mod(, 3), , "Fizz"), "Buzz"), "FizzBuzz")
printM mapF(fn, iota(1, 300))
  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
```

2015-07-10
前のよりずっとわかりやすいFizzBuzzができたので書く。
というより下の記事を書いたときは寝ぼけていたとしか思えない。

```vbnet
' 分かりやすいFizzBuzz
fun3 = p_if_else(, Array(p_mod(, 3), placeholder, "Fizz"))
fun5 = p_if_else(, Array(p_mod(, 5), fun3, "Buzz"))
fun15 = p_if_else(, Array(p_mod(, 15), fun5, "FizzBuzz"))
printM mapF(fun15, iota(1, 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
```

前より短くなったわけではないが、product_setとかreplaceNullといった関数を使う必要は全然なかったのだ。シンプルなif_elseのネストに対してmapFするだけになった。



* * *


（この記事ははてなブログ　http://mmyymmdd.hatenablog.com/ の再掲です）
2015-04-11
基本的なパーツの紹介の前にFizzBuzzを紹介する。

```vbnet
' 書きたくないコード
   If x Mod 15 = 0 Then    Debug.print "FizzBuzz"
   ElseIf x Mod 5 = 0 Then    Debug.print "Buzz"
   ElseIf x Mod 3 = 0 Then    Debug.print "Fizz"
   Else    Debug.print x      '(改行は省略)
```
１から100までの整数にこのロジックを当てはめるだけだが、もちろんこんなコードを書かずに基本的なパーツの組み合わせでシンプルに表現したい。

これはなかなか短くできなかった。現状のコードは以下の通り。

```vbnet
m = Array(Array(p_mod(, 15), Null, "FizzBuzz"), _
                Array(p_mod(, 5), Null, "Buzz"), _
                Array(p_mod(, 3), placeholder, "Fizz"))
printM foldl1(p_replaceNull, product_set(p_if_else, iota(1, 100), m), 2)
```
 使っているパーツは、剰余関数 **p_mod**、条件分岐関数 **p_if_else**、mapの直積版 **product_set**、上書き関数 **p_replaceNull**、そして **foldl1** [^1] である。

`product_set`は与えられた2つのリストの直積に対して関数を適用するので、イメージとしては表のようになる。対象となる関数は **if_else**で、1つ目のリストの要素は1~100の整数、2つ目のリストの要素は [条件、真の場合の値、偽の場合の値] だ。

if_else

| n  | m(0)  | m(1) | m(2)| replaceNull |
|:--:|:-----:|:----:|:----:|:----------:|
|1   |   Null | Null | 1    |1          |
|2   |   Null | Null | 2    |2          |
|3   |  Null  | Null | Fizz |Fizz       |
|4   |  Null  | Null | 4    |4          |
|5   |  Null  | Buzz | 5    |Buzz       |
|6   |   Null | Null | Fizz |Fizz       |
|... |  ...   |  ... | ...  |    ...    |
|15  |FizzBuzz| Buzz |Fizz  |FizzBuzz   |
|16  |  Null  | Null |16    |16         |
|... |   ...  | ...  | ...  |  ...      |
|98  |  Null  | Null |  98  |98         |
|99  |  Null  | Null |Fizz  |Fizz       |
|100 | Null   | Buzz |100   |100        |

15と5については、割り切れない場合にNull値、割り切れる場合にそれぞれFizzBuzzとBuzzを設定し、3についてだけは割り切れないとき元の整数引数を設定している。それぞれの `p_mod` で**第2引数を束縛**していることに注意。

これを左から見ていって、「Nullは新しい値に置き換えるがNullでなかったらそのまま」という関数を繰り返し適用すればよい。その関数はreplaceNullで、この配列に対して **foldl1** すれば結果が得られる。foldl1 の最後の引数である 2 は、2次元配列に対する畳み込みを横方向に行えという指定である。

 このコードはリンクしたgithubテストモジュール([test_module.bas](https://github.com/mYmd/VBA/blob/master/test_module.bas)）の中のデモ関数 vbaUnitに書かれている。

ソースコード：https://github.com/mYmd/VBA

[VBAHaskellの紹介 その4 （Find）](http://qiita.com/mmYYmmdd/items/9299e7cf862b4c8db29e)
[VBAHaskellの紹介 その2 （合成関数）](http://qiita.com/mmYYmmdd/items/1e528ad68ad2c4e3de29)
[VBAHaskellの紹介 その1 （最初はmapF）](http://qiita.com/mmYYmmdd/items/c731edf943acc0a0ebe9)

[^1]: Haskellの foldl1 と同様だがVBAには2次元以上の配列があるので、それに合わせた仕様になっている