会社の先輩エンジニアに、「剰余演算」なしでFizzBuzz書いて!
って何気なく言われたので書いてみました。
ルールのおさらい
- 入力値は0以上の正の整数
- 以下のルールで、結果を文字列として出力。
上から順に優先度が高いルール- 入力値が15で割り切れる場合は[FizzBuzz]を出力
- 入力値が 5で割り切れる場合は[Buzz]を出力
- 入力値が 3で割り切れる場合は[Fizz]を出力
- どれにも当てはまらない場合、入力値をそのまま出力
今回はこの基本ルールに、以下のルールを追加します。
・割った余りを求める演算子「%(剰余演算)」を使わない
実装一覧
剰余演算を使った普通の実装
特に言うことはない、いたってシンプルなやつ。
多少関数化したりできますが、概ねこの形になると思います。
テスト用としてListに結果を追加しています。
ふつうのFizzBuzzですね。
public class FizzBuzz { public static void main(String[] args) { System.out.println(doFizzBuzz(100)); } public static List<String> doFizzBuzz(int fizzBuzz) { List<String> retList = new ArrayList<>(); for (int i = 1; i < fizzBuzz + 1; i++) { if (i % 3 == 0 && i % 5 == 0) { retList.add("FizzBuzz"); } else if (i % 5 == 0) { retList.add("Buzz"); } else if (i % 3 == 0) { retList.add("Fizz"); } else { retList.add(String.valueOf(i)); } } return retList; } }
三項演算子で実装
三項演算子でtrue/falseの結果によって文字列を返す形。
シンプルですが、for文のindex開始が1なのでちょっと違和感ありますね。
public class FizzBuzzDevide { public static void main(String[] args) { System.out.println(doFizzBuzz(100)); } public static List<String> doFizzBuzz(int execNum) { List<String> retList = new ArrayList<>(); for (int i = 1; i < execNum + 1; i++) { String result = fizzBuzz(i); retList.add(result); } return retList; } private static String fizzBuzz(int i) { return isMatch(i, 15) ? "FizzBuzz" : isMatch(i, 5) ? "Buzz" : isMatch(i, 3) ? "Fizz" : String.valueOf(i); } private static boolean isMatch(int index, int fizzbuzzTerm) { return (index / fizzbuzzTerm) * fizzbuzzTerm == index; } }
再帰で実装
最初に配列を用意して、値を取り出していく実装。
用意した配列要素が16なので、繰り返し処理で配列を超えた要素を取得しようとするとArrayIndexOutOfBoundsExceptionが発生するのを期待して再帰します。
さらに、取得時に値が存在するかどうかで出力値を変えています。
仕事としてでこういう実装書いたら、まずレビュー通らないですね。
例外は例外として処理すべきです。
public class FizzBuzzRecursive { private static String[] _FizzBuzz = new String[16]; static { _FizzBuzz[3] = "Fizz"; _FizzBuzz[6] = "Fizz"; _FizzBuzz[9] = "Fizz"; _FizzBuzz[12] = "Fizz"; _FizzBuzz[5] = "Buzz"; _FizzBuzz[10] = "Buzz"; _FizzBuzz[15] = "FizzBuzz"; } public static void main(String[] args) { System.out.println(doFizzBuzz(100)); } public static List<String> doFizzBuzz(int limit) { List<String> retList = new ArrayList<>(); for (int i = 1; i < limit + 1; i++) { String result = fizzBuzzRecursive(i, i); retList.add(result); } return retList; } private static String fizzBuzzRecursive(int origin, int convert) { String fizzbuzz = null; try { fizzbuzz = getFizzBuzzElement(convert, origin); } catch (ArrayIndexOutOfBoundsException e) { // 配列要素を超えた場合、15で引いて再帰する事で16以上の数に対応する fizzbuzz = fizzBuzzRecursive(origin, convert - 15); } return fizzbuzz; } private static String getFizzBuzzElement(int convert, int origin) { try { Objects.requireNonNull(_FizzBuzz[convert]); return _FizzBuzz[convert]; } catch (NullPointerException e) { // 値がnullの場合はFizzBuzzの条件に合わないので数値を返却 return String.valueOf(origin); } } }
一行で実装
三項演算子の応用版。
Stream中に数値を文字列に変換する所は、参考演算子と同じ判定ロジックです。
テストができないので、できれば値を返却する関数を用意したほうが良いですが
「1行」というコンセプトに反するので辞めました。
フォーマッターかけるとたぶん複数行になってしまいます。あしからず。
public class FizzBuzzOneLiner { public static void main(String[] args) { Stream.iterate(1, i -> ++i).limit(100).map(i -> (i/15*15==i ? "FizzBuzz" : i/5*5==i ? "Buzz" : i/3*3==i ? "Fizz" : i)).forEach(System.out::println);; } }
その他、すごいやつ
for文やwhile文、if文などを禁止しているさらに厳しい条件。
後半異常な出来具合です。発想力が凄い