NoMethodError in Range#include?

L

Levin Alexander

Hi,

I just got an Exception that I found somewhat confusing:

$ ruby -e "puts((1..10).include?('a'))"
true

$ ruby -e "puts(('a'..'z').include?(1))"
-e:1:in `include?': undefined method `>' for false:FalseClass =20
(NoMethodError)
from -e:1

$ ruby -v
ruby 1.8.2 (2004-12-23) [i386-linux]

Is there a reason for this behaviour?

Thank You,
Levin
 
L

Luke Worth

Hi,

I just got an Exception that I found somewhat confusing:

$ ruby -e "puts((1..10).include?('a'))"
false

$ ruby -e "puts(('a'..'z').include?(1))"
-e:1:in `include?': undefined method `>' for false:FalseClass
(NoMethodError)
from -e:1

$ ruby -v
ruby 1.8.2 (2004-12-23) [i386-linux]

Is there a reason for this behaviour?

I think this has something to do with the implementation of
Range#include? (being that it returns rng.start <= val <= rng.end)
An easy way around it is to use ('a'..'z').entries.include?(1) instead
 
L

Levin Alexander

I think this has something to do with the implementation of
Range#include? (being that it returns rng.start <=3D val <=3D rng.end)

But I think Range#include? should catch the Exception and return false =20
instead.
An easy way around it is to use ('a'..'z').entries.include?(1) instead

That does not work when Range#start is a Float (and it builds an =20
additional Array)

-Levin
 
R

Robert Klemme

Levin said:
But I think Range#include? should catch the Exception and return false
instead.


That does not work when Range#start is a Float (and it builds an
additional Array)

Also, there are several equally valid interpretations of Range#include?:

- True, if the range while iterating yields the specific value (std
definition from Enumerable)

- True, if low <= x <= high

- True, if low < x <= high

....

So maybe Range#include? is not that useful after all... Or we should
leave the std definition from Enumerable in place and introduce
Range#contains? or Range#covers? to deal with the more range specific
interpretations.

Kind regards

robert
 
L

Levin Alexander

Robert Klemme said:
Also, there are several equally valid interpretations of Range#include?= :

(a)
- True, if the range while iterating yields the specific value (std
definition from Enumerable)
(b)
- True, if low <=3D x <=3D high
(c)
- True, if low < x <=3D high

Interesting. I would lean towards (b) (which I believe is the current =20
implementation if exclude_end? is false).

(a) is not really practical because Range is not always Enumerable and =20
this also conflicts with my mental model of a "Range"
So maybe Range#include? is not that useful after all... Or we should
leave the std definition from Enumerable in place and introduce
Range#contains? or Range#covers? to deal with the more range specific
interpretations.

A little more context on what I was trying to do:

I'm writing a class to filter bytestreams according to various parameters=
 
R

Robert Klemme

Levin Alexander said:
Interesting. I would lean towards (b) (which I believe is the current
implementation if exclude_end? is false).

In that case the inclusion of Enumerable becomes questionable.
(a) is not really practical because Range is not always Enumerable and
this also conflicts with my mental model of a "Range"
Right:
TypeError: cannot iterate from Float
from (irb):14:in `each'
from (irb):14:in `to_a'
from (irb):14

We have the weired situation that a Range where left and right are
enumerable (integers, strings) the inclusion of Enumerable is ok and for non
enumerable types (Float) it's not. That's a bit weired situation IMHO, at
least not exactly good design.
A little more context on what I was trying to do:

I'm writing a class to filter bytestreams according to various
parameters=

Huh? Sent too early? I'm missing the rest of the sentence here.

Kind regards

robert
 
C

Charles Mills

Levin said:
Hi,

I just got an Exception that I found somewhat confusing:

$ ruby -e "puts((1..10).include?('a'))"
true

$ ruby -e "puts(('a'..'z').include?(1))"
-e:1:in `include?': undefined method `>' for false:FalseClass
(NoMethodError)
from -e:1

$ ruby -v
ruby 1.8.2 (2004-12-23) [i386-linux]

Is there a reason for this behaviour?

Thank You,
Levin

This is a bug with the String#<=>. It has been discussed on Ruby talk
before, but at some point something similar to the following is
happening:
irb(main):001:0> 'a' <=> 1
=> false
irb(main):002:0> ('a' <=> 1) > 0
NoMethodError: undefined method `>' for false:FalseClass
from (irb):2

'a' <=> 1 should return nil in this situation.

See:
range_include() in range.c
rb_str_cmp_m() in string.c
rb_cmpint() in compar.c

I think this is changed in Ruby 1.9, but you could do something like
the following:

$ cat s.rb
p 'a' <=> 1
class String
alias old_cmp <=>
def <=>(v)
return nil unless v.is_a? String
return old_cmp(v)
end
end
p 'a' <=> 1
p ('a'..'z').include?(1)

$ ruby s.rb
false
nil
false

$ ruby -v
ruby 1.8.2 (2004-12-25) [i386-cygwin]

-Charlie
 
L

Levin Alexander

Robert Klemme said:
In that case the inclusion of Enumerable becomes questionable.

But Ranges have many other uses which depend on it being Enumerable
TypeError: cannot iterate from Float
from (irb):14:in `each'
from (irb):14:in `to_a'
from (irb):14

We have the weired situation that a Range where left and right are =20
enumerable (integers, strings) the inclusion of Enumerable is ok and fo=
r

A range is only Enumerable if the left side defines +succ+
(0..Math::pI).to_a =3D> [0, 1, 2, 3]
class Float; def succ; self+0.8; end; end; =3D> nil
(0.1..2.0).to_a
=3D> [0.1, 0.9, 1.7]
non enumerable types (Float) it's not. That's a bit weired situation =20
IMHO, at least not exactly good design.

I don't believe that it is much of a problem in practice.

Maybe Range should have a method enumerable? so that you can check if eac=
h =20
is going to work before you actually use it.

class Range
def enumerable?
first.respond_to?:)succ)
end
end

r =3D (1..10)
arr =3D r.enumerable? ? r.entries : []

-Levin
 
L

Levin Alexander

Charles Mills said:
This is a bug with the String#<=3D>. It has been discussed on Ruby tal= k
before, but at some point something similar to the following is
happening:
irb(main):001:0> 'a' <=3D> 1
=3D> false
irb(main):002:0> ('a' <=3D> 1) > 0
NoMethodError: undefined method `>' for false:FalseClass
from (irb):2

'a' <=3D> 1 should return nil in this situation.

Thank you for this detailed explanation.

I worked around the problem by catching NoMethodError in my code that use=
s =20
Range#include?

-Levin
 
R

Robert Klemme

Levin said:
But Ranges have many other uses which depend on it being Enumerable

Yeah, certainly.
TypeError: cannot iterate from Float
from (irb):14:in `each'
from (irb):14:in `to_a'
from (irb):14

We have the weired situation that a Range where left and right are
enumerable (integers, strings) the inclusion of Enumerable is ok and
for

A range is only Enumerable if the left side defines +succ+
(0..Math::pI).to_a => [0, 1, 2, 3]
class Float; def succ; self+0.8; end; end; => nil
(0.1..2.0).to_a
=> [0.1, 0.9, 1.7]
non enumerable types (Float) it's not. That's a bit weired situation
IMHO, at least not exactly good design.

I don't believe that it is much of a problem in practice.

Yeah, probably. Since generally Ruby favours pragmatic solutions over
formally more correct solutions we probably should leave it as is. In
fact, I haven't tripped into that trap yet. :)
Maybe Range should have a method enumerable? so that you can check if
each is going to work before you actually use it.

class Range
def enumerable?
first.respond_to?:)succ)
end
end

r = (1..10)
arr = r.enumerable? ? r.entries : []

Yeah, probably. Though I would not dare to guess how often this is really
needed...

Kind regards

robert
 
N

nobu.nokada

Hi,

At Wed, 13 Jul 2005 08:32:49 +0900,
Levin Alexander wrote in [ruby-talk:147929]:
$ ruby -v
ruby 1.8.2 (2004-12-23) [i386-linux]

In recent versions, both return false.
 

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
474,432
Messages
2,571,680
Members
48,796
Latest member
Greg L.

Latest Threads

Top