↑の記事にある Sleep FizzBuzz を Kotlin Coroutines でやってみた。
単純版
素直にやるとこんな感じ。
fun main() = runBlocking<Unit> {
launch {
for (i in 1..Int.MAX_VALUE) {
delay(1_000)
print("\n$i\r")
}
}
launch {
delay(100)
while (true) {
delay(3_000)
print("Fizz\u001b[K")
}
}
launch {
delay(200)
while (true) {
delay(5_000)
print("Buzz\u001b[K")
}
}
}
(元記事の方法とは少し変えて、
出力され始めるまで1秒待つが
その分読みやすいコードにした。)
結果。
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
うまくいっているように見える。
しかし 100 くらいまで来るともうグダグダ。
FizzBuzz
90
91
Fizz
93
Buzz
Fizz
96
97
Fizz
Buzz
100
print
関数の呼び出し、その引数とする文字列の構築、それにスレッドの切り替えなどに時間が掛かって、
誤差が出てくるのだろう。
SharedFlow
で Ticker を作る
お題の趣旨からは少し離れてしまうが、
SharedFlow
を使っていわゆる Ticker を作り、
ズレが出ないようにしてみた。
fun main() = runBlocking<Unit> {
val ticker =
flow {
for (sec in 1..Int.MAX_VALUE) {
emit(sec)
delay(1_000)
}
}.shareIn(this, SharingStarted.Lazily)
ticker
.onEach {
print("\n$it\r")
}.launchIn(this)
ticker
.filter { it % 3 == 0 }
.onEach {
delay(1)
print("Fizz\u001b[K")
}.launchIn(this)
ticker
.filter { it % 5 == 0 }
.onEach {
delay(2)
print("Buzz\u001b[K");
}.launchIn(this)
}
これはうまくいった。
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
1000付近でも大丈夫。
FizzBuzz
991
992
Fizz
994
Buzz
Fizz
997
998
Fizz
Buzz
【Kotlin/JVM】 ScheduledThreadPoolExecutor.scheduleAtFixedRate
を使う
Java の ScheduledThreadPoolExecutor.scheduleAtFixedRate
を使うと次のようになる。
fun main() = runBlocking<Unit> {
var i = 1
ScheduledThreadPoolExecutor(1)
.scheduleAtFixedRate(
{ print("\n${i++}\r") },
1_000 + 0,
1_000,
TimeUnit.MILLISECONDS,
)
ScheduledThreadPoolExecutor(1)
.scheduleAtFixedRate(
{ print("Fizz\u001b[K") },
3_000 + 1,
3_000,
TimeUnit.MILLISECONDS,
)
ScheduledThreadPoolExecutor(1)
.scheduleAtFixedRate(
{ print("Buzz\u001b[K") },
5_000 + 2,
5_000,
TimeUnit.MILLISECONDS,
)
}
これはお題の趣旨にも合っているし、
正しい結果が得られる。
(Kotlin らしさはほとんどないが。)
/以上