#概要
Javaでアルファベット1種禁止(a~zのうち1種類を使わずに書く)でどのアルファベットが抜いても書けるかを検証した。ただの遊び。以前の記事のFizzBuzz版。
以前は文字列定数("Hello,world")の回避は簡単だったので、「class宣言」「main宣言」「出力文」での回避が主だった。
今回も全て存在するので以前縛れなかったアルファベット( acdegimnorstv)は縛れない。
なので前回縛れた「bfhjklpquwxyz」についてFizzBuzzでも縛れるかを検証することになる
#ルール
- java10
- コンパイルはjavac, 実行はjavaでオプションなし(というかEclipseで書いて実行)
- 標準出力にFizzBuzzを出力する
- 数字1つにつき1行
- 文字は「Fizz」「Buzz」「FizzBuzz」
- FizzBuzzzの回数は実行時は20回だが実装上はint型最大値の半分あたりまでできるように
- ループや条件分岐するところを埋め込みで解決しない
- 余計な標準出力、エラー出力は禁止(改行はOK)
- アルファベットを1種類だけ使用禁止
- 大文字小文字は区別なし
- 外部ファイル禁止(Java標準ライブラリのみ可能)
- ユニコードエスケープ禁止
4番の縛りの理由は、アルファベット回避のためにbyteなどを使ってFizzBuzzの上限がさがるもなんかなぁ〜って思って一般的な数値型のintの範囲が可能にしとこうと思っていれた。(つまりlongはOK)
※2018/12/19追記 コメントでINT型最大値でオーバーフローして終わらないことを教えてもらった.
インデックスずらせば解決するけどウィキペディアのコードと違っちゃうし縛りの本質ではないのでINT型最大値の半分に変えてごまかした.
5番の縛りの理由は、埋め込み有りなら極端なループ使わずにそのまま全部出力を埋め込めば内容は前回のHello,worldとの違いが無くなる。どこまでが埋め込みかの判断は難しいがまあそのあたりは自己判断で。
判断が気に入らなかったら自分の判断基準でやってみた記事書いて。
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
10番の縛りについては以前の記事で
#結論
可能:bfhjklpquwxyz
不可能:acdegimnorstv
※前回と何も変わらない
#詳細
大体アルファベット順に縛ったコード書いてる。前回使ったテクニックはあんまり説明してない。
##hjkqwx
まずは普通にFizzBuzzを書いた。普通にと言ってもやり方はいろいろあるのでウィキペディアにあるpythonのコードをそのままjavaに書き換えた。
例のごとく変数名等はアルファベットを使わないように日本語で書いている。
class フィズバズ {
public static void main(String[] 引数) {
for (int 数=1;数 <= 20;数++) {
if (数 % 15 == 0)
System.out.println("FizzBuzz");
else if (数 % 3 == 0)
System.out.println("Fizz");
else if (数 % 5 == 0)
System.out.println("Buzz");
else
System.out.println(数);
}
}
}
##bpz
まずは文字列をchar型で書いてformatで起こすことで消した。これでzが消せる。
次にinterfaceにしてmainメソッドのpublicを消す。これでbが消える。
思ったよりも消えなかった。
interface フィズバズ {
static void main(String[] 引数) {
for (int 数=1;数 <= 20;数++) {
if (数 % 15 == 0)
System.out.format("%c%c%c%c%c%c%c%c%c",70,105,122,122,66,117,122,122,10);
else if (数 % 3 == 0)
System.out.format("%c%c%c%c%c",70,105,122,122,10);
else if (数 % 5 == 0)
System.out.format("%c%c%c%c%c",66,117,122,122,10);
else
System.out.format("%d%c",数,10);
}
}
}
##f
forとifとformatで使ってる。formatはprintに変えてchar配列に置き換えた。
forはwhileに。ifはswitchをif文みたいに使った。elseの代わりにcontinueを使っているのでインクリメントを書いてる。別に初期値変えて
class フィズバズ {
public static void main(String[] 引数) {
int 数=0;
while (数 < 20) {
数++;
switch(数 % 15){
case 0:
System.out.print(new char[] {70,105,122,122,66,117,122,122,10});
continue;
}
switch(数 % 3){
case 0:
System.out.print(new char[] {70,105,122,122,10});
continue;
}
switch(数 % 5){
case 0:
System.out.print(new char[] {66,117,122,122,10});
continue;
}
System.out.print(数+"\n");
}
}
}
##l
elseとprintlnで使ってた。一つ上のはそれらは使っていないがwhileで使ってしまってるのでそれをforに変えればいける。
publicでも使ってるのでinterfaceに変える。
interface フィズバズ {
static void main(String[] 引数) {
for(int 数=1;数 <= 20;数++) {
switch(数 % 15){
case 0:
System.out.print(new char[] {70,105,122,122,66,117,122,122,10});
continue;
}
switch(数 % 3){
case 0:
System.out.print(new char[] {70,105,122,122,10});
continue;
}
switch(数 % 5){
case 0:
System.out.print(new char[] {66,117,122,122,10});
continue;
}
System.out.print(数+"\n");
}
}
}
##uy
前回と同じScriptEngine使ってs[y]stem.o[u]tを置き換える方法。B[u]zzにもあるので文字列はchar配列にしてる。
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
interface フィズバズ {
static void main(String[] 引数) throws Exception{
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = (ScriptEngine)manager.getClass()
.getMethod("getEngineB"+(char)121+"Name", String.class).invoke(manager, "javascript");
for (int 数=1;数 <= 20;数++) {
if (数 % 15 == 0)
engine.eval("print('"+new String(new char[] {70,105,122,122,66,117,122,122})+"')");
else if (数 % 3 == 0)
engine.eval("print('"+new String(new char[] {70,105,122,122})+"')");
else if (数 % 5 == 0)
engine.eval("print('"+new String(new char[] {66,117,122,122})+"')");
else
engine.eval("print('"+数+"')");
}
}
}
#縛れない文字と理由
##acdgimnorstv
static void main(String[] 引数)が絶対必要なため。
##e
uyと同じくSyst[e]m.outに入っているし、Script[E]ngin[e]にも入ってる。
文字列から取得しようにもg[e]tClassに入ってる。
そうでなくてもかなり使われやすいアルファベットなので大体引っかかる。
#終わりに
以上より
可能:bfhjklpquwxyz
不可能:acdegimnorstv
となる。結果は前回と同じだった。そんなに面白くなかった。やっぱclassとかmainの宣言の部分は無視するべきなのかな。
と考えたけどそれならpythonとかでやったほうがいいかなぁって。