ズンドコキヨシ.jl with Iteration （追記あり）

@chezou さんの [ズンドコキヨシ.jl](http://qiita.com/chezou/items/e795708feaae6ff6df98) を見て、私も[ズンドコキヨシ](http://qiita.com/shunsugai@github/items/971a15461de29563bf90)を [Julia](http://julialang.org/) で書いてみたくなった。

ただのn番煎じじゃつまらないので、**できる限りトリッキーに**書いてみた。

**※【2016/03/17 20:20】 [厳密バージョン](#20160317-2020-%E8%BF%BD%E8%A8%98%E5%8E%B3%E5%AF%86%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3)について追記しました。**

## Iteration による実装

```jl:zundoko_iter.jl
# zundoko_iter.jl

immutable ZunDoko end

Base.start(::ZunDoko) = 0
Base.done(::ZunDoko, i::Int) = i == 6
function Base.next(::ZunDoko, i::Int)
    if i == 5
        ("キ・ヨ・シ！", 6)
    elseif i == 4
        rand([("ズン", 4), ("ドコ", 5)])
    else
        rand([("ズン", i + 1), ("ドコ", 0)])
    end
end

function runzundoko()
    for cw in ZunDoko()
        print(cw)
    end
    println()
end

runzundoko()
# => ズンズンズンドコズンズンズンドコドコドコドコズンズンズンズンズンドコキ・ヨ・シ！
```

Julia の Iteration の実装を利用。
（Iteration については、[私の過去記事](http://qiita.com/antimon2/items/1673fea71fc7b4e98bb4)等参照）
列挙そのものはこの Iteration の仕組みに依っているので、出力するのではなくリストで取得したければ、例えば
`collect(UTF8String, ZunDoko())`
のようにすれば `["ズン", "ズン", "ドコ", "ズン", "ドコ", "ドコ", "ズン", "ドコ", "ドコ", "ズン", "ズン", "ズン", "ズン", "ドコ", "キ・ヨ・シ！"]` というリスト（配列）が得られます[^collect]。

## Iteration + Value Type による実装

```jl:zundoko_iter_val.jl
# zundoko_iter_val.jl

immutable ZunDoko end

Base.start(::ZunDoko) = Val{0}
Base.done(::ZunDoko, ::Type{Val{6}}) = true
Base.done{N}(::ZunDoko, ::Type{Val{N}}) = false
Base.next(::ZunDoko, ::Type{Val{5}}) = ("キ・ヨ・シ！", Val{6})
Base.next(::ZunDoko, ::Type{Val{4}}) = rand([("ズン", Val{4}), ("ドコ", Val{5})])
Base.next{N}(::ZunDoko, ::Type{Val{N}}) = rand([("ズン", Val{N+1}), ("ドコ", Val{0})])

function runzundoko()
    for cw in ZunDoko()
        print(cw)
    end
    println()
end

runzundoko()
# => ドコドコズンズンズンドコズンズンドコズンドコドコズンドコズンズンズンズンドコキ・ヨ・シ！
```

[先の例](#iteration-による実装) をさらに発展させて、[Value Type](http://docs.julialang.org/en/release-0.4/manual/types/#value-types)を利用して `if〜` もなくしてみました。
Julia は関数引数の値によるパターンマッチングはサポートしていませんが、`Val{T}` の型パラメータ`T`として渡せるものに限定すれば、このようにパターンマッチング的な書き方もできます。
ただ、`Base.done(::ZunDoko, ::Type{Val{6}})`関数 とか `Base.next(::ZunDoko, ::Type{Val{5}})`関数 なんかは1回しか実行されないのでJITコンパイルによる最適化が有効に働かなくて、パフォーマンス的には今回はあまり効果がないのが残念。

## まとめに変えて

もっと Julia でズンドコキヨシが見てみたい。
できれば、追加パッケージとか使っても良いから、もっとすっきりした実装を。

## 【2016/03/17 20:20 追記】厳密バージョン

元ネタ：
<blockquote class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr">Javaの講義、試験が「自作関数を作り記述しなさい」って問題だったから<br>「ズン」「ドコ」のいずれかをランダムで出力し続けて「ズン」「ズン」「ズン」「ズン」「ドコ」の配列が出たら「キ・ヨ・シ！」って出力した後終了って関数作ったら満点で単位貰ってた</p>&mdash; てくも (@kumiromilk) <a href="https://twitter.com/kumiromilk/status/707437861881180160">March 9, 2016</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

元の文章が「『ズン』『ズン』『ズン』『ズン』『ドコ』が出たら『キ・ヨ・シ！』」だったので、とにかく**4つ（以上）**「ズン」が来てその後に「ドコ」が来たら「キ・ヨ・シ！」で終わり、で良いと思ってました。

つまり先述の私のコードでは、ズン4ドコ1だけでなくズン5ドコ1でもズン100ドコ1でも「キ・ヨ・シ！」発動するようになっています。

でも [ズンドコで学ぶ要件定義(未完)](http://qiita.com/cattaka/items/db5b83d770546d4524e0) を拝見して。

> C：1. 設計ではズン4ドコ1でキヨシーだが、運用でズン5ドコ1でキヨシーとなっている。どちらを正とするべきか
> C：2. ズン4ドコ1を正としたとき、ズン5ドコ1はキヨシー発動条件として認めるか

確かに。**厳密に4つ（だけ）**「ズン」が来てその後に「ドコ」が来たら「キ・ヨ・シ！」というのが元々の要件だった可能性は大いにありますね。

ということでそんな「厳密バージョン」も書いてみました（分かりやすいので Iteration による実装の方だけ）：

```jl:zundoko_iter_strict.jl
# zundoko_iter_strict.jl

immutable ZunDoko end

Base.start(::ZunDoko) = 0
Base.done(::ZunDoko, i::Int) = i == 6
function Base.next(::ZunDoko, i::Int)
    if i == 5
        ("キ・ヨ・シ！", 6)
    elseif i == 4
        rand([("ズン", -1), ("ドコ", 5)])
    elseif i == -1
        rand([("ズン", -1), ("ドコ", 0)])
    else
        rand([("ズン", i + 1), ("ドコ", 0)])
    end
end

function runzundoko()
    for cw in ZunDoko()
        print(cw)
    end
    println()
end

runzundoko()
# => ドコズンズンズンズンズンズンドコズンズンズンズンドコキ・ヨ・シ！
```

はい。簡単ですね。1行修正2行追加しただけです。

私のこの実装方法は、「それまでの『ズン』『ドコ』がどういうパターンで並んでいるか」を判定することはしないで、「次に出力する『ズン』『ドコ』が求められているシーケンスの何番目か」を整数値という「ステータス」で持つようにしたもの。
ついでに初期ステータスを 0 としており、3回以下（および厳密バージョンでは5回以上も）「ズン」が続いた後の「ドコ」もステータス 0 にしているので、「開始がいきなり『ズンズンズンズンドコ』、または『ドコズンズンズンズンドコ』」という場合分けが不要でどちらでも正しく判定出来ます。

もちろん [Iteration + Value Type による実装](#iteration--value-type-%E3%81%AB%E3%82%88%E3%82%8B%E5%AE%9F%E8%A3%85) も、Elixir に翻訳した [ズンドコキヨシ.ex by ストリーム without `Stream`](http://qiita.com/antimon2/items/363c248441147f579ff9) も全く同様の対応で「厳密バージョン」化できますね。

[^collect]: リスト内包表記を用いて `[w for w=ZunDoko()]` のように書くことはできません。`ZunDoko` 型に `length()` 関数へのメソッドが定義されていない（というか結果は不定なので定義出来ない）ためです。