http://www.perlmonks.org?node_id=273853

bsb has asked for the wisdom of the Perl Monks concerning the following question:

Or anywhere else for that matter?

Hardly anyone seems to in their modules:

$ find /usr/local/lib/perl -name '*pm' | xargs grep xor /usr/local/lib/perl/5.6.1/DateTime.pm: if ( $self->{tz}->is_floatin +g xor $was_floating ) /usr/local/lib/perl/5.6.1/Date/Calc/Object.pm: if ($val1 xo +r $val2);
Is 'xor' at all useful?

Brad

Replies are listed 'Best First'.
Re: Anyone use "xor" in conditionals?
by Abigail-II (Bishop) on Jul 14, 2003 at 00:48 UTC
    Yes it is. Because it's awkward to write:
    if ((COND1 || COND2) && !(COND1 && COND2))
    specially when either condition is either expensive, or has side-effects.

    A while ago, I was writing a small script that recursively compared directories (and no, plain diff didn't do, although the script called diff repeatedly). Since I was interested in the similarities instead of the differences, at one moment I wrote:

    next if -d $dir1 xor -d $dir2

    If either of the arguments wasn't a directory, but the other was, they couldn't match. But if both were, or both weren't, furthermore analysis was necessary.

    And yes, I know, xor can be written using other primitives as well. But that's not an argument, unless you also think 'for', 'while' and other looping constructs are redundant, because we have 'goto'.

    Abigail

      Atually there's an easier way to write xor:  if ( (COND1 and not COND2) or ((not COND1) and COND2) )

      This way the conditions are evaluated less times (I think this is the easier way of expressing 'xor' in terms of 'and', 'or' and 'not')

      While I agree that xor is a useful operation, there aren't that many places where it applies, as many situations rule themselves out in an or comparison.

      For example, unless you are trying to model the rainbow, if ( $raining or $sunny ) contemplates the whole weather (simplified) model. Admitedly, in some cases the possibility arises: just use it. But as CPAN demonstrates, there aren't so many examples.

      Now, if you are implementing a parser, or any complicated enough state machine...

      Best regards,

      --
      our $Perl6 is Fantastic;

        I use xor at a few places in the Regexp::Common test suite. I've two variables, one saying whether a regexp matched, and another saying whether the regexp should have matched. The test fails if one variable is true, the other false. Hence, an xor.

        Abigail

        This way the conditions are evaluated less times

        Eh? Not so.

        $ perl -le 'print "T" if (print "foo" xor print "bar")' foo bar $ perl -le 'print "T" if ((print "foo" and not print "bar") or (not pr +int "foo" and print "bar"))' foo bar foo
        At best, the expanded version will result in the same number of evaluations.

        -sauoq
        "My two cents aren't worth a dime.";
        
        Can you say that the internals of perl don't do the optimizations on user behalf? I'd hope that perl automatically thows out the expression w/o doing it fully if it finds out it's dead in the middle.

        btw, your way is for those who don't have the concept, or truth table, memorized and reproduce it w/o thinking. Nothing wrong w/ it though.

        Regardless, xor isn't something that is as easily applicapble such as, the plus operator or just the and operator.

        Certain tools, for certain situations.

      Wouldn't that (next if -d $dir1 xor -d $dir2) be exactly the same as next if -d $dir1 == -d $dir2 ? Why would you choose xor over that?
        Because I have warnings turned on.

        Abigail

        A reply falls below the community's threshold of quality. You may see it by logging in.
Re: Anyone use "xor" in conditionals?
by BrowserUk (Patriarch) on Jul 14, 2003 at 02:06 UTC

    It has its uses. The conventional way of determining if a year is leapyear goes something like this code from Date::Leapyear

    sub isleap { my ($year) = @_; return 1 if (( $year % 400 ) == 0 ); # 400's are leap return 0 if (( $year % 100 ) == 0 ); # Other centuries are not return 1 if (( $year % 4 ) == 0 ); # All other 4's are leap return 0; # Everything else is not }

    This can also be written as

    not $year % 4 xor $year % 100 xor $year % 400;

    Proof :)

    You should probably also seach for '^', but as I discovered, this is a sight more complex isolate the xor uses from all the other uses perl makes of this character.


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller


      Such code irks me. It always has to perform three modulus operations, even while in 99% of the cases it doesn't have to. The Date::Leapyear code irks me too, as it tests the rare cases first. I prefer code like the following:
      sub is_leap { my $year = shift; return 0 if $year % 4; return 1 if $year % 100; !($year % 400) }

      That code does one modulus operation in 75% of the cases, two modules operations in 24% of the cases, and only in 1% of the cases, it does 3 modulus operations.

      A benchmark shows the expected results. The code that always does three modulus operations is the slowest - the code that on averages does the least modulus operations is the fastest.

      #!/usr/bin/perl use strict; use warnings; use Benchmark qw /cmpthese/; sub isleap { my ($year) = @_; return 1 if (( $year % 400 ) == 0 ); # 400's are leap return 0 if (( $year % 100 ) == 0 ); # Other centuries are not return 1 if (( $year % 4 ) == 0 ); # All other 4's are leap return 0; # Everything else is not } sub browseruk { my ($year) = @_; not $year % 4 xor $year % 100 xor $year % 400; } sub abigail { my ($year) = @_; return 0 if $year % 4; return 1 if $year % 100; !($year % 400) } cmpthese -10 => { isleap => 'my $v = isleap $_ for 1800 .. 2199', browseruk => 'my $v = browseruk $_ for 1800 .. 2199', abigail => 'my $v = abigail $_ for 1800 .. 2199', }; __END__ Benchmark: running abigail, browseruk, isleap, each for at least 10 CP +U seconds... abigail: 11 wallclock secs (10.90 usr + 0.00 sys = 10.90 CPU) @ 18 +43.58/s (n=20095) browseruk: 10 wallclock secs (10.48 usr + 0.01 sys = 10.49 CPU) @ 14 +09.82/s (n=14789) isleap: 11 wallclock secs (10.76 usr + 0.01 sys = 10.77 CPU) @ 14 +54.04/s (n=15660) Rate browseruk isleap abigail browseruk 1410/s -- -3% -24% isleap 1454/s 3% -- -21% abigail 1844/s 31% 27% --

      Abigail

        Ah! But if it is out and out speeed we are after then I might have offered :)

        sub buk2 { $_[0] % 4 ? 0 : $_[0] % 100 ? 1 : not $_[0] % 400; } Rate browseruk isleap abigail buk2 browseruk 82.8/s -- -40% -54% -67% isleap 138/s 67% -- -24% -45% abigail 180/s 118% 31% -- -28% buk2 251/s 203% 82% 39% --

        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller


      Well, it helped me:
      $ perl -MO=Deparse,-p -e 'not $year % 4 xor $year % 100 xor $year % 40 +0;' (((not ($year % 4)) xor ($year % 100)) xor ($year % 400));
Re: Anyone use "xor" in conditionals?
by tachyon (Chancellor) on Jul 14, 2003 at 06:07 UTC

    It is one way to write a flip_flop, like in this snippet which uses a xor flip flop to alternate row colours in a 'pretty' HTML table...

    sub pretty_table { my ($ary, $width, $no_center) = @_; my $header = shift @$ary; $width = $width ? qq!width="$width"! : ''; my $html = qq!<table border="0" cellpadding="2" cellspacing="1" $w +idth>\n!; my $cols = scalar @$header; $html .= get_row( $header, $cols, 'header' ); my $flip_flop = 1; for my $row_ref ( @$ary ) { my $class = $flip_flop ? 'light' : 'dark'; $html .= get_row( $row_ref, $cols, $class ); $flip_flop ^= 1; } $html .= qq! <tr>\n <td colspan="$cols" class="header">&nbsp; +</td>\n </tr>\n!; $html .= "</table>\n"; $html = qq!<div align="center">\n<center>\n$html\n</center>\n</div +>! unless $no_center; return $html } sub get_row { my ( $row_ref, $cols, $class ) = @_; my $html = qq! <tr>\n!; for my $td ( 0.. $cols-1 ) { my $data = $row_ref->[$td] || '&nbsp;'; $html .= qq! <td class="$class">$data</td>\n!; } $html .= " </tr>\n"; return $html; }

    Of course you can get the same effect using $flip++ % 2 == 0 amongst others.

    cheers

    tachyon

    s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

      Hehe, whenever I use flipflops I confuse the cow orkers. However, I've always written it differently:
      my $flipflop = 1; for my $row_ref ( @$ary ) { ... $flip_flop = 1 - $flipflop; }
      I'm not sure that this is potentially more efficient in Perl (probably less efficent?), but is an idiom I remember from my Amiga asm days. Personally, I also think it's easier to see what's going on too :)

        I'm a bit confused by these flipflops being used, as well as the ternary thing, to alternate colours.

        I long ago whittled it down to just

        $bgcolor = ($bgcolor eq 'white'?'black':'white');
        which flips itself.

        “Every bit of code is either naturally related to the problem at hand, or else it's an accidental side effect of the fact that you happened to solve the problem using a digital computer.”
        M-J D
      I prefer a data driven approach.
      my @cycle = ('#fff', '#fff', '#fff', '#ccc', '#ccc'); my $i = 0; # ... $foo .= $cycle[$i++ % @cycle]; # ...
      That way it is a no-brainer to use any pattern you wish. "Capture similarities in code, disparities in data."

      Makeshifts last the longest.

Re: Anyone use "xor" in conditionals? (yes/no)
by tye (Sage) on Jul 14, 2003 at 06:08 UTC

    On at least two occasions (but not very many more than that) I've written code like:

    if( !$a == !$b ) { #or if( !$x != !$y ) {
    and those could now be rewritten:
    if( not $a xor $b ) { #or if( $x xor $y ) {
    but I don't adopt new Perl features lightly and I haven't written Perl code that wanted that sort of test since I noticed the existance of xor so I've never used xor, just written code where I would have used it had it been available enough at the time.

    Though, I actually find the concept of "these two logical expressions are equal" to be more useful/natural than a logical "xor" (not equal) so I'm more likely to want

    if( !$a == !$b ) {
    than the other case and I'd probably leave that as-is rather than change it to
    if( not $a xor $b ) {
    because I find the latter harder to understand. So I'd probably rather have an eqv (pronounced "equivalent") over an xor. (:

                    - tye

      On at least two occasions (but not very many more than that) I've written code like:

      Heh. In my test code I use the syntax you've shown quite regularly. I was unaware that xor did the right thing with undef and hence have used the !$x == !$y or !$x != !$y syntax fairly often.

      And I also agree with you that I prefer this syntax to xor as it to me makes the intent of the test easier to see.


      ---
      demerphq

      <Elian> And I do take a kind of perverse pleasure in having an OO assembly language...
Re: Anyone use "xor" in conditionals?
by edan (Curate) on Jul 14, 2003 at 07:12 UTC

    Not that I have anything really earth-shattering to add to what others have written, but just wanted to add another voice to the 'xor can be useful' crowd...

    I used xor in the following situation:

    There are 3 states, call them A, B, and Z, where the first 2 are sort of similar, and the last is different. I needed a conditional to test for the transition between (A or B) and Z (or vice versa). So instead of writing some monstrous thing like:

    if ((($before eq 'A' || $before eq 'B') && $after eq 'Z') || ($before eq 'Z' && ($after eq 'A' || $after eq 'B'))) { # change from A|B <=> Z }
    I simply wrote this:
    if ($before eq 'Z' xor $after eq 'Z') { # change from A|B <=> Z }

    I hope my explanation made sense... but that's my story of how I used xor...

    Update: Upon further reflection, my non-xor conditional would be more simply and accurately written as one of these:

    if (($before ne 'Z' && $after eq 'Z') || ($before eq 'Z' && $after ne 'Z')) { # change from A|B <=> Z } if (($before eq 'Z' || $after eq 'Z') && !($before eq 'Z' && $after eq 'Z')) { # change from A|B <=> Z }

    ... and that is equivalent to Abigail-II's summary of xor's logical equivalency:
    if ((COND1 || COND2) && !(COND1 && COND2))

    But I think it's still clear why xor is useful here, and perhaps I shed some light on a real-world usage... Not having looked at the source, but only bsb's post, I am guessing it's similar to the situation here:

    /usr/local/lib/perl/5.6.1/DateTime.pm: if ( $self->{tz}->is_floatin +g xor $was_floating )

    --
    3dan