Hatena::Groupbugrammer

蟲!虫!蟲!

Esehara Profile Site (by Heroku) / Github / bookable.jp (My Service)
過去の記事一覧はこちら

なにかあったら「えせはら あっと Gmail」まで送って頂ければ幸いです。
株式会社マリーチでは、Pythonやdjango、また自然言語処理を使ったお仕事を探しています

 | 

2011-10-08

[]Pythonで"if"を使わない"FizzBuzzチェック関数"を三種類ほど実装する 17:55

 FizzBuzz問題を使って社内プログラミングコンテストを開催してみた - give IT a tryをやってみる。

 普通にやると面白くないので、あえてifを封印した。というより、プログラムの勉強をするのならば、ifを使ったFizzBuzzは当然できるとして、ifを使わないFizzBuzzの方法も一つくらいは書けてると、「そもそも、これってどういう動きをするものなの?」ということを理解していることになっていいんじゃないかなーとか思ったので。FizzBuzzといえば、「どれだけ短いコードでFizzBuzzが書けるか」という競技、FizzBuzzGolf(anarchy golf - FizzBuzz)が有名だけど、こっちの「あえてifを使わないFizzBuzzコード大会」みたいなのの名称は無いのかしら。

 正直、何周目だよとは思うんだけど、一応。

 あと、以下の奴だと「0」がfizzbuzzになってしまうのがちょっと問題かなと思ったりもする。

反復を使う

def fizzbuzz_not_if(num):
    while num % 15 == 0:return "fizzbuzz"
    while num % 3 == 0:return "fizz"
    while num % 5 == 0:return "buzz"
    return str(num)

 whileは「式がTrueならば、そのブロックの手続きを行う」という反復。逆に言えばFalseだと実行されないので、上のようにreturnを忍ばしておいて、実行されたら抜け出すというようにすれば、擬似的なifとして使用することも可能だったり。あえてwhile - breakを使うのも小賢しくていいのかしら。初歩的といえば初歩的。

配列を使う

 三の倍数、五の倍数、十五の倍数を周期として考えると次のようにもかける。

def fizzbuzz_not_if_2(num):
    snum = str(num)
    arraybuzzfizz = [snum,snum,"fizz",snum,"buzz"] * 3
    arraybuzzfizz[14] = "fizzbuzz"
    arraybuzzfizz = arraybuzzfizz * ((num / 15) + 1)
    return arraybuzzfizz[num - 1]

 考え方としては、まず3の倍数の周期"3,6,9,12,15"で"buzz"を埋め込み、そのあとに"5,10"で"fizz"を埋め込む。さらに最後に"15"で"fizzbuzz"を埋め込む。これが配列のプロトタイプとなる。そこで、あとは元の数字よりも長い配列を作り、そしてその数字の要素を取り出してやればアレビックリ、fizz、buzzfizzbuzzが取り出せる。

 このメリットはわざわざ余りを求めなくてもいいこと。ただ、いちいち配列を作るのでメモリは使うと思う。

追記

 スライスを使った方法がとてもCoolだったので、こちらでもリンクして紹介しておきます。

 Pseudo L0H

エラーを使う

 ここに来ると自分でも「それはちょっと……」と思ったりする。

import re
def fizzbuzz_not_if_3(num):
    fizzbuzz = re.compile("0")
    notfizzbuzz = re.compile("10")
    returnfizzbuzz = re.compile("don't touch!!")
    try:
        snum = str(num % 15)
        snum = notfizzbuzz.sub("don't touch!!",snum)
        snum = fizzbuzz.sub("fizzbuzz",snum)
        snum = returnfizzbuzz.sub("10",snum)
        snum = int(snum)
        snum = str(num)
        snum = fizzbuzz.sub("fizz",str(num % 5))
        snum = int(snum)
        snum = fizzbuzz.sub("buzz",str(num % 3))
        snum = int(snum)
        snum = str(num)
        return snum
    except ValueError:
        return snum

 手間がかかった。

 まず、Python正規表現を使うためのモジュール"re"のsubメソッドは、文字列になにもマッチしない場合、元の文字列を戻してくれることを利用。またint型に出来ない文字列型はエラーになることを利用。fizzという文字列は明らかに数字に出来ないので、エラーに飛ぶので、そこから値をリターン。

 一回、10の余りを"don't touch!!"に変換するのは、15の割り算をすると、"10"の値である"0"が置換されて"1fizzbuzz"というしょうもないバグを出してしまう。そして、0が出てくるのは理論上、0そのものを除けば10の余りしかないので、そのまま指定。意外と動いてくれる。

 自分が思いつくのはこんなところかな。他に「こういう方法もあるよ」というのがあれば、教えてください。

ショートコーディング版を見つけたので追記

FizzBuzz問題が難しかった - はてブロ@ama-ch

tootoo2011/10/15 15:30def fizzbuzz_not_if(num):
fizz = "Fizz" if num % 3 == 0 else None
buzz = "Buzz" if num % 5 == 0 else None
try:
return fizz + buzz
except TypeError:
return fizz or buzz or str(num)

for i in range(1,17):
print fizzbuzz_not_if(i)

tootoo2011/10/15 16:06あらら、インデントが消えてしまいました。
はてなもPythonも不慣れなものですいません。

以前FizzBuzzが流行ったときに作ってみた、剰余も条件判断も使わないFizzBuzzジェネレーターからアイデアを抜粋。
タブを全角スペースに置換して書き込んでみます。
import itertools
def fizzbuzz():
  fizz = itertools.cycle([None,None,"Fizz"])
  buzz = itertools.cycle([None,None,None,None,"Buzz"])
  for i in itertools.count(1):
    f, b = fizz.next(), buzz.next()
    try:
      yield f+b
    except TypeError:
      yield f or b or i

g = fizzbuzz()
for i in range(100):
  print g.next()

nisemono_sannisemono_san2011/10/16 04:38返事が遅れてしまってすいません。
イテレーターを使うというのも考えたのですけど、自分で書くとあまり綺麗にならないので見送りました。
このイテレーターの使い方は綺麗ですね。参考になります!!

 |