Einige der hier vorgeschlagenen Implementierungen führen in einigen Fällen zu einer wiederholten Bewertung der Operanden, was zu unbeabsichtigten Nebenwirkungen führen kann und daher vermieden werden muss.
Das heißt, eine xor
Implementierung, die entweder zurückgibt True
oder False
ziemlich einfach ist; Eine, die nach Möglichkeit einen der Operanden zurückgibt, ist viel schwieriger, da kein Konsens darüber besteht, welcher Operand der ausgewählte sein soll, insbesondere wenn mehr als zwei Operanden vorhanden sind. Zum Beispiel sollte xor(None, -1, [], True)
zurückkehren None
, []
oderFalse
? Ich wette, jede Antwort erscheint einigen Leuten als die intuitivste.
Für das True- oder das False-Ergebnis gibt es bis zu fünf mögliche Optionen: Rückgabe des ersten Operanden (wenn er mit dem Endergebnis im Wert übereinstimmt, sonst Boolescher Wert), Rückgabe der ersten Übereinstimmung (wenn mindestens einer vorhanden ist, sonst Boolescher Wert), letzten Operanden zurückgeben (wenn ... sonst ...), letzte Übereinstimmung zurückgeben (wenn ... sonst ...) oder immer boolesch zurückgeben. Insgesamt sind das 5 ** 2 = 25 Geschmacksrichtungen 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()