Kotlinで流行りのズンドコキヨシを日本語順通りに作りたかった

  • 3
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

今流行りのズンドコキヨシチェックをKotlinで便乗してみたくなったのでせっかくなのでRangeとinfix使って日本語の語順通りにしようと頑張りました

元ネタ

ソース

import java.util.*

class ZunDoko(val ite: () -> Iterator<String>) : Iterable<String> {
    override fun iterator(): Iterator<String> {
        return ite()
    }

    // ランダムでcount個の要素を出力しながら次の要素に行くIterable
    fun randomPrint(count: Int): ZunDoko {
        val iterator = {
            object : Iterator<String> {
                var cache: MutableList<String>? = null
                var count = 0

                override fun hasNext() = count > this.count

                override fun next(): String {
                    this.count++
                    if (cache == null) {
                        cache = this@ZunDoko.toMutableList()
                        while (count < cache!!.size) {
                            cache!!.removeAt(random.nextInt(cache!!.size))
                        }
                    }
                    val element = cache!![0].apply { cache!!.removeAt(0) }
                    println(element)
                    if (cache!!.isEmpty()) cache = null
                    return element
                }

            }
        }
        return ZunDoko(iterator)
    }

    // count回ループするIterable
    // countが-1なら無限ループ
    fun loop(count: Int): ZunDoko {
        val iterator = {
            object : Iterator<String> {
                var isColoopEnnded = false
                var cache: MutableList<String>? = null
                var count = 0

                override fun hasNext() = count == forever || count > this.count || isColoopEnnded.not()

                override fun next(): String {
                    isColoopEnnded = false
                    if (cache == null) {
                        cache = this@ZunDoko.toMutableList()
                    }
                    require(cache!!.size > 0)
                    val element = cache!![0].apply { cache!!.removeAt(0) }
                    if (cache!!.isEmpty()) {
                        cache = null
                        isColoopEnnded = true
                        this.count++
                    }
                    return element
                }

            }
        }
        return ZunDoko(iterator)
    }

    // String配列が存在したときにイベント実行するListenerを返す
    fun ifAppear(words: Array<String>): Listener {
        return object : Listener {
            var event: () -> Boolean = { false }

            override fun call() {
                val iterator = iterator()
                var matchCount = 0
                //イテレーターがある限り確認する
                while (iterator.hasNext()) {
                    val element = iterator.next()
                    if (element == words[matchCount]) {
                        matchCount++
                    } else {
                        matchCount = 0
                    }
                    if (matchCount >= words.size) {
                        // eventの返り値がtrueなら処理終了
                        if (event()) {
                            break
                        }
                    }
                }
            }

            override fun addEvent(event: () -> Boolean) {
                val e = this.event
                this.event = {
                    e() || event()
                }
            }
        }
    }

    companion object {
        val random = Random()
    }
}

interface Listener {
    fun addEvent(event: () -> Boolean)

    fun call()
}

operator fun String.rangeTo(that: String) = ZunDoko({ arrayListOf(this, that).iterator() })

infix fun ZunDoko.loop(count: Int) = loop(count)

infix fun ZunDoko.randomPrint(count: Int) = randomPrint(count)

infix fun ZunDoko.ifAppear(words: Array<String>): Listener = ifAppear(words)

infix fun Listener.print(word: String) = this.after {
    println(word)
    false
}

infix fun Listener.after(event: () -> Boolean) = this.apply {
    addEvent(event)
}

// Listenerを呼び出す
fun Listener.writeAndGetUnit() {
    call()
}

// 演算子の左右には値がないといけないので値として宣言しておく
val loopEnd: () -> Boolean = { true }
val one = 1
val forever = -1

fun main(args: Array<String>) {
    ("ズン".."ドコ" randomPrint one loop forever ifAppear arrayOf("ズン", "ズン", "ズン", "ズン", "ドコ") print "キ・ヨ・シ!" after loopEnd).writeAndGetUnit()
}

長い(確信)英語力(皆無)

実行結果

zundoko.PNG

肝心の呼び出し

("ズン".."ドコ" randomPrint one loop forever ifAppear arrayOf("ズン", "ズン", "ズン", "ズン", "ドコ") print "キ・ヨ・シ!" after loopEnd).writeAndGetUnit()

「ズン」「ドコ」
"ズン".."ドコ"

いずれかをランダムで出力
randomPrint one

し続けて
loop forever

「ズン」「ズン」「ズン」「ズン」「ドコ」の配列が出たら
ifAppear arrayOf("ズン", "ズン", "ズン", "ズン", "ドコ")

「キ・ヨ・シ!」って出力
print "キ・ヨ・シ!"

した後終了
after loopEnd

関数作ったら~~単位貰ってた
.writeAndGetUnit()

残念なとこ

  1. 細部まで日本語の語順はやっぱり無理だった

  2. 配列のとこでカッコを使ってしまった
    arrayOf("ズン", "ズン", "ズン", "ズン", "ドコ")
    文章としてはひとまとまりなとこなのでここではカッコ使いたくなかった

  3. 問題(の配列などの個数)が変われば動かなさそうなとこ

解説

Rangeとinfix処女ズンドコキヨシさんに奪われたのでRangeとinfixは初めて自作したので詳しい解説はできないです

"ズン".."ドコ"

Rangeです、よくfor(i in 1..10)とかするやつです

operator fun String.rangeTo(that: String) = ZunDoko({ arrayListOf(this, that).iterator() })

ここで定義してます、rangeToは決まり文句みたいな感じっぽいです

randomPrint one

前述のとこでZunDokoクラスが返ってくるのでこれに拡張関数を定義し演算子のように使えるようにinfixキーワードつけてます

infix fun ZunDoko.randomPrint(count: Int) = randomPrint(count)

演算子のように真ん中に関数名がくるので右辺にくるInt型はそれっぽく見えるように他で定義しときます(逃げ)
randomPrint(count)はZunDokoクラスで定義してるものです、拡張関数なのでthisで参照できthisを省略してます

loop forever

むげんるーぷしたいのでふらぐを-1にした

ifAppear arrayOf("ズン", "ズン", "ズン", "ズン", "ドコ")

infix fun ZunDoko.ifAppear(words: Array<String>): Listener = ifAppear(words)

とりあえず左辺は決まってる、右辺は配列にしたい…じゃあ右辺どうするか?arrayOfしかないじゃない!(涙)
返り値のListenerですがこの後続の関数で処理を追加したい&それを追加したうえで最後に処理を実行する必要があるので作ることにしました(いわゆる遅延実行ってやつかな)

print "キ・ヨ・シ!" after loopEnd

前述のとこでListenerが返ってくるのでそれに合わせて定義

infix fun Listener.print(word: String) = this.after {
    println(word)
    false
}

infix fun Listener.after(event: () -> Boolean) = this.apply {
    addEvent(event)
}

after関数の引数がラムダである必要があるので

val loopEnd: () -> Boolean = { true }

と定義します(逃げ)

.writeAndGetUnit()

文章の最後で右辺がないのでinfixつけない拡張関数にします

fun Listener.writeAndGetUnit() {
    call()
}

Listenerを遅延実行するようにしておいたのでここで呼び出します(終わり)

さいごに

もっといい実装方法あると思う()