準備体操はRuby。無駄に再帰を使う。
自然数nに対して、3つ前がFizzだったらそれ自身もFizz、 5つ前がBuzzだったらそれもBizz。
#!/usr/bin/env ruby def isfizz(n) case n when 3 'Fizz' when 1,2 '' else isfizz(n-3) end end def isbuzz(n) case n when 5 'Buzz' when 1,2,3,4 '' else isbuzz(n-5) end end def fizzbuzz(n) fb = isfizz(n)+isbuzz(n) fb>'' ? fb : n.to_s end for i in 1..50 printf("%s\n", fizzbuzz(i)) end
まずzshをインストール。
sudo apt install -y zsh
シェルなので手続き実行で戦わせてみる。
1つめのループでは自然数を出力し、出力後行頭にカーソル移動して 待機する。3か5の倍数が来たら出力した自然数を上書きする。
#!/bin/zsh SECONDS=0.8 # 1秒に1ずつ進むシェルのグローバル変数 goal=30 i=1 next1s() { n=$SECONDS while [[ "$n" = "$SECONDS" ]]; do : # 秒数が変わるまで空ループ done } while [[ $i -le $goal ]]; do next1s printf "\n%d\r" $i i=$((i+1)) sleep 0.9 # 次の秒直前まで休む done & # 自然数を出し続けるバックグラウンドジョブ while [[ $i -le $goal ]]; do next1s sleep 0.01 [[ $((i%3)) -eq 0 ]] && printf 'Fizz' sleep 0.9 i=$((i+1)) done & # 3の倍数でバックグラウンド起動 while [[ $i -le $goal ]]; do next1s sleep 0.02 [[ $((i%5)) -eq 0 ]] && printf 'Buzz' sleep 0.9 i=$((i+1)) done # 3と同じく5の倍数でフォアグラウンド起動
1から50までの自然数を以下のようにして作る。
printf "%d\n" {1..50} > 1to50.csv
以下のSQL文をsqlite3に投入する。
sqlite3 fz.sq3 < fizzbuzz.sql
-- For SQLite3 .mode csv DROP TABLE IF EXISTS num; CREATE TABLE num(n INTEGER); .import 1to50.csv num .separator '' SELECT x.f, -- 3で割り切れる場合の 'Fizz' y.b, -- 5で割り切れる場合の 'Buzz' CASE -- 上記いずれも WHEN x.f IS NULL AND y.b IS NULL -- NULLなら THEN x.n -- 自然数自身をSELECT END FROM (SELECT a.n, b.f f FROM num a LEFT JOIN (SELECT n, 'Fizz' f FROM num WHERE n%3 = 0) b ON a.n=b.n) x LEFT JOIN (SELECT n,'Buzz' b FROM num where n%5=0) y ON x.n=y.n;
これは、「3の倍数ならFizz」を出す以下のSQL文から考えると分かりやすい。
sqlite3 fz.sq3
としてから実行してみる。
まず、1から50までの自然数で構成される「左」テーブル a を出す。
SELECT n FROM num; 1 2 3 4 5 6 7 8 9 10 : :
同様に、それが3で割り切れたら 'Fizz' を添えて出す「右」テーブル b を出す。
SELECT n, 'Fizz' AS fz FROM NUM WHERE n%3=0; 3|Fizz 6|Fizz 9|Fizz 12|Fizz 15|Fizz 18|Fizz 21|Fizz 24|Fizz 27|Fizz 30|Fizz 33|Fizz 36|Fizz 39|Fizz 42|Fizz 45|Fizz 48|Fizz
条件に当てはまらない(3で割り切れない)nの行は欠損となる。
a と b のテーブルを外部結合する。
SELECT a.n, fz FROM num a LEFT JOIN (SELECT n, 'Fizz' AS fz FROM num WHERE n%3=0)b ON a.n=b.n; 1| 2| 3|Fizz 4| 5| 6|Fizz 7| 8| 9|Fizz 10| 11| 12|Fizz : : 48|Fizz 49| 50|
fzがNull値の場合のみ、nを選択するようにcoalesce関数でつなぐ。
SELECT coalesce(fz, a.n) FROM num a LEFT JOIN (SELECT n, 'Fizz' AS fz FROM num WHERE n%3=0)b ON a.n=b.n; 1 2 Fizz 4 5 Fizz 7 8 Fizz 10 : : 47 Fizz 49 50
以上の流れを、WITH句を利用して構造が分かりやすくなるよう 書き直したものを示しておく。
.separator '' WITH num AS ( SELECT 1 AS n UNION ALL SELECT n+1 FROM num WHERE n < 50 ), fizz AS ( SELECT n, 'Fizz' AS fz FROM num WHERE n%3=0 ), buzz AS ( SELECT n, 'Buzz' AS bz FROM num WHERE n%5=0 ) SELECT fz, bz, CASE WHEN fz IS NULL AND bz IS NULL THEN a.n END FROM (num a LEFT JOIN fizz b ON a.n=b.n) LEFT JOIN buzz c ON a.n=b.n AND b.n=c.n AND a.n=c.n;
sqlite3 < fz2.sql
とすると実行できる。