2013年5月24日金曜日

えせはらさん( @esehara )のブログ(蟲!虫!蟲! - #!/usr/bin/bugrammer)の以下の投稿を読んで。

変数に代入せず、関数でandやorで判定しているときに気をつけたいこと

その関数、メソッドは他に副作用を持つか?または副作用を持ったあとに、何かしら値を使う必要があるか?
ということに注意しないといけない。もし、上記に当てはまる場合は、真偽値を一度変数に代入して持っておき、あとでそれをandやorによって比較したほうが安全だと思う。

よく使うなら、その都度変数に代入してその変数を使う(2回書く必要がある)のも大変だし、あれば便利かなぁと思ってショートサーキット評価(短絡評価)を避けるための関数(ロングサーキット評価?)を書いてみた。

コード(BBEdit)

sample.py

#!/usr/bin/env python3.3
#-*- coding: utf-8 -*-

def longCirEval4Func(*funcs, op="and"):
    op = op.lower()
    bools = [func() for func in funcs]
    if op == "and":
        for b in bools:
            if not b:
                return False
        else:
            return True
    elif op == "or":
        for b in bools:
            if b:
                return True
        else:
            return False
    raise Exception("error: op should be 'and' or 'or'")

class Foobar(object):

    def __init__(self, numbers):
        self.numbers = numbers

    def has_odd(self):
        odd = []
        has_odd = False
        for number in self.numbers:
            if number % 2 == 1:
                odd.append(number)
                has_odd = True
        self.odd = odd
        return has_odd

foo = Foobar([1, 3, 5, 9])
bar = Foobar([2, 4])
foobar = Foobar([1, 2, 4])

print("and/or演算子の前に関数を呼び出す場合")
tmp1, tmp2, tmp3 = foo.has_odd(), bar.has_odd(), foobar.has_odd()
if tmp1 and tmp2 and tmp3:
    print("ALL ODD")

for x in [foo, bar, foobar]:
    print(x.odd)

foo = Foobar([1, 3, 5, 9])
bar = Foobar([2, 4])
foobar = Foobar([1, 2, 4])

print("longCirEval4Funcを使った場合")
if longCirEval4Func(foo.has_odd, bar.has_odd, foobar.has_odd, op="and"):
    print("ALL ODD")

for x in [foo, bar, foobar]:
    print(x.odd)

副作用がちゃんと起こっているかどうか確認。

入出力結果(Terminal)

$ ./tmp.py
and/or演算子の前に関数を呼び出す場合
[1, 3, 5, 9]
[]
[1]
longCirEval4Funcを使った場合
[1, 3, 5, 9]
[]
[1]
$

foobar.oddが作成されているのが確認できた。

けど、せっかく作成してはみたものの、必ず実行させたい関数、副作用を起こしたい関数、メソッドが特に多い場合以外は、変数に代入する方法で十分かも。。><w

他にもbool演算の応用などについて詳しくは、『初めてのPython 第3版』のp.264とかに記述有り。

0 コメント:

コメントを投稿