CodeIQ:FizzBuzz?やつは四天王の中でも最弱-CodeIQ四天王

私自身が表題の問題を解いた時のプログラムについて解説します。
問題の詳細は「FizzBuzz?やつは四天王の中でも最弱-CodeIQ四天王」(CodeIQ)を参照してください。

問題の概要

標準入力から「key,value」が与えられます。
keyはHelloWorld、FizzBuzz、Prime、Fibonacciの4種類です。
valueは1〜15の整数です。

HelloWorldの場合はvalue回連続してHelloWorldを出力してください。
FizuuBuzzの場合はValueが3の倍数ならFizz、5の倍数ならBuzz、15の倍数ならFizzBuzz、それ以外ならvalueの値を出力してください。
Primeの場合、Value番目の素数を出力してください。
Fibonacciの場合、Value番目の素数を出力してください。

私のプログラム

Pythonで解答しています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#!/usr/local/bin/python3
# -*- coding:utf-8 -*-
 
import fileinput
import sys
 
# HelloWorldを引数の回数繰り返し出力
def doHelloWorld(v):
    sys.stdout.write("HelloWorld" * v)
 
# 入力値を判断してFizzBuzzを出力
def doFizzBuzz(v):
    if v%15 == 0:
        sys.stdout.write("FizzBuzz")
    elif v%5 == 0:
        sys.stdout.write("Buzz")
    elif v%3 == 0:
        sys.stdout.write("Fizz")
    else:
        sys.stdout.write(str(v))
 
# 引数番目の素数を出力。15個しかないので表を使う
def doPrime(v):
    p = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47]
    sys.stdout.write(str(p[v-1]))
 
# 引数番目のフィボナッチ数を出力。15個しかないので表を使う
def doFibonacci(v):
    f = [1,1,2,3,5,8,13,21,34,55,89,144,233,377,610]
    sys.stdout.write(str(f[v-1]))
 
Funcs = {
    "HelloWorld":doHelloWorld,
    "FizzBuzz":doFizzBuzz,
    "Prime":doPrime,
    "Fibonacci":doFibonacci,
}
 
if __name__ == "__main__":
    for line in fileinput.input():
        if not line.strip():
            continue
 
        cmd, val = line.strip().split(",")
 
        Funcs[cmd](int(val))

解説

どうこう言うような問題ではありませんが、ちょっとして工夫があります。

表引き

素数とフィボナッチ数列ですが仕様で15個しかないことが明らかです。
たかだか15個しかないものを計算すするのは無駄ですので表引きにしてしまいました。O(1)の計算量で済みます。確かK&Rの『プログラミング言語C」にも書いてあったと思うのですが、はっきりとわかっている結果はああらかじめ計算しておいてそれを使うというのは有効な手段です。

ファンクションテーブル

32〜37行目のディクショナリです。キーに対してそれを処理する関数をセットしています。実際の呼び出しは46行目ですがディクリョナリで得た値を関数として解決しています。
このようなファンクションテーブルは入力によって処理を動的に決定したい場合に使える有効なテクニックです。プログラムの見通しも良くなりますし、機能追加や削除も容易です。条件文による処理よりもずっと優れています。

雑感

この文章を書きながら思ったのですが、この問題の趣旨はどれだけ華麗なプログラムを書けるのかにあるのかもしれません。4つの関数を全て結果の文字列を返すようにすればラムダ式で書けるので32〜37行目のvalueに直接設定すれば7〜30行目までがなくなってすごく短くなります。
こんな感じ。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/local/bin/python3
 
import fileinput
import sys
 
Funcs = {
    "HelloWorld": (lambda n: "HelloWorld" * n),
    "FizzBuzz": (lambda n: "FizzBuzz" if n%15 == 0 else ( "Buzz" if n%5 == 0 else ( "Fizz" if n%3 == 0 else str(n)))),
    "Prime": (lambda n: str([2,3,5,7,11,13,17,19,23,29,31,37,41,43,47][n])),
    "Fibonacchi": (lambda n: str([1,1,2,3,5,8,13,21,34,55,89,144,233,377,610][n])),
}
 
if __name__ == "__main__":
    for line in fileinput.input():
        if not line.strip():
            continue
 
    cmd, val = line.strip().split(",")
    sys.stdout.write(Funcs[cmd](int(val)))