Algunas de las implementaciones sugeridas aquí causarán una evaluación repetida de los operandos en algunos casos, lo que puede provocar efectos secundarios no deseados y, por lo tanto, debe evitarse.
Dicho esto, una xor
implementación que devuelve True
o False
es bastante simple; uno que devuelve uno de los operandos, si es posible, es mucho más complicado, porque no existe consenso sobre qué operando debe ser el elegido, especialmente cuando hay más de dos operandos. Por ejemplo, debería xor(None, -1, [], True)
regresar None
, []
oFalse
? Apuesto a que cada respuesta parece ser la más intuitiva para algunas personas.
Para el resultado Verdadero o Falso, hay hasta cinco opciones posibles: devolver el primer operando (si coincide con el resultado final en valor, de lo contrario booleano), devolver la primera coincidencia (si existe al menos uno, de lo contrario booleano), devolver el último operando (si ... más ...), devolver la última coincidencia (si ... más ...), o siempre devolver boolean. En total, eso es 5 ** 2 = 25 sabores de 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()