Python School 2.0.0 documentation

トイプログラム - FizzBuzz

«  基本的なスクリプト   ::   Contents   ::   スクリプトファイルの構成  »

トイプログラム - FizzBuzz

お遊びがてら FizzBuzz を解きます。

実行例

次のような結果を出力するスクリプトを書いてみましょう。

$ python fizzbuzz.py 16
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16

スクリプト例

例として3つの実装を示します。 以下のことを身につけましょう。

  • 関数定義、main 関数
  • 引数処理、入力チェック
  • 組み込み関数: range, zip, enumerate
  • 後置 IF 文
  • ジェネレーター (yield)
  • ラムダ式
  • リスト操作: map / filter
  • 型 (type) の利用、リストとタプル
  • テストの作成、assert の利用
  • shebang の記述、エンコーディングの指定 (PEP 263)
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""Toy FizzBuzz implementation.
"""

import argparse

SEPARATOR = "=" * 78


def parse_args():
    parser = argparse.ArgumentParser()
    parser.add_argument("number", type=int)
    args = parser.parse_args()
    if args.number <= 0:
        parser.error("Number must be positive: {}".format(args.number))
    return args.number


def fizzbuzz(i):
    """Basic implementation."""
    if i % 15 == 0:
        return "FizzBuzz"
    elif i % 5 == 0:
        return "Buzz"
    elif i % 3 == 0:
        return "Fizz"
    else:
        return i


def fizzbuzz_iter(n):
    """Generator, using zip and postposion IF statement."""
    fb = ("Fizz", "Buzz")
    i = 1
    while i <= n:
        t = (i % 3 == 0, i % 5 == 0)
        s = ''.join(map(lambda t: t[1], filter(lambda t: t[0], zip(t, fb))))
        yield s if s else i
        i += 1


def fizzbuzz_list(n):
    """map and lamba function. also showing enumerate and type, and zip."""
    ret = []
    m = range(1, n + 1)
    m1 = map(lambda i: i if i % 3 > 0 else "Fizz", m)
    m2 = map(lambda i: i if i % 5 > 0 else "Buzz", m)
    m3 = map(lambda i: i if i % 15 > 0 else "FizzBuzz", m)
    for i, t in enumerate(zip(m1, m2, m3)):
        r = [i for i in t if type(i) == str]
        ret.append(list(r).pop() if r else str(i + 1))
    return ret


def main():
    n = parse_args()

    print(SEPARATOR)
    for i in range(1, n + 1):
        val = fizzbuzz(i)
        print(val)

    print(SEPARATOR)
    for i in fizzbuzz_iter(n):
        print(i)

    print(SEPARATOR)
    print('\n'.join(fizzbuzz_list(n)))


def test():
    expected = [
        "1", "2", "Fizz", "4", "Buzz",
        "Fizz", "7", "8", "Fizz", "Buzz",
        "11", "Fizz", "13", "14", "FizzBuzz",
        "16"
    ]
    actual = fizzbuzz_list(16)
    assert len(expected) == len(actual)
    for e, a in zip(expected, actual):
        assert e == a, 'expected={}, actual={}'.format(e, a)

if __name__ == "__main__":
    main()
    #test()

# vim: set et ts=4 sw=4 cindent fileencoding=utf-8 :

宿題

引数やオプションを追加して、自分で書いたスクリプトもしくは上記のスクリプトを改造しましょう。

  1. 特定の範囲だけを出力する。100から200など。
  2. Linux 上の bash で実行したときに出力に色を付ける。”Fizz”=青、”Buzz”=赤、”FizzBuzz”=黄。
  3. 標準出力を横取りして出力結果を assert する。
  4. 通常の実行かテストの実行かを切り替える。
  5. プロファイリングする。 (profile, cProfile, pstats – Python プログラムのパフォーマンス解析)

ワンライナーや剰余利用禁止などは Google で検索するとたくさん出てきます。 同じことを実現する方法はいくつか存在しますが、実行効率や読みやすさは千差万別です。 特別な理由が無い限りは、読みやすい平易なソースコードを書くように心がけましょう。

«  基本的なスクリプト   ::   Contents   ::   スクリプトファイルの構成  »