[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

NoMethodError in Range#include?

Levin Alexander

7/12/2005 11:33:00 PM

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


11 Answers

Levin Alexander

7/12/2005 11:36:00 PM

0

Levin Alexander <levin@grundeis.net> wrote:

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

This is supposed to read "false", sorry for the typo.

-Levin


Luke Worth

7/13/2005 1:06:00 AM

0

On Wed, 2005-07-13 at 08:32 +0900, Levin Alexander wrote:
> 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
--
Luke Worth




Levin Alexander

7/13/2005 2:56:00 PM

0

Luke Worth <luke@worth.id.au> wrote:

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

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

But I think Range#include? should catch the Exception and return false
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
additional Array)

-Levin


Robert Klemme

7/13/2005 3:48:00 PM

0

Levin Alexander wrote:
> Luke Worth <luke@worth.id.au> wrote:
>
>>> $ ruby -e "puts(('a'..'z').include?(1))"
>>> -e:1:in `include?': undefined method `>' for false:FalseClass
>>> (NoMethodError)
>
>> I think this has something to do with the implementation of
>> Range#include? (being that it returns rng.start <= val <= rng.end)
>
> But I think Range#include? should catch the Exception and return false
> 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
> 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

Levin Alexander

7/13/2005 5:00:00 PM

0

Robert Klemme <bob.news@gmx.net> wrote:

>>> 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
>> additional Array)
>
> 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 <= x <= high

(c)
> - True, if low < x <= high

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

(a) is not really practical because Range is not always Enumerable and
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

Robert Klemme

7/13/2005 8:29:00 PM

0

Levin Alexander <levin@grundeis.net> wrote:
> Robert Klemme <bob.news@gmx.net> wrote:
>
>>>> 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
>>> additional Array)
>>
>> 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 <= x <= high
>
> (c)
>> - True, if low < x <= high
>
> 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:

>> (1.5 .. 3.4).to_a
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.

>> 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=

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

Kind regards

robert

Charles Mills

7/13/2005 9:47:00 PM

0

Levin Alexander wrote:
> 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

Levin Alexander

7/13/2005 10:23:00 PM

0

Robert Klemme <bob.news@gmx.net> wrote:

>> 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.

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

>> (a) is not really practical because Range is not always Enumerable and
>> this also conflicts with my mental model of a "Range"
>
> Right:
>
>>> (1.5 .. 3.4).to_a
> 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.

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 : []

-Levin


Levin Alexander

7/13/2005 10:24:00 PM

0

Charles Mills <cmills@freeshell.org> wrote:

> 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.

Thank you for this detailed explanation.

I worked around the problem by catching NoMethodError in my code that uses
Range#include?

-Levin


Robert Klemme

7/14/2005 8:26:00 AM

0

Levin Alexander wrote:
> Robert Klemme <bob.news@gmx.net> wrote:
>
>>> 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.
>
> But Ranges have many other uses which depend on it being Enumerable

Yeah, certainly.

>>> (a) is not really practical because Range is not always Enumerable
>>> and this also conflicts with my mental model of a "Range"
>>
>> Right:
>>
>>>> (1.5 .. 3.4).to_a
>> 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