Rubyで作る奇妙なプログラミング言語 ~Esoteric Language~にて紹介した本の中から、「HQ9+」言語をJavaで実装してみます。
「HQ9+」言語とは、
です。
というわけで早速実装してみます。
「HQ9+」言語とは、
- Hコマンドは文字列"Hello, world!"を出力する。
- Qコマンドはプログラムのソースコードを出力する。
- 9コマンドは『99 Bottles of Beer』(アメリカの数え歌で、プログラミングの例題でよく利用される)の歌詞を出力する。
- +コマンドはアキュムレータをインクリメント(1だけ増やす)する。
です。
というわけで早速実装してみます。
まずは本のRubyソースをほぼそのまま移植。
package st.happy_camper.esoteric.hq9plus;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* @author ueshin
*/
public class HQ9Plus {
private final String src;
private int count = 0;
public HQ9Plus(String src) {
this.src = src;
}
public void run() {
for(char c : src.toCharArray()) {
if(c == 'H') {
hello();
}
else if(c == 'Q') {
printSource();
}
else if(c == '9') {
print99BottlesOfBeer();
}
else if(c == '+') {
increment();
}
}
}
private void hello() {
System.out.println("Hello World!");
}
private void printSource() {
System.out.println(src);
}
private void print99BottlesOfBeer() {
for(int k = 99; k >= 0; k--) {
String before;
String after;
switch(k) {
case 0:
before = "No more bottles";
after = "99 bottles";
break;
case 1:
before = "1 bottle";
after = "no more bottles";
break;
case 2:
before = "2 bottles";
after = "1 bottle";
break;
default:
before = k + " bottles";
after = (k - 1) + " bottles";
}
String action;
if(k == 0) {
action = "Go to the storea and buy some more";
}
else {
action = "Take one down and pass it around";
}
System.out.println(before + " of beer on the wall, "
+ before.toLowerCase() + " of beer.");
System.out.println(action + ", " + after + " of beer on the wall.");
if(k != 0) {
System.out.println();
}
}
}
private void increment() {
count++;
}
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
BufferedReader reader = null;
try {
if(args.length > 0) {
reader = new BufferedReader(new FileReader(args[0]));
}
else {
reader = new BufferedReader(new InputStreamReader(System.in));
}
StringBuilder src = new StringBuilder();
String line;
while((line = reader.readLine()) != null) {
src.append(line);
src.append("\n");
}
new HQ9Plus(src.toString()).run();
}
finally {
if(reader != null) {
reader.close();
reader = null;
}
}
}
}
使い方
$ echo 'HQ' | java st.happy_camper.esoteric.hq9plus.HQ9Plus
Hello World!
HQ
もう少しJavaっぽく?
package st.happy_camper.esoteric.hq9plus;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
/**
* @author ueshin
*/
public class HQ9PlusPlus {
public interface CommandProcessor {
public void process();
}
private final String src;
private final Map<Character, CommandProcessor> processors = new HashMap<Character, CommandProcessor>();
/**
* @param src
*/
public HQ9PlusPlus(String src) {
this.src = src;
processors.put('H', new HelloWorldProcessor());
processors.put('Q', new QuineProcessor(src));
processors.put('9', new NinetyNineBottlesOfBeerProcessor());
processors.put('+', new IncrementProcessor());
}
/**
* @param src
* @param processors
*/
public HQ9PlusPlus(String src,
Map<Character, ? extends CommandProcessor> processors) {
this(src);
this.processors.putAll(processors);
}
/**
*
*/
public void run() {
for(char c : src.toCharArray()) {
CommandProcessor processor = processors.get(c);
if(processor != null) {
processor.process();
}
}
}
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
BufferedReader reader = null;
try {
if(args.length > 0) {
reader = new BufferedReader(new FileReader(args[0]));
}
else {
reader = new BufferedReader(new InputStreamReader(System.in));
}
StringBuilder src = new StringBuilder();
String line;
while((line = reader.readLine()) != null) {
src.append(line);
src.append("\n");
}
new HQ9PlusPlus(src.toString()).run();
}
finally {
if(reader != null) {
try {
reader.close();
reader = null;
}
catch(IOException e) {
}
}
}
}
/**
* @author ueshin
*/
private static class HelloWorldProcessor implements CommandProcessor {
@Override
public void process() {
System.out.println("Hello World!");
}
}
/**
* @author ueshin
*/
private static class QuineProcessor implements CommandProcessor {
private final String src;
/**
* @param src
*/
public QuineProcessor(String src) {
this.src = src;
}
@Override
public void process() {
System.out.print(src);
}
}
/**
* @author ueshin
*/
private static class NinetyNineBottlesOfBeerProcessor implements
CommandProcessor {
@Override
public void process() {
for(int k = 99; k >= 0; k--) {
String before;
String after;
switch(k) {
case 0:
before = "No more bottles";
after = "99 bottles";
break;
case 1:
before = "1 bottle";
after = "no more bottles";
break;
case 2:
before = "2 bottles";
after = "1 bottle";
break;
default:
before = k + " bottles";
after = (k - 1) + " bottles";
break;
}
String action;
if(k == 0) {
action = "Go to the storea and buy some more";
}
else {
action = "Take one down and pass it around";
}
System.out.println(before + " of beer on the wall, "
+ before.toLowerCase() + " of beer.");
System.out.println(action + ", " + after
+ " of beer on the wall.");
if(k != 0) {
System.out.println();
}
}
}
}
/**
* @author ueshin
*/
private static class IncrementProcessor implements CommandProcessor {
private int counter = 0;
@Override
public void process() {
counter++;
}
}
}
各コマンドをそれぞれのクラスで処理させるようにしました。
これにより拡張可能に。
CommandProcessor
インターフェイスを実装したクラスをコマンドとのマップに収めて、コンストラクタに渡してあげればそのコマンドも受け付けるようになります。また、デフォルトのコマンドも上書きするので、処理の変更をすることも出来ます。