用Java实现ズンドコキヨシ
我用Java尝试写了个时下流行的Zundoko Kiyoshi。
我在Stream上试着写了一下。
2016-03-13 追加记录。请参考@saka1029先生的评论,按照他的方法可以简洁地写。
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一样简洁,于是稍微查了一下,但好像并不是这样。(也许是我不知道的,但似乎不是)
我尝试使用多线程写了一下。
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后要立即中断程序。如果不这样做,那么即使输出了”キ・ヨ・シ!”,线程的处理也不会结束。此外,即使这样做了,仍然可能在输出”キ・ヨ・シ!”之后调用ZundokoPool#put()函数。如果更加细致地管理锁,或许可以避免这种情况的发生。
2016-03-16更新。在添加()之前检查isInterrupted()。这样,很好!在输出后,线程处理结束的概率会增加。如果在append()处理中被中断,线程的处理会继续进行,所以只是增加了可能性(虽然在我小范围的简单测试中,似乎正常运行。不考虑操作确认,理论上如何解决?需要什么原子操作吗?
有没有更多不一样的写法呢?有没有更有趣的写法呢?尽管没有分号的Java和代码高尔夫绝对不错,但却缺乏新鲜感。(实际写一下肯定更厉害,不过。)