missing 'xor' Boolean operator

  • Thread starter Dr. Phillip M. Feldman
  • Start date
D

Dr. Phillip M. Feldman

Current Boolean operators are 'and', 'or', and 'not'. It would be nice to
have an 'xor' operator as well.
 
M

Mark Dickinson

Current Boolean operators are 'and', 'or', and 'not'.  It would be nice to
have an 'xor' operator as well.

Hmm. I don't think 'nice' is sufficient. You'd need to make the case
that it's sufficiently useful to justify adding a new keyword 'xor' to
the language; I suspect that would be an uphill struggle. :)

I'll just note that:

(1) It's easy to emulate xor: 'x xor y' <-> bool(x) != bool(y)

(2) 'and' and 'or' are special in that they have useful short-
circuiting behaviour; xor doesn't have this property (that is, you
always need to evaluate *both* operands to determine the result).

I'd also guess that 'xor' would be much less used than 'and' or 'or',
but maybe that's just a reflection of the sort of code that I tend to
write.
 
C

Chris Rebert

Hmm.  I don't think 'nice' is sufficient.  You'd need to make the case
that it's sufficiently useful to justify adding a new keyword 'xor' to
the language;  I suspect that would be an uphill struggle. :)

I'll just note that:

(1) It's easy to emulate xor:  'x xor y' <-> bool(x) != bool(y)

Using the xor bitwise operator is also an option:
bool(x) ^ bool(y)

Cheers,
Chris
 
M

Mark Dickinson

Using the xor bitwise operator is also an option:
bool(x) ^ bool(y)

Good point. For some reason I expected bitwise operations on bools to
return ints rather than bools. Now I know better. :)

Thanks,

Mark
 
D

Dr. Phillip M. Feldman

!= does do what I want, except that it doesn't indicate to someone reading
the code that the operands are being treated as logicals. (Readability is
supposed to be one of the major selling points of Python). But, this is
probably good enough.

Here's a related issue: I would like to see an option for type checking on
operands of logical operators, so that attempting to apply a logical
operator to non-Boolean entities generates a warning message. With operand
type checking, 'xor' and != would be different.
 
C

Chris Rebert

On Tue, Jul 14, 2009 at 12:56 PM, Dr. Phillip M.
Feldman said:
Here's a related issue: I would like to see an option for type checking on
operands of logical operators, so that attempting to apply a logical
operator to non-Boolean entities generates a warning message. With operand
type checking, 'xor' and != would be different.

That's probably not gonna happen. Python is dynamically and duck
typed, and has a sweet coercion system (__bool__ or __nonzero__
depending on your version) to coerce non-booleans to booleans in a
sensible and useful way. So, it's not gonna change any time soon.

Some illustrative examples:
#empty containers considered false
bool([] or {} or set() or "")
False
#zero and null considered false
bool(0 or None)
False
#anything else is, by default, true
bool(object())
True

And like I said, a class can override a special method to provide
whatever boolean semantics are desired. It's surprisingly handy.

Cheers,
Chris
 
R

Robert Kern

!= does do what I want, except that it doesn't indicate to someone reading
the code that the operands are being treated as logicals. (Readability is
supposed to be one of the major selling points of Python). But, this is
probably good enough.

In the words of those greater than myself, "Not every one-liner needs to be in
the standard library."

def xor(a, b):
return bool(a) != bool(b)

--
Robert Kern

"I have come to believe that the whole world is an enigma, a harmless enigma
that is made terrible by our own mad attempt to interpret it as though it had
an underlying truth."
-- Umberto Eco
 
E

Ethan Furman

Robert said:
In the words of those greater than myself, "Not every one-liner needs to
be in the standard library."

def xor(a, b):
return bool(a) != bool(b)

Let's see...

and returns the last object that is "true"
or returns the first object that is "true"

so should xor return the only object that is "true", else False/None?

def xor(a, b)
if a and b:
return None
elif a:
return a
elif b:
return b
else:
return None

~Ethan~
 
M

MRAB

Ethan said:
Let's see...

and returns the last object that is "true"
or returns the first object that is "true"

so should xor return the only object that is "true", else False/None?

def xor(a, b)
if a and b:
return None
elif a:
return a
elif b:
return b
else:
return None
How about:

def xor(a, b):
return not b and a or not a and b
 
E

Ethan Furman

MRAB said:
How about:

def xor(a, b):
return not b and a or not a and b

In [12]: not 1 and 0 or 1 and not 0
Out[12]: True

In [13]: not 0 and 0 or 0 and not 0
Out[13]: 0

In [14]: not 1 and 1 or 1 and not 1
Out[14]: False

In [15]: not [] and [] or [] and not []
Out[15]: []

Doesn't produce consistent objects, sometimes bool, sometimes something
else. 'Course, it all depends on what you need.

~Ethan~
 
E

Ethan Furman

Christian said:
I prefer something like:

bool(a) + bool(b) == 1

It works even for multiple tests (super xor):

if bool(a) + bool(b) + bool(c) + bool(d) != 1:
raise ValueError("Exactly one of a, b, c and d must be true")

Christian

super_xor! I like it!

In [23]: def super_xor(args, failure=False):
....: found_one = False
....: for item in args:
....: if item:
....: if found_one:
....: return failure
....: else:
....: found_one = item
....: return found_one or failure

In [25]: super_xor((0, 1, 0, []))
Out[25]: 1

In [26]: super_xor((0, 1, 0, [],('this',)))
Out[26]: False

In [27]: super_xor((0, {}, 0, [],()))
Out[27]: False

In [16]: def super_or(args, failure=False):
....: for item in args:
....: if item:
....: return item
....: else:
....: return failure
....:

In [17]: super_or((None, [], 0, 3, {}))
Out[17]: 3

In [18]: super_or((None, [], 0, (), {}))
Out[18]: False

In [19]: def super_and(args, failure=False):
....: for item in args:
....: if not item:
....: return failure
....: else:
....: return item
....:

In [20]: super_and((1, 2, 3))
Out[20]: 3

In [21]: super_and((1, 2, 3, 4))
Out[21]: 4

In [22]: super_and((1, 0, 3, 4))
Out[22]: False

A whole family of supers. :)

~Ethan~
 
E

Ethan Furman

Scott said:
A little suspect this.
_and_ returns the first object that is not "true," or the last object.


Similarly:
_or_ returns the first object that is "true," or the last object.

Thanks for the correction.

~Ethan~
 
D

Dr. Phillip M. Feldman

I appreciate the effort that people have made, but I'm not impressed with any
of the answers. For one thing, xor should be able to accept an arbitrary
number of input arguments (not just two), and should return True if and only
if the number of input arguments that evaluate to True is odd (see
www.mathworld.com article on xor). Here's my code:

def xor(*args):
"""xor accepts an arbitrary number of input arguments, returning True
if and only if bool() evaluates to True for an odd number of the input
arguments."""

result= False

for arg in args:
if bool(arg): result= not result

return result
 
M

Miles Kaufmann

I appreciate the effort that people have made, but I'm not impressed
with any
of the answers. For one thing, xor should be able to accept an
arbitrary
number of input arguments (not just two)

You originally proposed this in the context of the existing short-
circuit boolean operators. Those operators (being infix) take only
two operands.
and should return True if and only
if the number of input arguments that evaluate to True is odd

The existing and/or operators always return the value of one of the
operands--not necessarily True or False--which is another important
property, but one that can't be translated fully to xor. Given the
lack of context in your original post, hopefully you'll forgive me
being unimpressed by your not being impressed. :)
Here's my code:

def xor(*args):
"""xor accepts an arbitrary number of input arguments, returning
True
if and only if bool() evaluates to True for an odd number of the
input
arguments."""

result= False

for arg in args:
if bool(arg): result= not result

return result

If all you want is a True or False result, I'd write it like this:

import operator
def xor(*args):
return reduce(operator.xor, map(bool, args)) # or imap

In order to make it act more like the other logical operators, I'd use
MRAB's 2-argument xor as the reducer--though I can't really see the
utility.

def xor2(a, b):
return (not b and a) or (not a and b)

def xor(*args):
return reduce(xor2, args)

You may also find this question interesting:

http://stackoverflow.com/questions/432842/

-Miles
 
M

Mark Dickinson

I appreciate the effort that people have made, but I'm not impressed with any
of the answers.  For one thing, xor should be able to accept an arbitrary
number of input arguments (not just two), and should return True if and only
if the number of input arguments that evaluate to True is odd.

Well, that's not exactly what you originally asked for. But it's
still a one-liner:

def xor(*args): return bool(sum(map(bool, args)) % 2)

or perhaps

def xor(*args): return bool(len(filter(None, args)) & 1)
Here's my code:

def xor(*args):
   """xor accepts an arbitrary number of input arguments, returning True
   if and only if bool() evaluates to True for an odd number of the input
   arguments."""

   result= False

   for arg in args:
      if bool(arg): result= not result

It's more idiomatic to say "if arg: ..." rather than "if bool
(arg): ...".
   return result

Mark
 
A

Asun Friere

On Jul 15, 5:07 am, "Dr. Phillip M. Feldman" <[email protected]>
wrote:
[snip]
   for arg in args:
      if bool(arg): result= not result

It's more idiomatic to say "if arg: ..." rather than "if bool
(arg): ...".

Ah yes, but not once conditional tests, (just like logical operators),
type-check to ensure they have been supplied with Boolean entities. ;)
 
J

Jonathan Gardner

A whole family of supers.  :)

You should pick up Lisp. The whole concept of a binary operator
doesn't exist over there. All the things binary operators can do, Lisp
does with 0, 1, 2, or more arguments.

[1]> (+)
0
[2]> (+ 1)
1
[3]> (+ 1 2)
3
[4]> (+ 1 2 3)
6

Once you get used to that, binary operators don't seem so useful
anymore.

The equivalent in Python is dropping the operators and replacing them
with built-in functions that take 0, 1, 2, or more arguments.
 
S

Steven D'Aprano

Current Boolean operators are 'and', 'or', and 'not'. It would be nice
to have an 'xor' operator as well.

I've often wished there was too, for the sake of completeness and
aesthetics, I'd love to be able to write:

a xor b

instead of defining a function xor(a, b).

Unfortunately, outside of boolean algebra and simulating electrical
circuits, I can't think of any use-cases for an xor operator. Do you have
any?
 
P

pdpi

I prefer something like:

    bool(a) + bool(b) == 1

It works even for multiple tests (super xor):

  if bool(a) + bool(b) + bool(c) + bool(d) != 1:
      raise ValueError("Exactly one of a, b, c and d must be true")

Christian

"if bool(a) + bool(b) + bool(c) + bool(d) != 1:" is not equivalent to
xor. 1 xor 1 xor 1 = 1 xor (1 xor 1) = 1 xor 0 = 1 (or = (1 xor 1) xor
1 = 0 xor 1 = 1 if you assicate to the left)
 
T

Tim Wintle

All the things binary operators can do, Lisp
does with 0, 1, 2, or more arguments.

+1

n-ary operators are great, but binary operators are just syntactic sugar
for functions (which are obviously a generalisation of an n-ary operator
in python).

The fact that lisp-like languages don't expose this sugar is good for
making you think about what you're actually doing, but just like
mathematicians use binary (and unary) operators in equations rather than
working in the lambda calculus, having that sugar is useful to python.
[1]> (+)
0
[2]> (+ 1)
1
[3]> (+ 1 2)
3
[4]> (+ 1 2 3)
6

c.f. :
sum([]) 0
sum([1]) 1
sum([1,2]) 3
sum([1,2,3])
6



Once you get used to that, binary operators don't seem so useful
anymore.

The equivalent in Python is dropping the operators and replacing them
with built-in functions that take 0, 1, 2, or more arguments.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top