読者です 読者をやめる 読者になる 読者になる

【Java・Ruby・Lisp】Fizz-Bazzを解けないとやばいらしい。ので懇切丁寧に解説。解き方。

Common Lisp Java Lisp Ruby

FizzBuzz問題が解けないとやばいらしい。

FizzBuzzというのは、プログラムの問題として非常に有名なもので、一度くらいは解いたことがあるのではないでしょうか。

FizzBuzz問題は、正直そんなに難しいものではなく私の入社後の研修でも序盤の方でやりました。
簡単ではあるのですが、ループ処理、条件分岐と簡単なロジックなど基礎文法が身に付いているか、
プログラムの組み方がわかっているかを知るのに最適だともっぱらの評判です。私もそう思います。

そもそも、FizzBuzz問題は基礎的なスキルを持っているプログラマーかを判定するために考えた問題らしいですね。
つまり、この問題が解けないということはプログラマーではないと。


でも逆に言えば、この問題が解けるようになれば基礎的なロジカルシンキングが身について、
大変勉強になると思うのです。
プログラミングの勉強を始めて、本を一周読んだ人にはぜひこういう問題を解いてみて欲しいです。

なので、文系新卒入社したばかりの私と共にこの問題を解いてみましょう(煽り


真面目な話。

今解けなかったとしても解けるようになればいいんですよ。
基本構文理解していてもアルゴリズムの方は別ですからね。
javaの本まるまる一冊読んで中身理解しても自分の力で組めるようになるには論理的思考力が必要なので。
こういう問題の数をこなしてパターンとして把握しておくと解けるようになるもんです。

目安の解答時間ですが、
一応、ある程度プログラム経験のあるかたは3分とかで組むのがいいみたいですよ。
経験の浅い人なら10分とかですかね。
そんなにかからないかも??

(問題)
1から100までの数字を画面に表示しなさい。
その際、3で割りきれる場合はFizz、5で割りきれる場合はBuzz、
15で割りきれる場合はFizzBuzzと表示しなさい。

(出力例)

1
2
Fizz
4
Buzz
5
.
.
.
98
Fizz
FizzBuzz








シンキングタイム(・∀・)











(解く手順のヒント)

・まず普通に1から100を出力してみる

・3で割り切れる時はFizzと出してみる

・5で割り切れる時のも追加

・15で割り切れる時のも追加

・数字が30とかのときにちゃんとFizzBuzzと出るか確認する










(さらにヒント)

・「わり切れるか」という部分は剰余計算を使います

3で割り切れるというのは、数字から3で割るとあまりが0になるということです。
6 ÷ 3 = 2 余り0
大体のプログラミング言語では%を使うことで剰余を出すことができると思うのでそれを使います。

・15で割り切れる時にはちゃんとFizzBuzz判定にする

if文などで3で割り切れる時の処理を先頭に書いている場合、
おそらく数字が30の時なんかはFizzBuzzが出る前にFizzが出てしまうと思います。
if文の優先順位を見直しましょう。









(解答例と解説)

タイトルにもありますけど、今回JavaRubyLispで解きました。
同じことやってるだけです。あしからず。

javaRubyは初心者がよく手にするものなのでいいんですけど、
Lisp書く人でFizzBuzz解けない人なんているのだろうか(笑)


Java

class FizzBuzz {

private final static int LOOP_TIMES = 100;

  public static void main(String[] args) {
    for (int i = 0; i < LOOP_TIMES; i++) {
      fizzBuzzPrint(i + 1);
    }
  }

  private static void fizzBuzzPrint(int num) {
    if (num % 15 == 0) {
      System.out.println("FizzBuzz");
    } else if (num % 3 == 0) {
      System.out.println("Fizz");
    } else if (num % 5 == 0) {
      System.out.println("Buzz");
    } else {
      System.out.println(num);
    }
  }
}

どこから解説すればいいんだろう(笑)

まず、プログラムを書くときは一度に書き始めないで徐々に作っていったほうがいいです。
最初にシンプルに1から100までを表示するものを作りましょう。

for (int i = 0; i < 100; i++) {
  System.out.println(i + 1);
}

これで1から100まで表示できましたね。
System.out.printlnの中にi + 1とありますが、これをi++のようにインクリメントはしないように気をつけてください。
同じ1を足す処理でも全然ちがいます。
i++はi = i + 1と同じで、1足した数をiに代入します。iの数が変更されてしまっているのでfor文の回数が半分になってしまいます。


さて次はFizzBuzz判定の部分を作りましょう。
違う処理を書くときは関数定義をしたほうがいいです。

private static void fizzBuzzPrint(int num) {
  
}

1から100までの数字をnumに受けて判定するようにします。

最初は3で割り切れる処理を書きます。

private static void fizzBuzzPrint(int num) {
  if (num % 3 == 0) {
    System.out.println("Fizz");
  }  
}

「割り切れる」というのは、余りが0になるということです。
5を3で割ったら余りは2ですが、6だと0ですからね。
剰余の演算子%を使えば、あまりの数がでます。それが0と一致した時に処理を書けばOK

private static void fizzBuzzPrint(int num) {
  if (num % 3 == 0) {
    System.out.println("Fizz");
  } else if (num % 5 == 0) {
    System.out.println("Buzz");
  } else if (num % 15 == 0) {
    System.out.println("FizzBuzz");
  }
}

5の時と15の時の処理も追加しました。
さて、実はこれでは正常に動作しません。なぜでしょうか。

15で割り切れる数30が来た時のことを考えてみましょう。
num に30が来て、最初に判定されるのは if(num % 3 == 0) の部分です。
30は3でわり切れるのはtrueとなってFizzが表示されてしまうのです。
15で割り切れる時はFizzBuzzと表示されなくてはならないので、これではまずいですね。
ifの処理順に気をつけて修正しましょう。

private static void fizzBuzzPrint(int num) {
  if (num % 15 == 0) {
    System.out.println("FizzBuzz");
  } else if (num % 3 == 0) {
    System.out.println("Fizz");
  } else if (num % 5 == 0) {
    System.out.println("Buzz");
  } else {
    System.out.println(num);
  }
}

あとは、どれとも一致しない場合にそのまま数字を表示することを忘れないように。

FizzBuzz判定の関数ができたので、forの中で呼び出します。

class FizzBuzz {

  public static void main(String[] args) {
    for (int i = 0; i < 100; i++) {
      fizzBuzzPrint(i + 1);
    }
  }

  private static void fizzBuzzPrint(int num) {
    if (num % 15 == 0) {
      System.out.println("FizzBuzz");
    } else if (num % 3 == 0) {
      System.out.println("Fizz");
    } else if (num % 5 == 0) {
      System.out.println("Buzz");
    } else {
      System.out.println(num);
    }
  }
}

これで完成!



Ruby

LOOP_RANGE = 1..100

def fizz_buzz_put (num)
  if num % 15 == 0 then
    puts "FizzBuzz"
  elsif num % 3 == 0 then
    puts "Fizz"
  elsif num % 5 == 0 then
    puts "Buzz"
  else
    puts num
  end
end

LOOP_RANGE.each do |i|
  fizz_buzz_put(i)
end

やってることは完全にJavaと同一です。


Common Lisp

(defconstant *loop-times* 100)

(defun fizz-buzz-print (num)
  (fresh-line)
  (cond
    ((= 0 (mod num 15)) (princ '"FizzBuzz"))
    ((= 0 (mod num 3)) (princ '"Fizz"))
    ((= 0 (mod num 5)) (princ '"Buzz"))
    (t (princ num))))

(loop for i below *loop-times* do
  (fizz-buzz-print (1+ i)))

同上。
princ関数は改行処理をしないため、fresh-line関数で改行処理を行っておきます。
print関数の方は改行をして表示するものなのですが、表示の形式がLisp構造なので今回は良くないですね。



以上です。

新しい言語に触れた時とかにもこのFizzBuzz問題はいいと思います。
久しぶりに触る言語の思い出しにも有効ですね。

ではでは。