Don’t use < in programming

I don’t actually mean that you shouldn’t use <, but I’m playing off Don’t use the greater than sign in programming. That post from Lewellyn Falco reminded me of a trick for a conditional that checks if a number is inside or outside of a range. A C programmer pointed this out to me years and years ago. It’s not an uncommon trick, but it’s not one I think about much. And, in Perl 6, there’s another way to do the same thing.

To convey intent, he constructed his comparisons so both uses of the variable were either on the inside or on the outside. That way, the position of the variable in the comparisons showed where he intended its value to be. Here’s that expressed in C:

/* x between 5 and 10 */
if( 5 < x && x < 10 ) { ... }

/* x not between 5 and 10 */
if( x <= 5 && 10 <= x ) { ... }

The lowest value is left-most and the greatest value is right-most. Those might be literals or variables. For this to work, the comparators have to be the same. Flip one of them and it might never work because the combined conditions don't mean what they superficially appear to mean:

if( 5 < x && 10 < x ) { ... }  /* Just x > 10 really */

Even with this trick of placing the variable in the literal code, thi still annoys me because I have to specify the variable twice.

Python 2 lets you do it with a chained comparison:

#!/usr/bin/python

x = 6

if 5 < x < 10 :
	print "It's between 5 and 10"
else:
	print "It's outside the range"

Python didn't invent this feature. Various resources that credit BCPL curiously link to a Perl 6 RFC. Odd. I think they are all pulling from the same source. You can do it in Ruby if you redefine some operators. CoffeeScript has it. Other languages have it.

But, this is a blog about Perl 6, which can do this too. This is one of the more pleasing small changes that I really like:

my $x = 6;

if 5 < $x < 10 {
	put "x is between 5 and 10";
	}
else {
	put "x is not in range";
	}

But, Perl 6 goes one better. If you want to test that something is within a range, test that it's in a Range. To exclude the endpoints, you need the ^ characters around the ..:

if $x ~~ 5^..^10 {
	put "x is between 5 and 10";
	}
else {
	put "x is not in range";
	}

This comparison really is the same thing. Instead of checking that $x is in the set 6,7,8,9 (as you might expect from turning the Range into a list) it tests that the value of $x as a Real is between the endpoints. So, 5.5 and 9.999 are within that Range. That's the same behavior as the previous examples.

Perl 6 doesn't stop there. You can make the chain as long as you like:

if 1 < 2 < 3 < 4 < 5 < 6 < 7 {
	say "That's a long chain!";
	}

But, so far Perl 6 does this comparison with limited precision by default. To get arbitrary precision, put your floating point number inside the allomorph-producing <> to get a RatStr instead of a Rat:

> my @a = 5^..^10;
[6 7 8 9]
> 9.999999 ~~ 5^..^10
True
> 9.9999999999999999999999 ~~ 5^..^10
True
> 9.99999999999999999999999 ~~ 5^..^10
False
> <9.99999999999999999999999> ~~ 5^..^10
True

You can read more about this issue in Some things that are less than 5 aren't.

One comment

  1. /* x not between 5 and 10 */
    if( x <= 5 && 10 <= x ) { … }

    I'm thinking you may have meant || instead of &&. I'm wondering, also, what the p6 idiom is for "outside of this range." If "5 <= x x > 10″ means “x is outside 5 and 10”. I’m assuming that this example quoted above would be done something like “! ( $x ~~ 5..10 )” as one of the more concise / idiomatic methods available?

Leave a Reply

Your email address will not be published. Required fields are marked *