ここで提案されている実装の一部は、場合によってはオペランドの繰り返し評価を引き起こし、意図しない副作用を引き起こす可能性があるため、回避する必要があります。
そうは言っても、xor
どちらかを返すか、True
またはFalse
かなり単純な実装です。特に2つ以上のオペランドがある場合、どのオペランドを選択するかについてコンセンサスが存在しないため、可能であれば、オペランドの1つを返すものはよりトリッキーです。たとえば、必要がありますxor(None, -1, [], True)
返すNone
、[]
またはFalse
?私はそれぞれの答えが最も直感的なものとして何人かの人々に見えるに違いない。
TrueまたはFalseの結果の場合、最大5つの選択肢があります。最初のオペランドを返す(最終結果と値が一致する場合はブール値)、最初の一致を返す(少なくとも1つが存在する場合はブール値)、最後のオペランドを返す(if ... else ...)、最後の一致を返す(if ... else ...)、または常にブール値を返します。要するに、これは5 ** 2 = 25フレーバーですxor
。
def xor(*operands, falsechoice = -2, truechoice = -2):
"""A single-evaluation, multi-operand, full-choice xor implementation
falsechoice, truechoice: 0 = always bool, +/-1 = first/last operand, +/-2 = first/last match"""
if not operands:
raise TypeError('at least one operand expected')
choices = [falsechoice, truechoice]
matches = {}
result = False
first = True
value = choice = None
# avoid using index or slice since operands may be an infinite iterator
for operand in operands:
# evaluate each operand once only so as to avoid unintended side effects
value = bool(operand)
# the actual xor operation
result ^= value
# choice for the current operand, which may or may not match end result
choice = choices[value]
# if choice is last match;
# or last operand and the current operand, in case it is last, matches result;
# or first operand and the current operand is indeed first;
# or first match and there hasn't been a match so far
if choice < -1 or (choice == -1 and value == result) or (choice == 1 and first) or (choice > 1 and value not in matches):
# store the current operand
matches[value] = operand
# next operand will no longer be first
first = False
# if choice for result is last operand, but they mismatch
if (choices[result] == -1) and (result != value):
return result
else:
# return the stored matching operand, if existing, else result as bool
return matches.get(result, result)
testcases = [
(-1, None, True, {None: None}, [], 'a'),
(None, -1, {None: None}, 'a', []),
(None, -1, True, {None: None}, 'a', []),
(-1, None, {None: None}, [], 'a')]
choices = {-2: 'last match', -1: 'last operand', 0: 'always bool', 1: 'first operand', 2: 'first match'}
for c in testcases:
print(c)
for f in sorted(choices.keys()):
for t in sorted(choices.keys()):
x = xor(*c, falsechoice = f, truechoice = t)
print('f: %d (%s)\tt: %d (%s)\tx: %s' % (f, choices[f], t, choices[t], x))
print()