LoginSignup
1
1

More than 3 years have passed since last update.

自分なりのFizzBuzzの解説。

Last updated at Posted at 2020-06-09

エセコボラーがFizzBuzzを解説してみました。

材料

  • 代数演算子
  • 比較演算子
  • 条件分岐
  • 繰返し制御
  • 順次、逐次
  • 改行コード
  • 数値型変数
  • 変数のパース

PHPでFizzBuzz問題を解いてみる。

問題の解決には、問題を分解することが重要。

これは実生活でも同じ。(知らんけど)

僕が初めて、SES契約で常駐した会社の常務さんは、
「我々の仕事は、因数分解なんやで」 ってプロパーの社員さんに話してた気がする。
もう14年前かな。

でもこの話は時間と共に、大きなインパクトを僕の中に植え付けた。(多分、多分)

脱線を戻そう

問題の分割をやってみよう。

  • 1〜100までの数字を出力
  • 3の倍数はFizzを出力
  • 5の倍数はBuzzを出力
  • 3の倍数と5の倍数の場合FizzBuzzを出力

これら4つが分割結果。

1〜100までの数字を出力しよう。

<?php
for($i=1;$i<=100;$i++){
    echo $i;
}

結果は↓となった。

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100

Try and Error

僕の人生はトライアンドエラーの繰返し。(知らんけど)

トライアンドエラーは、和製英語で、試行錯誤という意味らしい。

英語だと、

Trial and error

脱線を戻そう

さっきの出力した結果には、改行が入ってなかったんだ。

なので、改行を入れてみる。

1〜100までの数字を出力(改行コードを入れてみる)

改行とは。改めて行。人生とは行の連続だ。(多分業のことを言っている)

なんて意味のわからないことを言うのはもう辞めよう。多分無理だけど。

改行コードって[CR]とか[LF]とか[CRLF]とかですね。

リテラル文字に表してみると、"\r" "\n"ですね。

リテラル文字って・・・なんのこっちゃ分からない。

気になる人は 改行コード (wikipedia)をみてください。

脱線を戻そう

<?php
for($i=1;$i<=100;$i++){
    echo $i."\n"; // ←文字列結合で数字と改行文字を合体!
}

すると、1〜100を改行付きで出力しましたとさ。
めでたしめでたし。

1
2
3
4
....省略...

ちなみにMacの日本語キーボードだと。

[option]キー + [¥]キーで、[\n]になるよ。

変数のパース

echo $i."\n";

は、

echo "{$i}\n";

でも良いし、

echo "${i}\n";

でも良いんだ。こんな書き方を変数のパースって呼ぶそう。

書き方は自由。つまり言論の自由。

でも、シングルクォテーションで囲った文字は、変数のパースしてくれないから気をつけよう。

脱線を戻そう

Fizzを出力してみよう。

で、次に、Fizzを出力してみる。

出力する数字を3で割ってみて、余りが0なら、3の倍数ってこと。

PHP的には$i % 3で求めます。

今回 % は 代数演算子 です。

$a % $b → (%は剰余) → つまり $a を $b で割った余り

PHPで表すと以下のようになります。

if( $i % 3  == 0 ){
     // $iを3で割った余りが、0の場合(つまり、3で割り切れた時) ここを通ります。
}

改めて、条件分岐について復習。

条件分岐には ifelse があります。

ifはもしもTrue の場合、 elseFalseの場合に動作します。

if( $foo ) {
     //$fooがTrueの場合ここを通ります。
} else {
    //$fooがFalseの場合ここを通ります。
}

※$fooは例えばの変数です。

TrueかFalseは比較演算子の結果

($i % 3  == 0)
// ↑の計算式の通りなら、True。 
// ↑の計算式の通りにならない場合は、False。

この流れを当てはめてみると。

<?php
for($i=1;$i<=100;$i++){
    if($i % 3 == 0 ){
        // $iを3で割った余りが、0の場合(つまり、3で割り切れた時)
        echo "Fizz\n";
    } else {
        // $iを3で割った余りが、0以外の場合(つまり、3で割り切れない場合)
        echo $i."\n";
    }
}

と。なりました。

このプログラムの出力結果は、以下のようになりました。

1
2
Fizz
4
5
Fizz
7
8
...省略...

それっぽいのができた。

目出度目出度。

これメデタシメデタシって読むんだね。

Buzzを出力。

elseifを使う時が出てきました。

Buzzは5で割り切れる場合に出力します。

phpのマニュアルより、elseifをみてみる。

<?php
if ($a > $b) {
    echo "aはbより大きい";
} elseif ($a == $b) {
    echo "aはbと等しい";
} else {
    echo "aはbより小さい";
}

これをBuzzに当てはめてみましょう。

まずはシンプルに。elseif を追加してみます。

<?php
for($i=1;$i<=100;$i++){
    if($i % 3 == 0 ){
        //$iを3で割った余りが、0の場合(つまり、3で割り切れた時)
        echo "Fizz\n";
    } elseif($i % 5 == 0){
        //$iを5で割った余りが、0の場合(つまり、5で割り切れた時)
        echo "Buzz\n";
    } else {
        echo $i."\n";
    }
}

結果は。

1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
Fizz
...省略...

となりました。

15 の場合に、FizzBuzzと出力したいですが、このままだとFizzの出力となります。

この問題を解消するため、単純に、elseif を加えてみます。

<?php
for($i=1;$i<=100;$i++){
    if( $i % 3 == 0 ){
        //$iを3で割った余りが、0の場合(つまり、3で割り切れた時)
        echo "Fizz\n";
    } elseif ( $i % 5 == 0 ){
        //$iを5で割った余りが、0の場合(つまり、5で割り切れた時)
        echo "Buzz\n";
    } elseif ( $i % 3 == 0 && $i % 5 == 0 ){
        //$iを3で割った余りが、0かつ、
        //$iを5で割った余りが、0の場合
        echo "FizzBuzz\n";
    } else {
        echo $i."\n";
    }
}

あy .... 過ち。

このプログラムには間違いがあります。 15の時、Fizzしか出力されません。

3の余りが0かつ、5の余りが0の場合、

は、

必ず、3の余りが0の条件にマッチする

なので。

3の倍数の場合で5の倍数の場合、一番上の echo "Fizz\n"; の部分が動いてしまうの。

FizzBuzzを出力したければ、3の余りが0の条件分岐より、先に、

3の余りが0かつ、5の余りが0 かどうかを判定する必要があります。

結果このようなプログラムに修正されました。


<?php
for($i=1;$i<=100;$i++){
    if ( $i % 3 == 0 && $i % 5 == 0 ){
        //$iを3で割った余りが、0かつ、
        //$iを5で割った余りが、0の場合
        echo "FizzBuzz\n";
    } elseif ( $i % 5 == 0 ){
        //$iを5で割った余りが、0の場合(つまり、5で割り切れた時)
        echo "Buzz\n";
    } elseif( $i % 3 == 0 ){
        //$iを3で割った余りが、0の場合(つまり、3で割り切れた時)
        echo "Fizz\n";
    } else {
        echo $i."\n";
    }
}

結果、うまくいきました。

1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
...省略...

改善案

少し改善する方法を考えてみます。

if ( $i % 3 == 0 && $i % 5 == 0 ){

この部分は、もしかすると、

if ( $i % 15 == 0 ){

に置き換えれるかもしれないな。とか。

その考えを当てはめてみました。


<?php
for($i=1;$i<=100;$i++){
    if ( $i % 15 == 0 ){
        //$iを3で割った余りが、0かつ、
        //$iを5で割った余りが、0の場合
        echo "FizzBuzz\n";
    } elseif ( $i % 5 == 0 ){
        //$iを5で割った余りが、0の場合(つまり、5で割り切れた時)
        echo "Buzz\n";
    } elseif( $i % 3 == 0 ){
        //$iを3で割った余りが、0の場合(つまり、3で割り切れた時)
        echo "Fizz\n";
    } else {
        echo $i."\n";
    }
}

関数を使ってみよう。 (オマケ)

FizzBuzzの出力を関数にしてみよう。

関数って、↓↓↓こんなやつ。

function FizzBuzzEcho($num){
    echo "${num}\n";
}

関数のイメージはこんな感じでやってみる。

関数は、引数が合って、戻り値が合って、って色々ありそうだけど、大事なことは 処理を1つにまとめた部品にしてしまうこと。

今回作る関数は、FizzBuzzEcho関数。

やることは、

  • 渡した数字が15の倍数の場合はFizzBuzzを出力、
  • 渡した数字が3の倍数の場合はFizzを出力、
  • 渡した数字が5の倍数の場合はBuzzを出力、
  • それ以外は、渡した数字を出力する関数。

関数の使い方は簡単。


FizzBuzzEcho(1);
FizzBuzzEcho(3);
FizzBuzzEcho(5);
FizzBuzzEcho(15);

こんな感じで使えます。

結果は、

1
3
5
15

関数を正しい結果になるよう修正してみよう。


<?php

function FizzBuzzEcho($num){
    if ( $num % 15 == 0 ){
        //$iを3で割った余りが、0かつ、
        //$iを5で割った余りが、0の場合
        echo "FizzBuzz\n";
    } elseif ( $num % 5 == 0 ){
        //$iを5で割った余りが、0の場合(つまり、5で割り切れた時)
        echo "Buzz\n";
    } elseif( $num % 3 == 0 ){
        //$iを3で割った余りが、0の場合(つまり、3で割り切れた時)
        echo "Fizz\n";
    } else {
        echo $num."\n";
    }
}

FizzBuzzEcho(1);
FizzBuzzEcho(3);
FizzBuzzEcho(5);
FizzBuzzEcho(15);

結果は。

1
Fizz
Buzz
FizzBuzz

が出力された。問題ないですね。

繰り返しの中で、関数を呼び出してみよう。

こんな感じで呼び出す。

<?php 
for($i=1;$i<=100;$i++){
    FizzBuzzEcho($i);
}

でできたプログラムは↓こんな感じ。

<?php
function FizzBuzzEcho($num){
    if ( $num % 15 == 0 ){
        //$iを3で割った余りが、0かつ、
        //$iを5で割った余りが、0の場合
        echo "FizzBuzz\n";
    } elseif ( $num % 5 == 0 ){
        //$iを5で割った余りが、0の場合(つまり、5で割り切れた時)
        echo "Buzz\n";
    } elseif( $num % 3 == 0 ){
        //$iを3で割った余りが、0の場合(つまり、3で割り切れた時)
        echo "Fizz\n";
    } else {
        echo "${num}\n";
    }
}

for($i=1;$i<=100;$i++){
    FizzBuzzEcho($i);
}

動かした結果は、↓となった。

1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
...省略...

こんな感じ。です。

関数の大事なことは、役割を明確にすること。

名前を丁寧につけること。

名前を丁寧につけて、役割を明確にすると、

やりたいことが伝わりやすくなります。

for($i=1;$i<=100;$i++){
    FizzBuzzEcho($i);
}

そして、役割を明確にして、名前を丁寧につけることは、構造化プログラミングの一歩。

参考: 構造化プログラミング (wikipedia)

以上。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1