【DNCL】大学入試の言語でFizzBuzzをできる限り短く実装しよう！

# 前書き
本記事は、[Qiitaのesolang Advent Calendar 2024](https://qiita.com/advent-calendar/2024/esolang) の、12日目の記事です。

世の中にはできる限りプログラムの文字数を少なくして競う競技、通称「**Code Golf**」と呼ばれるものが存在しています。
まあそれをやっていきます。
ちなみに今回は基本的に**バイト数を減らす**方針です。

（2024/12/13 15:50分追記）
最小文字数を更新しましたので、[追記](https://qiita.com/Sei2423/items/3c4f4bcafb27bfe09808#追記)に書きました。

# 言語紹介
~~今回使用する言語は2025年度の共通テストから行われる「情報I」のテスト内で実際に使用される言語です。~~
（2024/12/17 12:30追記）2024年度までの「情報関係基礎」のテスト内で使用されていた言語でした。
訂正してお詫び申し上げます。（@nodai2h_ITCさん、ありがとうございます。）
<br>
言語名は「**DNCL**」といいます。(Daigaku Nyushi Center Languageだと言われているそう)
そして意外と言語仕様がしっかりしているため、実際に実行することが可能だそうです。

今回はDNCL学習環境である「どんくり」を用いてCode Golfをします。

https://dolittle.eplang.jp/dncl

この言語は大学入試をするために~~とてつもなく魔改造~~作られたされた言語であり、コードの大部分が日本語で構成されている(特に構文が)、若干Pythonよりの言語です。

しかし、「なでしこになることができなかった」、「別にPythonでもいいだろ」、「読みにくい」、「言語仕様がひどい」、「Esolang」などと散々な言われ方をしている可哀想な言語です。~

ということで、一部の人から猛烈に批判が飛んでくるようなかわいそうな言語でCode Golfをしてみようと思います！

# コード
してみようとは言ってもこの記事を書いている時点ですでにコードは書き切っているので先にコードをお見せします

**ドドン**（セルフ）
```ruby: DNCL
i←0i≦99の間、n(x,y)はa←{y}a[i%x+1]または""を返すを実行する i←i+1n(3,"Fizz")+n(5,"Buzz")またはiを表示するを繰り返す
```
<sup>（86文字、118バイト）</sup>

はい。

はい？

はい。

---
見てもらえばわかる通り、見てもわからないです。
ということで、どのように縮めていったか、またどのようなところで苦戦したかを見てみましょう。
# DNCLのCode Golfテクニック！（無需要）
（改行を減らすみたいな大体どの言語でも言えるようなことは省きます）
DNCLは日本語を主体としている言語なので、ひらがなや漢字が多くなればなるほどバイト数が増えてしまいます。
なので**できる限り数式で**書くということを念頭に置きながら書きます。

## 繰り返し編

いろんな言語でCode Golfをやったことがある人ならわかると思いますが、
**for文って長いんですよ。**
しかもこのDNCL、PythonのRangeみたいな挙動するので特に文字数とられるんです。

まあとりあえず見てみましょう。
```ruby: DNCL
iを0から10まで1ずつ増やしながら、
    iを表示する
を繰り返す
```
長いですね。
ということでほかのループも試してみましょう。
```ruby:DNCL
i←0
i<10の間、
    iを表示する
    i←i+1
を繰り返す
```
```ruby:DNCL
i←0
ここから10回、
　　iを表示する
    i←i+1
を繰り返す
```
```ruby:DNCL
f(i)は
    iを表示する
    もしi!=10ならばf(i+1)を実行する
を実行する
f(0)
```
どうやらwhileが一番短そうですね。

## 条件分岐編
まあこんな記事読んでる時点で知っている方がほとんどでしょうが、if以外にも条件分岐する方法はあります。
```ruby:DNCL
もし1ならば
    1を表示する
を実行し、そうでなければ
    2を表示する
を実行する
```
割と長いですね。
他を見てみましょう。
```ruby:DNCL
a←{1,2}
a[0]を表示する
```
相当スッキリしましたね。
ただこの言語、「一度代入しないといけない」「Booleanの型変換が出来ない」「関数内の引数にBooleanを代入できない」と言う~~EsolangをEsolangたらしめる~~仕様があるので使い所には注意ですね。

関数内で`1を返す`とかが使えるなら
```ruby:DNCL
もし1ならば
    1を返す
を実行する
2を返す
```
```ruby:DNCL
1の間、
    1を返す
を繰り返す
2を返す
```
この言語本当にwhileが強いですね

あとは論理演算子も用意されてます。
```ruby:DNCL
1または2を返す
```
# 解説
```ruby:DNCL
i←0
i≦99の間、
    n(x,y)は
        a←{y}
        a[i%x+1]または""を返す
    を実行する
    i←i+1
    n(3,"Fizz")+n(5,"Buzz")またはiを表示する
を繰り返す
```
割と読みやすいですね！

上のパートで言った通り、反復処理はwhileが強いのでwhileで書いてます。

で、見たら分かる通り、何故か関数化されてますね？
これなにがあったかというと、、、

---
FizzBuzzの分岐は短くするためにできる限り数式で書きたい
　↓
まあ`{i,"Fizz","Buzz","FizzBuzz"}[i%3+i%5*2+1]`だと思ってた
　↓
`{1}[0]`の用に直接くっつけられないことに気づく
　↓
`True`を数字に変換することが出来ないことに気づく
　↓
余りが0の時だけの分岐`{1}[0]または2`をFizzとBuzzそれぞれに使う
　↓
処理が被ってるのをまとめる

---
みたいな経緯です。

<sup>（ちなみに`i%x+1`となっているのは配列が1スタートだからです。）</sup>

~~もう主要な部分殆ど終わってしまったんですが~~
```ruby:DNCL
n(3,"Fizz")+n(5,"Buzz")またはiを表示する
```

として、消してもエラーを吐かない改行を取っ払ったら終わりです！
```ruby:DNCL
i←0i≦99の間、
a←{y}a[i%x+1]
i←i+1n(3,"Fizz")
```
どうやら代入の右辺には1つの要素しかこないためスペースがなくても動くようです。
```ruby:DNCL
を実行する i←i+1
```
しかし構文末尾の`を実行する`のあとにはスペースや改行を挟まないと動きません。
なんで？
# 追記
(2024/12/13 15:50追記)
```ruby:DNCL
i←0i≦99の間、i←i+1a←{"FizzBuzz","Buzz","Fizz",i}a[i*i%3+i*i%5%3*2+1]を表示するを繰り返す
```
<sup>（75文字、92バイト）</sup>
（@angel_p_57さん、ありがとうございました！）
## 解説
```ruby:DNCL
i←0
    i≦99の間、
    i←i+1
    a←{"FizzBuzz","Buzz","Fizz",i}
    a[i*i%3+i*i%5%3*2+1]を表示する
を繰り返す
```
大分スッキリしましたね。
もはや分からない所が数式だけだと思います。
`i*i%3`を表にすると
|i =|1|2|3|...
|--:|:-:|:-:|:-:|:-:
|i<sup>2</sup> =|1|4|9|...
|i<sup>2</sup> % 3 =|1|1|0|...
<br>

となり、`i%3==0`の時だけ`0`になる関数となります。
また、`i*i%5%3*2`を表にすると
|i =|1|2|3|4|5|...
|--:|:-:|:-:|:-:|:-:|:-:|:-:
|i<sup>2</sup> =|1|4|9|16|25|...
|i<sup>2</sup> % 5 =|1|4|4|1|0|...
|i<sup>2</sup> % 5 % 3 =|1|1|1|1|0|...
|i<sup>2</sup> % 5 % 3 * 2 =|2|2|2|2|0|...
<br>

となり、`i%5==0`の時だけ`0`になる関数となります。
これにより、
```ruby:DNCL
i*i%3+i*i%5%3*2+1
```
この数式は、
`3`でも`5`でも割り切れる時`4`が、
`3`で割り切れて`5`で割り切れない時`3`が、
`5`で割り切れて`3`で割り切れない時`2`が、
`15`で割り切れる時`1`が
返ってくるので、配列の参照している場所にきれいに対応します。

# あとがき
ここまでお読みいただきありがとうございました！！！
1時間程度で書いたコードなのでもしかしたらまだ省略できる箇所があるかもしれません、、
何かあればコメントにお願いします！

<font color="ここまで読んでるんですか！？DNCLってesolangだよね（）　まあそれは置いといてとりあえず今回は割とサクッと出来た気がする？多分。　次何やりましょうかねぇ、、、 めちゃくちゃ縮まりまったな、、、間違った情報流してしまって申し訳ないです。"><font color="#303030">dev</font></font>