Edited at

ズンドコキヨシをJavaで

More than 3 years have passed since last update.

流行りのズンドコキヨシをJavaで書いてみた。


Streamで書いてみた


2016-03-13追記。 @saka1029 さんのコメントのやり方だと簡潔に書けます。こちらを参考にして下さい。



Zundoko.java

import java.util.stream.Stream;

public class Zundoko {
public static void main(String args[]) {
StringBuilder sb = new StringBuilder();
Stream<String> stream = Stream.generate(() -> Math.random() > 0.5 ? "ズン" : "ドコ");
try {
stream.peek(s -> {
System.out.print(s);
sb.append(s);
if (sb.toString().endsWith("ズンズンズンズンドコ")) {
throw new RuntimeException();
}
}).forEach(s -> {
});
} catch (RuntimeException r) {
System.out.print("キ・ヨ・シ!");
}
}
}


むりやりStreamで書いてみたが、終了の判定が例外処理で美しくない。それに終端処理したいがためだけに空のforEachをしてるし。Eclipse Collections を使えばRuby並に簡潔に行けるかなと思ってちょっと調べたが、そうでもないようだ。(私が知らないだけかもですが)


マルチスレッドで書いてみた


ZundokoThread.java

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

public class ZundokoThread {
public static void main(String args[]) {
ExecutorService zunSer = Executors.newFixedThreadPool(1);
ExecutorService dokoSer = Executors.newFixedThreadPool(1);
ZundokoPool pool = new ZundokoPool(zunSer, dokoSer);

zunSer.submit(new PutTask("ズン", pool));
dokoSer.submit(new PutTask("ドコ", pool));
}
}

class PutTask implements Runnable {
String text;
ZundokoPool pool;

public PutTask(String text, ZundokoPool pool) {
this.text = text;
this.pool = pool;
}

@Override
public void run() {
while (true) {
try {
Thread.sleep((long) Math.random() * 100);
} catch (InterruptedException e) {
break;
}
pool.put(text);
}
}
}

class ZundokoPool {
StringBuilder sb = new StringBuilder();
ExecutorService zunSer;
ExecutorService dokoSer;

public ZundokoPool(ExecutorService zunSer, ExecutorService dokoSer) {
this.zunSer = zunSer;
this.dokoSer = dokoSer;
}

synchronized void put(String str) {
if (Thread.currentThread().isInterrupted()) {
return;
}
sb.append(str);
// System.out.println("debug ->"+sb);
if (sb.toString().endsWith("ズンズンズンズンドコ")) {
System.out.println(sb + "キ・ヨ・シ!");
zunSer.shutdownNow();
dokoSer.shutdownNow();
}
}
}


本当は、Math.random() を使わないで書くという一発芸を狙ってスレッドで書いてみたが、各スレッドの動作頻度をバラけさせるために結局使ってしまった。重要ポイントは、InterruptedExceptionをキャッチしたら breakするところだと思う。これをやらないと、キ・ヨ・シ!を出力した後もスレッドの処理が終わらない。また、これをやってもキ・ヨ・シ!出力後にZundokoPool#put()が呼ばれる場合がある。ロックの管理をもっと丁寧に細かくやれば回避できるかな?

2016-03-16追記。append()する前にisInterrupted()をチェックするようにした。これで、キ・ヨ・シ!出力後はスレッドの処理が終わる確率が上がる。append()の処理途中でインタラプトされたらスレッドの処理は進んでしまうから、可能性が上がるだけ。(でも手元で軽く試した範囲では、うまく言っている様子。動作確認に依らず、原理的に解決するにはどうすればよいだろうか。アトミックな何かが必要?

もっと別の書き方ないかなあ。もっと面白い書き方ないかなあ。セミコロンレスJavaもコードゴルフ決して悪くないが新鮮味に欠ける。(実際に書いたほうが絶対的に偉いんだけど。)