Niektóre sugerowane tutaj implementacje spowodują w niektórych przypadkach ponowną ocenę operandów, co może prowadzić do niezamierzonych efektów ubocznych i dlatego należy ich unikać.
Powiedział, że xor
realizacja który powraca albo True
czy False
jest dość prosta; jeden, który zwraca jeden z operandów, jeśli to możliwe, jest znacznie trudniejszy, ponieważ nie istnieje konsensus co do tego, który operand powinien być wybrany, zwłaszcza gdy są więcej niż dwa operandy. Na przykład, należy xor(None, -1, [], True)
powrócić None
, []
albo False
? Założę się, że każda odpowiedź wydaje się niektórym osobom najbardziej intuicyjna.
W przypadku wyniku True lub False istnieje aż pięć możliwych opcji: zwróć pierwszy operand (jeśli jest zgodny, wynikiem końcowym jest wartość, w przeciwnym razie boolean), zwróć pierwsze dopasowanie (jeśli przynajmniej jeden istnieje, boolean), zwraca ostatni argument (jeśli ... else ...), zwraca ostatni mecz (jeśli ... else ...) lub zawsze zwraca wartość logiczną. W sumie to 5 ** 2 = 25 smaków 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()