Chaining comparisons

When I learned python I was overjoyed that I could evaluate 1 < 2 < 3
and get “true”. I just realized that you can’t do that in Ruby. Is
there a reason why? Is it good? I know I can use “between”, but
still…

-Kurt

I don’t know if there’s a good reason for it, but I think it’d be cool if
Ruby had it.

···

On Sat, Jul 19, 2003 at 02:58:51PM +0900, Kurt M. Dresner wrote:

When I learned python I was overjoyed that I could evaluate 1 < 2 < 3
and get “true”. I just realized that you can’t do that in Ruby. Is
there a reason why? Is it good? I know I can use “between”, but
still…

-Kurt


Daniel Carrera | OpenPGP fingerprint:
Mathematics Dept. | 6643 8C8B 3522 66CB D16C D779 2FDD 7DAC 9AF7 7A88
UMD, College Park | http://www.math.umd.edu/~dcarrera/pgp.html

I agree it would be cool, but it’s pretty clear why Ruby doens’t
support it:

        2 < 3              == true
        1 < true           == error

therefore 1 < 2 < 3 == error

Ruby is a very expression-oriented language, and derives its strength
from conceptual purity. If an expression evaluated to X in some
circumstances and Y in others, a small part of Ruby would be lost.

Gavin

···

On Saturday, July 19, 2003, 3:58:51 PM, Kurt wrote:

When I learned python I was overjoyed that I could evaluate 1 < 2 < 3
and get “true”. I just realized that you can’t do that in Ruby. Is
there a reason why? Is it good? I know I can use “between”, but
still…

While I was writing this message, Phil responded with some similar
thoughts, but I will post anyway, with apologies for the areas of
duplication.

A few of thoughts:

  1. It seems that in python ‘1 < 2 < 3’ is sugar for ‘1 is less than 2
    and 2 is less than 3’.

  2. How does python handle ‘1 < 2 < 3 < 4’? How does python handle ‘1 <
    2 or 5 < 4’?

  3. In Ruby 1 < 2 is a representation of the Fixnum object 1 calling the
    ‘<’ method with argument Fixnum object 2. The object returned is ‘true’
    (an object of the TrueClass). In the expression ‘1 < 2 < 3’, the ‘true’
    object calls the ‘<’ method with argument ‘3’, an a no method error is
    raised.

  4. You can write the expression in Ruby as ‘1 < 2 and 2 < 3’.

  5. You could also modify the ‘<’ method to return the value of the
    argument given if it evaluates to ‘true’ – however, you would have to
    parse the output so that the last comparison returns ‘true’ and not the
    last argument. The following just modifies the return value of ‘<’ and
    does not handle the problem of the method chain now returning the last
    argument:

class Fixnum
alias old_less_than <
def < (arg)
if self.old_less_than(arg)
return arg
end
end
end

1 < 2 < 3 => 3

  1. You could also parse input so that ‘1 < 2 < 3’ is translated to ‘1 <
    2 and 2 < 3’. I haven’t thought about how to do this from within Ruby.

  2. I prefer the explicit ‘1 < 2 and 2 < 3’.

Regards,

Mark

···

On Saturday, July 19, 2003, at 01:58 AM, Kurt M. Dresner wrote:

When I learned python I was overjoyed that I could evaluate 1 < 2 < 3
and get “true”. I just realized that you can’t do that in Ruby. Is
there a reason why? Is it good? I know I can use “between”, but
still…

-Kurt

When I learned python I was overjoyed that I could evaluate 1 < 2 < 3
and get “true”. I just realized that you can’t do that in Ruby. Is
there a reason why? Is it good? I know I can use “between”, but
still…

You can do this:

if [1, 2, 3].ordered?
puts “ok”
end

It will require that you extend the Array class yourself, like this:

expand -t4 b.rb
class Array
def ordered?
return true if self.empty?
a = self.first
self[1…-1].each { |b|
return false if a > b
a = b
}
true
end
end

p .ordered? #=> true
p [2].ordered? #=> true
p [-1, 2, 3, 5].ordered? #=> true
p [17, 13, 11].ordered? #=> false
p [1, -1, 1, 0].ordered? #=> false

The output is:

ruby b.rb
true
true
true
false
false

···

On Sat, 19 Jul 2003 15:58:51 +0900, Kurt M. Dresner wrote:


Simon Strandgaard

You can also use range objects for a clean alternative to a <= b <= c :

x = 2 # or 1 or 3
if (1…3) === x
puts “It’s in the range”
end

The strange ‘backwards’ ordering is to support case statements, which make
it look much clearer:

case x
when (1…3)
puts “It’s in the lower range”
when (4…6)
puts “It’s in the upper range”
else
puts “It’s out of range”
end

In the above, 3.5 is also ‘out of range’. But the three-dot form of range
supports a <= b < c

puts “nope” if (1…3) === 3.0 # false
puts "yep " if (1…3) === 2.99 # true

case x
when (1…4) # 1 to 3.999
when (4…7) # 4 to 6.999
when (7…10) # 7 to 9.999
end

Cheers,

Brian.

···

On Sat, Jul 19, 2003 at 02:58:51PM +0900, Kurt M. Dresner wrote:

When I learned python I was overjoyed that I could evaluate 1 < 2 < 3
and get “true”. I just realized that you can’t do that in Ruby. Is
there a reason why? Is it good? I know I can use “between”, but
still…

Hi –

···

On Sat, 19 Jul 2003, Kurt M. Dresner wrote:

When I learned python I was overjoyed that I could evaluate 1 < 2 < 3
and get “true”. I just realized that you can’t do that in Ruby. Is
there a reason why? Is it good? I know I can use “between”, but
still…

Matz’s reason, in rejecting this in RCR form, was, “It’s hard to
define semantics of chain comparison in “OO” way. Use ranges instead.”
(See http://www.rubygarden.org/article.php?sid=286.)

David


David Alan Black
home: dblack@superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

Saluton!

  • Kurt M. Dresner; 2003-07-19, 11:33 UTC:

When I learned python I was overjoyed that I could evaluate 1 < 2 <
3 and get “true”. I just realized that you can’t do that in Ruby.
Is there a reason why?

a < b and b < c imply a < c. But what if you want to evaluate
a < b > c? Which conditons do you require to be met?

(1) a < b, b > c and a < c
(2) a < b, b > c and a > c
(3) a < b, b > c and nothing more

Some examples:

2 < 3 > 1 → (2), (3)
1 < 3 > 2 → (1), (3)
2 < 3 > 2 → (3)

It seems to be better not to use ‘<’ and the like. Better use
methods that can be applied to an arbitrary number of elements:

def increasing(*list)
return true if list.length < 2
y = nil
list.each {|x|
return false unless y.nil? or y < x
y = x
}
true
end

Gis,

Josef ‘Jupp’ Schugt

···


N’attribuez jamais à la malice ce que l’incompétence explique !
– Napoléon

So basically, it’s because it’s hard to implement, even more so because
true/false/nil are singleton objects. (So you can’t, for instance, save state
in a particular instance of ‘true’, because there’s only one.)

Jason Creighton

···

On Sat, 19 Jul 2003 14:58:51 +0900 “Kurt M. Dresner” kdresner@cs.utexas.edu wrote:

When I learned python I was overjoyed that I could evaluate 1 < 2 < 3
and get “true”. I just realized that you can’t do that in Ruby. Is
there a reason why? Is it good? I know I can use “between”, but
still…

I realize that this is a dumb question, but what is an expression-oriented
language?

Can you contrast Ruby with a language that is not expression-oriented?


Daniel Carrera | OpenPGP fingerprint:
Mathematics Dept. | 6643 8C8B 3522 66CB D16C D779 2FDD 7DAC 9AF7 7A88
UMD, College Park | http://www.math.umd.edu/~dcarrera/pgp.html

···

On Sat, Jul 19, 2003 at 03:47:38PM +0900, Gavin Sinclair wrote:

I agree it would be cool, but it’s pretty clear why Ruby doens’t
support it:

        2 < 3              == true
        1 < true           == error

therefore 1 < 2 < 3 == error

Ruby is a very expression-oriented language, and derives its strength
from conceptual purity. If an expression evaluated to X in some
circumstances and Y in others, a small part of Ruby would be lost.

Yeah, I guess that’s a pretty good reason. Oh well, I’ll just use
“between” from now on :o)

-Kurt

···

On Sat, Jul 19, 2003 at 03:47:38PM +0900, Gavin Sinclair wrote:

On Saturday, July 19, 2003, 3:58:51 PM, Kurt wrote:

When I learned python I was overjoyed that I could evaluate 1 < 2 < 3
and get “true”. I just realized that you can’t do that in Ruby. Is
there a reason why? Is it good? I know I can use “between”, but
still…

I agree it would be cool, but it’s pretty clear why Ruby doens’t
support it:

        2 < 3              == true
        1 < true           == error

therefore 1 < 2 < 3 == error

Ruby is a very expression-oriented language, and derives its strength
from conceptual purity. If an expression evaluated to X in some
circumstances and Y in others, a small part of Ruby would be lost.

Gavin

======= End of Original Message =======<

In article 20030719060149.GA2247@math.umd.edu,

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

I don’t know if there’s a good reason for it, but I think it’d be cool if
Ruby had it.

When I learned python I was overjoyed that I could evaluate 1 < 2 < 3
and get “true”. I just realized that you can’t do that in Ruby. Is
there a reason why? Is it good? I know I can use “between”, but
still…

I can see how it would be cool, however:

1 < 2 #this gives a value of true
1 < 2 < 3 #this calls the ‘<(3)’ method on true

It essentially looks like:
(true) < 3

Which is meaningless.

to do this you’ve got to somehow change the parser so that for the special cases of
<,>,<=,>= you instead call the method on the middle value in the chain of thee values.
Or you’ve got to somehow make ‘1 < 2’ return 2 instead of/or in addition to true.
Seems problematic and could very likely break things.

2.between?(1,3)
May not look as pretty, but works fine without potential parser headaches.

Phil

···

Daniel Carrera dcarrera@math.umd.edu wrote:

On Sat, Jul 19, 2003 at 02:58:51PM +0900, Kurt M. Dresner wrote:

One way to do it would be to have ‘if’ call to_boolean on its argument,
and have < return an object that carried some state around.

martin

···

Gavin Sinclair gsinclair@soyabean.com.au wrote:

On Saturday, July 19, 2003, 3:58:51 PM, Kurt wrote:

When I learned python I was overjoyed that I could evaluate 1 < 2 < 3
and get “true”. I just realized that you can’t do that in Ruby. Is
there a reason why? Is it good? I know I can use “between”, but
still…

I agree it would be cool, but it’s pretty clear why Ruby doens’t
support it:

       2 < 3              == true
       1 < true           == error

therefore 1 < 2 < 3 == error

Ruby is a very expression-oriented language, and derives its strength
from conceptual purity. If an expression evaluated to X in some
circumstances and Y in others, a small part of Ruby would be lost.

self == sort
end
end

(I’m a lazy typist :slight_smile:

Both versions generate a temporary copy of the array, e.g. self[1…-1] does
that too. But you can avoid it:

module Enumerable
def ordered?
first = true
prev = nil
each do |item|
if first
first = false
else
return false if prev > item
end
prev = item
end
return true
end
end

You can then check whether all the lines in a file are ordered, for example,
without reading it into memory. (I am a big fan of Enumerable :slight_smile:

Regards,

Brian.

···

On Sat, Jul 19, 2003 at 05:08:03PM +0900, Simon Strandgaard wrote:

You can do this:

if [1, 2, 3].ordered?
puts “ok”
end

It will require that you extend the Array class yourself, like this:

expand -t4 b.rb
class Array
def ordered?

This (anti?)pattern always bothers me - surely a 2-element sliding
window is a common enough pattern that we should capture it once and for
all in Enumerable.

It’s in Joel VanderWerf’s excellent EnumerableTools, but people tend not
to include external packages when writing quick scripts. For instance,
the current script would simply be

class Array
def ordered?
each_cluster(2){|i,j| return false if i > j}
true
end
end

Personally, I’d like to see an each and an eachn, both taking block
arity into account:

a = *(1…6)
a.eachn {|x,y| p [x,y]} #=> [1,2] [3,4] [5,6]
a.each {|x,y| p [x,y]} #=> [1,2] [2,3] [3,4] [4,5] [5,6]

This would also preserve the semantics of ‘each’ only consuming one
element per iteration.

martin

···

Simon Strandgaard 0bz63fz3m1qt3001@sneakemail.com wrote:

class Array
def ordered?
return true if self.empty?
a = self.first
self[1…-1].each { |b|
return false if a > b
a = b
}
true
end
end

“Mark Wilson” mwilson13@cox.net wrote in

following up on your example. Assuming that <,<=, …
were left associative “comparison chaining” could
work like this

···

class Fixnum
alias __lt <
alias __le <=
alias __gt >
alias __ge >=
def <®
__lt® ? r : nil
end
def <®
__lt® ? r : nil
end
def <=®
__le® ? r : nil
end
def >®
__gt® ? r : nil
end
def >=®
__ge® ? r : nil
end
end

class NilClass
def <® self end
def <=® self end
def >® self end
def >=® self end
end

p ((4 < 5) < 6) < 7 # 7

p ((4 < 5) < 5) < 7 # nil

p ((4 < 5) >= 5) < 7 # 7

Personally I wouldn’t mind such a semantic but it is
probably too wired for general consumption …

/Christoph

Jason Creighton wrote:

···

On Sat, 19 Jul 2003 14:58:51 +0900 > “Kurt M. Dresner” kdresner@cs.utexas.edu wrote:

When I learned python I was overjoyed that I could evaluate 1 < 2 < 3
and get “true”. I just realized that you can’t do that in Ruby. Is
there a reason why? Is it good? I know I can use “between”, but
still…

http://www.rubygarden.org/article.php?sid=286

So basically, it’s because it’s hard to implement, even more so because
true/false/nil are singleton objects. (So you can’t, for instance, save state
in a particular instance of ‘true’, because there’s only one.)

As you say, there is only one true; but you still can save state in it:

irb(main):001:0> true.instance_eval {@x = 1}
1
irb(main):002:0> true.instance_eval {@x}
1

Not that it’s relevant to the 1 < 2 < 3 discussion, but it’s kinda cute…

I agree it would be cool, but it’s pretty clear why Ruby doens’t
support it:

        2 < 3              == true
        1 < true           == error

therefore 1 < 2 < 3 == error

Ruby is a very expression-oriented language, and derives its strength
from conceptual purity. If an expression evaluated to X in some
circumstances and Y in others, a small part of Ruby would be lost.

I realize that this is a dumb question, but what is an expression-oriented
language?

Don’t worry, I’ve never seen/heard that assortment of words either.
What I mean is that Ruby gives primacy to expressions (as opposed to
statements). For instance (not tested):

extractor =
case opt
when “-r” then ReceiptExtractor
when “-t” then TransactionExtractor
end.new(filename)

Every chunk of code has a “return value” that can be used to build
larger expressions.

Can you contrast Ruby with a language that is not expression-oriented?

Try the concepts in the above code in just about any language :slight_smile:

Many/most languages enforce a difference between “statement” and
“expression”. Pascal is an example.

Gavin

···

On Saturday, July 19, 2003, 4:51:42 PM, Daniel wrote:

On Sat, Jul 19, 2003 at 03:47:38PM +0900, Gavin Sinclair wrote:

You can do this:

if [1, 2, 3].ordered?
puts “ok”
end

It will require that you extend the Array class yourself, like this:

expand -t4 b.rb
class Array
def ordered?
self == sort
end
end

(I’m a lazy typist :slight_smile:

Work smarter, Not harder.
I know sort, But this solution didn’t come to mind.

Both versions generate a temporary copy of the array, e.g. self[1…-1] does
that too. But you can avoid it:

Yes. Avoiding temporary copy were my goal when I started writing it.
As you can see I apparently forgot it in the hurry :slight_smile:

[snip enum#ordered?]

You can then check whether all the lines in a file are ordered, for example,
without reading it into memory. (I am a big fan of Enumerable :slight_smile:

Enum is Nice.

Some more thoughts on #ordered?

Supplying an operator, could be useful?

[3, 2, 1].ordered? :> #=> true
[2, 2, 2].ordered? :> #=> false
[2, 2, 2].ordered? :>= #=> true

Supplying an block could also be useful?

[2, 2, 2].ordered? { |a, b| (((a-b) ^ (b-a)) % 3) == 0 }
#=> true

[1, 2, 3].ordered? { |a, b| (((a-b) ^ (b-a)) % 3) == 0 }
#=> false

···

On Sat, 19 Jul 2003 18:41:33 +0900, Brian Candler wrote:

On Sat, Jul 19, 2003 at 05:08:03PM +0900, Simon Strandgaard wrote:


Simon Strandgaard

Hi,

···

In message “Re: chaining comparisons” on 03/07/19, news_chr swap@gmx.net writes:

class Fixnum
alias __lt <
alias __le <=
alias __gt >
alias __ge >=
def <(r)
__lt(r) ? r : nil
end
def <(r)
__lt(r) ? r : nil
end
def <=(r)
__le(r) ? r : nil
end
def >(r)
__gt(r) ? r : nil
end
def >=(r)
__ge(r) ? r : nil
end
end

class NilClass
def <(r) self end
def <=(r) self end
def >(r) self end
def >=(r) self end
end

p ((4 < 5) < 6) < 7 # 7

p ((4 < 5) < 5) < 7 # nil

p ((4 < 5) >= 5) < 7 # 7

Personally I wouldn’t mind such a semantic but it is
probably too wired for general consumption …

They used to work like this long time ago. But I felt the semantic
was too weird.

						matz.