今流行りのズンドコキヨシチェックをKotlinで便乗してみたくなったのでせっかくなのでRangeとinfix使って日本語の語順通りにしようと頑張りました
元ネタ
Javaの講義、試験が「自作関数を作り記述しなさい」って問題だったから
— てくも (@kumiromilk) 2016年3月9日
「ズン」「ドコ」のいずれかをランダムで出力し続けて「ズン」「ズン」「ズン」「ズン」「ドコ」の配列が出たら「キ・ヨ・シ!」って出力した後終了って関数作ったら満点で単位貰ってた
ソース
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()
}
長い(確信)英語力(皆無)
実行結果
肝心の呼び出し
("ズン".."ドコ" randomPrint one loop forever ifAppear arrayOf("ズン", "ズン", "ズン", "ズン", "ドコ") print "キ・ヨ・シ!" after loopEnd).writeAndGetUnit()
「ズン」「ドコ」
"ズン".."ドコ"
いずれかをランダムで出力
randomPrint one
し続けて
loop forever
「ズン」「ズン」「ズン」「ズン」「ドコ」の配列が出たら
ifAppear arrayOf("ズン", "ズン", "ズン", "ズン", "ドコ")
「キ・ヨ・シ!」って出力
print "キ・ヨ・シ!"
した後終了
after loopEnd
関数作ったら~~単位貰ってた
.writeAndGetUnit()
残念なとこ
-
細部まで日本語の語順はやっぱり無理だった
-
配列のとこでカッコを使ってしまった
arrayOf("ズン", "ズン", "ズン", "ズン", "ドコ")
文章としてはひとまとまりなとこなのでここではカッコ使いたくなかった -
問題(の配列などの個数)が変われば動かなさそうなとこ
解説
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を遅延実行するようにしておいたのでここで呼び出します(終わり)
さいごに
もっといい実装方法あると思う()