ズンドコキヨシ.jl with Iteration (追記あり)

  • 2
    Like
  • 0
    Comment
More than 1 year has passed since last update.

@chezou さんの ズンドコキヨシ.jl を見て、私もズンドコキヨシJulia で書いてみたくなった。

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

※【2016/03/17 20:20】 厳密バージョンについて追記しました。

Iteration による実装

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 については、私の過去記事等参照)
列挙そのものはこの Iteration の仕組みに依っているので、出力するのではなくリストで取得したければ、例えば
collect(UTF8String, ZunDoko())
のようにすれば ["ズン", "ズン", "ドコ", "ズン", "ドコ", "ドコ", "ズン", "ドコ", "ドコ", "ズン", "ズン", "ズン", "ズン", "ドコ", "キ・ヨ・シ!"] というリスト(配列)が得られます1

Iteration + Value Type による実装

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()
# => ドコドコズンズンズンドコズンズンドコズンドコドコズンドコズンズンズンズンドコキ・ヨ・シ!

先の例 をさらに発展させて、Value Typeを利用して 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 追記】厳密バージョン

元ネタ:


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

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

でも ズンドコで学ぶ要件定義(未完) を拝見して。

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

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

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

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 による実装 も、Elixir に翻訳した ズンドコキヨシ.ex by ストリーム without Stream も全く同様の対応で「厳密バージョン」化できますね。


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