19

Does Ruby have a plain-English keyword for exclusive or, like they have "and" and "or"? If not, is this because exclusive or doesn't allow evaluation short-cutting?

Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338

7 Answers7

26

No it doesn't, you can only use ^.

Don't know why there isn't particularly, may just be because it isn't as commonly used.

Garry Shutler
  • 32,260
  • 12
  • 84
  • 119
  • 1
    `^` runs into problems with truthy values. I defined my own function: – John Aug 08 '12 at 13:45
  • I have just been hit by this. I am using ruby-prof to improve an algorithm, and I thought that simplifying some condition with a XOR would make it faster. But it's the other way around. – Papipo Nov 26 '12 at 00:43
19

I ran into an issue because the '^' operator acts bitwise on numbers,

true ^ 1
=> false

1 ^ true
TypeError: can't convert true into Integer
true ^ 1

so my workaround was:

( !!a ^ !!b ) where the double-bang coerces them into booleans.

!!1 ^ !!true
=> false

!!1 ^ !!false
=> true
Matt Van Horn
  • 1,654
  • 1
  • 11
  • 21
  • 1
    Interesting, but not hugely relevant to the question. – Macha Jun 29 '09 at 14:08
  • 2
    ++ Useful. I came here looking for XOR, but know how to do the opposite, that is, coerce a bool to_i? My other operand is already an int, and I need the result to be int. – Marcos Apr 14 '12 at 21:37
  • @Macha +1ing because I've just come across the same issue, and there's no other questions about Ruby boolean xors that weren't about strings. – Andrew Grimm May 18 '12 at 04:13
  • 2
    Since basically you are checking that the values are different, you can actually save a `!` and just do: `!foo ^ !bar`. Or `!foo != !bar` – Zequez Jul 20 '14 at 02:53
  • In fact you only need first value to be a boolean. `!!var1 ^ var2`. Also `nil` has the `^` operator working as a boolean xor. – akostadinov Nov 28 '14 at 13:03
13

Try ^

true  ^ false #=> true
true  ^ true  #=> false
false ^ false #=> false

No plain english equivalent operator though.

Alex Wayne
  • 178,991
  • 47
  • 309
  • 337
  • my issue with this is `true ^ true ^ true` => `true` – MrYoshiji Sep 26 '19 at 21:53
  • 2
    @MrYoshiji It makes sense if the order of operations is (true ^ true) ^ true. The parenthesis evaluates to false, giving you false ^ true, which in the end is true. – Claudiu Feb 21 '20 at 16:32
13

Firstly, I don't think shortcircuiting can sensibly apply to XOR: whatever the value of the first operand, the second needs to be examined.

Secondly, and, &&, or and || use shortcircuiting in all cases; the only difference between the "word" and "symbol" versions is precedence. I believe that and and or are present to provide the same function as perl has in lines like

process_without_error or die

I think the reason for not having a xor named function is probably that there's no point in a low-precedence operator in this case and that it's already a confusing enough situation!

Mike Woodhouse
  • 51,832
  • 12
  • 88
  • 127
  • I think that "no point in low precedence `xor` function" is not right. In Perl I was used to check two exclusive CGI parameters with something like `param1 xor param2`. Shame I need to do this in a more complicated way in Ruby. – geronime Aug 23 '11 at 20:59
  • 1
    Nowadays, I agree about `and` and `or` being control flow. Avdi has written about it well at http://devblog.avdi.org/2010/08/02/using-and-and-or-in-ruby/ (in fact, I linked to this question in the comments section of that blog post) – Andrew Grimm Jun 24 '13 at 06:33
10

As an alternative to Matt Van Horn's double negation trick for using XOR on arbitrary types, you can chain another XOR test, starting with nil. i.e.:

!!foo ^ !!bar

is equivalent to

nil ^ foo ^ bar

This looks neater to my eye, and I suppose requires one less logical operation

charrison
  • 806
  • 1
  • 7
  • 14
2

Any implementation of xor won't allow short circuiting. Both expressions need to be evaluated no matter what.

Ruby does provide the ^ operator, but this will choke on truthy values. I've implemented a function to handle the cases where I want an xor that behaves more like and and or:

def xor(a,b)
  (a and (not b)) or ((not a) and b)
end

Unlike ^, this function can be used in situations similar to the following:

xor("hello".match(/llo/), false) # => true
xor(nil, 1239)                   # => true
xor("cupcake", false)            # => false
John
  • 138
  • 1
  • 7
2

John's answer appears incorrect. In irb with 1.9.3, xor("cupcake", false) returns true, as you'd expect.

1.9.3-p429 :104 > def xor(a,b)
1.9.3-p429 :105?>     (a and (not b)) or ((not a) and b)
1.9.3-p429 :106?>   end
 => nil 
1.9.3-p429 :107 > xor(false, true)
 => true 
1.9.3-p429 :108 > xor("cupcake", false)
 => true 
CRABOLO
  • 8,605
  • 39
  • 41
  • 68
ChrisPhoenix
  • 1,040
  • 1
  • 11
  • 16