Delphi でズンドコキヨシを書いてみる

# はじめに
なんだか**ズンドコキヨシ**てのがあるみたいで。

 - [ズンドコキヨシまとめ (Qiita: @shunsugai@github)](https://qiita.com/shunsugai@github/items/971a15461de29563bf90)

**Delphi** で書かれたものもあるのですが、

 - [平成から令和を跨ぐズンドコキヨシのせいだよwith Delphi (Qiita: @e99h2121)](https://qiita.com/e99h2121/items/b67c03b3b53477e9847d)
 - [[Delphi][ネタ]ズンドコキヨシ・コードゴルフ (Qiita: @pik)](https://qiita.com/pik/items/9ddb684967a5bec4d970)

僕なりに書いてみたいと思います [^1]。


# コード

こんな感じ。**Delphi 10.4 Sydney** で書いたコンソールアプリケーションです。

```pascal:ZunDoko.dpr
{$APPTYPE CONSOLE}
const
  a: array [0..1] of string = ('ズン', 'ドコ');
begin
  Randomize;
  var l := MaxInt;
  repeat
    var v := Random(2);
    Write(a[v]);
    l := (l shl 1) or v;
  until (l and $1F) = $01;
  Writeln('キ・ヨ・シ！');
end.
```

ちょっと短く書くとこう。

```pascal:ZunDoko.dpr
{$APPTYPE CONSOLE}
const
  a: array [0..1] of string = ('ズン', 'ドコ');
begin
  Randomize;
  var l := MaxInt;
  repeat
    l := (l shl 1) or Random(2);
    Write(a[l and 1]);
  until (l and $1F) = $01;
  Writeln('キ・ヨ・シ！');
end.
```

## 説明
特に難しい事はやっていません。

 1. 変数 `l` を **MAXINT** で初期化。0xFFFFFFFF (0b11111111111111111111111111111111) になっている。
 2. 変数 `l` を左シフトしてズンドコの結果 (ランダム) を最下位ビットに格納。
 3. 最下位ビットを見て `ズン (0)`, `ドコ (1)` を表示。
 4. 変数 `l` と 0x1F (0b11111) の **and** (5 ワード分) を取って、結果が `0x01 (0b00001 = ズン(0)ズン(0)ズン(0)ズン(0)ドコ(1))` ならループを抜ける。
 5. `キ・ヨ・シ！` を表示してプログラム終了。

## もうひとつ

`System.Types` を **uses** してもいいのなら、次のような記述も可能です。

```pascal:ZunDoko.dpr
{$APPTYPE CONSOLE}
uses
  System.Types;
begin
  Randomize;
  var l := MaxInt;
  repeat
    l := (l shl 1) or Random(2);
    Write(TStringDynArray.Create('ズン', 'ドコ')[l and 1]);
  until (l and $1F) = $01;
  Writeln('キ・ヨ・シ！');
end.
```

## さらにもうひとつ
`System.SysUtils` を **uses** してもいいのなら、次のような記述も可能です。

```pascal:ZunDoko.dpr
{$APPTYPE CONSOLE}
uses
  System.SysUtils;
begin
  Randomize;
  var l := MaxInt;
  repeat
    l := (l shl 1) or Random(2);
    Write('ズン,ドコ'.Split([','])[l and 1]);
  until (l and $1F) = $01;
  Writeln('キ・ヨ・シ！');
end.
```

一行だと 160 文字にできます。

```pascal:ZunDoko.dpr
uses SysUtils;begin Randomize;var l:=MaxInt;repeat l:=(l shl 1)or Random(2);Write('ズン,ドコ'.Split([','])[l and 1]);until(l and 31)=1;Write('キ・ヨ・シ！')end.
```

## さらにさらにもうひとつ

動的配列をインライン変数宣言する記述も可能です。

```pascal:ZunDoko.dpr
{$APPTYPE CONSOLE}
begin
  Randomize;
  var a := ['ズン', 'ドコ'];
  var l := MaxInt;
  repeat
    l := (l shl 1) or Random(2);
    Write(a[l and 1]);
  until (l and $1F) = $01;
  Writeln('キ・ヨ・シ！');
end.
```

上記コードを

 - 改行を含めた不要なホワイトスペースを抜く
 - 16進値は短くなるのであれば 10進値に変更
 - 演算子の優先順位により不要な `()` を除去
 - **end.** 前のセミコロンを抜く

あれやこれやで一行にし、

 - `{$APPTYPE CONSOLE}` を抜く
 - `Writeln()` を `Write()` にする
 - (`Randomize()` を抜く)

様々なインチキを施せば、

```pascal:ZunDoko.dpr
begin Randomize;var l:=1;var a:=['ズン','ドコ'];repeat l:=l shl 1 or Random(2);Write(a[l and 1]);until l and 31=1;Write('キ・ヨ・シ！')end.
```
140 文字で納まるようです (Randomize を抜いて 130 文字)。僕が単独で思いついた短いコードはここまでです。

**See also:**

 - [演算子の優先順位 (DocWiki)](http://docwiki.embarcadero.com/RADStudio/ja/%E5%BC%8F%EF%BC%88Delphi%EF%BC%89#.E6.BC.94.E7.AE.97.E5.AD.90.E3.81.AE.E5.84.AA.E5.85.88.E9.A0.86.E4.BD.8D)

## 短いコードの変遷

パクりパクられ...いえいえ、インスパイアの歴史です (w

| 作者 | 長さ [^2]| 説明 |
|:---|:---:|:---|
| @pik | 167 | 初版  |
| @ht_deko | 160 | Split() を使った手法 |
| @fujitanozomu | 147 | 160文字版のズンドコ逆転、シフト演算を乗算に |
| @ht_deko | 140 | 動的配列をインライン変数で定義 |
| @ht_deko | 133 | 140文字版に147文字版のロジックを適用 |
| @ht_deko | 132 | 133文字版のビットフラグを見直し |
| @pik | 130 | 演算順序の変更による空白の除去、<br>空文のセミコロン除去 |
| @ht_deko | 129 | 132文字版に130文字版のロジックを適用 |
| @fujitanozomu | 125 | ビットフラグの見直し |
| @pik | 122 | 125文字版の動的配列を Copy() で置き換え |
| @fujitanozomu | 120 | 122文字版の Copy() の引数を変更、<br>Index=1 と Index=0 が同じ事を利用 |

※ この記事と [@pik さんの記事](https://qiita.com/pik/items/9ddb684967a5bec4d970)、双方のコメント欄にコードがあります。
※ 他言語持ってきて「こんなに短く書けるぜ～」って言うのは野暮なのでやめておきましょう。

# おわりに
元々は自作関数の課題だったみたいで、僕が書いたコードは関数でない時点で論外なのかもしれません。

そもそも、**Delphi (Pascal)** だと結果を返さないルーチンは**手続き**であり関数ではないのですが、課題のズンドコは何を返したのでしょうね。

**See also:**

 - [<11> 手続きと関数 (標準 Pascal 範囲内での Delphi 入門) (Qiita)](https://qiita.com/ht_deko/items/b93ac03bfee002f17137)

[^1]: この記事を書いている時点では、Delphi で書かれたコード (記事) しか見ていません。
[^2]: いわゆる `全角文字` は 2 バイト換算です。