Range#member? Oddity

  • Thread starter James Edward Gray II
  • Start date
L

Logan Capaldo

How about having Range use Object#strict_succ to generate
its sequence? Define String#strict_succ as needed to guarantee
s.succ > s and then alias strict_succ to succ for other classes
(such as Fixnum) so they don't break when used in a Range.


Gary Wright

This seems to me to make the problem worse. People expect the values
generated by String#succ to be in the array when doing a to_a for
instance. I believe the real solution would be to bring back the
distinction between member and include (possibly with a new name for
the method with the functionality of #member).
 
D

David Vallner

On Jan 13, 2006, at 5:54 PM, David Vallner wrote:

I'm pretty sure we don't want to change the meaning of String =20
comparisons at this point. succ() just doesn't happened to be defined = =20
under those terms, because then it would be a lot less useful to us. =20
It's hard for me to see any of that as "broken".

James Edward Gray II


I never said anything the like, the code breakage would be inexcusable.

My point is it's String that behaves erreneously in this context and isn'=
t =20
suitable for use in symbolic ranges, not a bug in Range code - the posts =
=20
were an objection to people wanting to mess up the Range interface.

David Vallner
 
D

David Vallner

This seems to me to make the problem worse. People expect the values =20
generated by String#succ to be in the array when doing a to_a for =20
instance. I believe the real solution would be to bring back the =20
distinction between member and include (possibly with a new name for th= e =20
method with the functionality of #member).


I agree that the change proposed would break Range#to_a for strings, whic=
h =20
I can imagine being used. The real solution would be people coding sanely=
=20
and using Integers to represent integers, and Strings to represent text -=
=20
using unsuitable data types resulting in a bug is arguably not a language=
=20
fault.

David Vallner
 
G

gwtmp01

This seems to me to make the problem worse. People expect the
values generated by String#succ to be in the array when doing a
to_a for instance. I believe the real solution would be to bring
back the distinction between member and include (possibly with a
new name for the method with the functionality of #member).

It seems strange to want Range to behave like an interval and to
also want Range#to_a to create a list of elements that don't all
belong in that same interval.

I understand the desire to generate a sequence of strings
as defined by the current behavior of String#succ but I don't understand
why you would want to use a Range object as a shortcut to that sequence.
That particular sequence really has nothing to do with an interval
or the ordering defined by String#<=>


Gary Wright
 
D

David Vallner

It seems strange to want Range to behave like an interval and to
also want Range#to_a to create a list of elements that don't all
belong in that same interval.

It's a Convenient Hack (tm), and those are extremely hard to weed out onc=
e =20
they catch hold. It's the same as when people use #eval that must a =20
nightmare to the brave folks working on optimizing YARV (I want my clean =
=20
blocks!) instead of the much cleaner metaprogramming facilities in Ruby =20
that let you achieve 99% of what I've seen hacked with evals anyway. (Ver=
y =20
obfuscated eval hacks notwithstanding).

David Vallner
 
L

Logan Capaldo

--Apple-Mail-1--18436058
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
charset=US-ASCII;
delsp=yes;
format=flowed


I understand the desire to generate a sequence of strings
as defined by the current behavior of String#succ but I don't
understand
why you would want to use a Range object as a shortcut to that
sequence.
That particular sequence really has nothing to do with an interval
or the ordering defined by String#<=>

As far as I can tell two classes(Well 4ish) have #succ methods.
Integers and Strings. For all integers x, x has the property that
x.succ > x is true.
There exists at least one String x for which x.succ > x is true, and
at least one String x for which x.succ > x is false. So the question
is, what is a Range? Is it a pair of end-points for checking between-
ness? Is it the set of all values between those end points? If it is
the second, I would argue that #to_a is meaningless for strings
because there are end points for which the set would have infinite
cardinality (i.e. ("a".."b"), the set of strings matched by /^a+\z/
at least is in that "range".).
--Apple-Mail-1--18436058--
 
B

Bertram Scharpf

Hi,

Am Samstag, 14. Jan 2006, 05:49:56 +0900 schrieb James Edward Gray II:
I'm not understanding what I am seeing here. Can anyone please
explain why the last line of this session gives *false* as an answer?
range = ("1".."10") => "1".."10"
range.to_a => ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
range.member?("1") => true
range.member?("2")
=> false

irb(main):006:0> 2 < 10
=> true
irb(main):007:0> "2" < "10"
=> false

Bertram
 
H

Hal Fulton

James said:
I'm not understanding what I am seeing here. Can anyone please explain
why the last line of this session gives *false* as an answer?
range = ("1".."10") => "1".."10"
range.to_a => ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
range.member?("1") => true
range.member?("2")
=> false

In short: Don't do this.

String ranges are very weird. They don't obey the rule that
x.succ > x.

Since "2" > "10", "2" is considered outside the range entirely.

If you really want to do this kind of thing, say
("1".."10").to_a.member?("2")


Hal
 
C

Christer Nilsson

Hal said:
In short: Don't do this.

String ranges are very weird. They don't obey the rule that
x.succ > x.

Since "2" > "10", "2" is considered outside the range entirely.

If you really want to do this kind of thing, say
("1".."10").to_a.member?("2")


Hal

If the two strings have the same length, the weirdness disappears.

Christer
 
D

dblack

Hi --

If the two strings have the same length, the weirdness disappears.

But other weirdnesses arise :)

irb(main):019:0> ("A".."z").member?("g")
=> true
irb(main):020:0> ("A".."z").to_a.include?("g")
=> false


David

--
David A. Black
(e-mail address removed)

"Ruby for Rails", from Manning Publications, coming April 2006!
http://www.manning.com/books/black
 
S

Stefan Walk

But other weirdnesses arise :)

irb(main):019:0> ("A".."z").member?("g")
=> true
irb(main):020:0> ("A".."z").to_a.include?("g")
=> false


David

IMO, the thing at fault here is that Ranges include Enumerable. From a
mathematical POV, that's nonsense, and that's why oddities like this
exist. Things that can be used for ranges can not used like an
Enumarable (rational..rational, float..float), because those don't have
#succ instance methods... and String ranges behave oddly because the
member? checks use the <=> behaviour, and each uses succ...

Regards,
Stefan
 
S

Stefan Walk

On Sat, 14 Jan 2006, Stefan Walk wrote:
but enumerable means neither monotonically increasing nor finite. so
that a
range includes enumerable means only that one may start, but perhaps never
finish, to count the elements in the range. i think this is accurate isn't
it?

cheers.

-a

Enumerable means that you can map the natural numbers to your set in an
invertable way, and that's not possible for ranges of real numbers.
Being enumerable means the possibility of a "succ" operation, and
therefore of traversing the whole set with "each" (which may take
infinite time). Being enumerable implies a working each, now try
(1.0..2.0).each...

Regards,
Stefan
 
D

David Vallner

Enumerable means that you can map the natural numbers to your set in an= =20
invertable way, and that's not possible for ranges of real numbers. =20
Being enumerable means the possibility of a "succ" operation, and =20
therefore of traversing the whole set with "each" (which may take =20
infinite time). Being enumerable implies a working each, now try =20
(1.0..2.0).each...

Regards,
Stefan


There's nothing wrong with enumerating ranges, you can well have interval=
s =20
of natural numbers or other countably infinite sets. Bounded intervals of=
=20
countable or countably infinite sets are finite sets and may be (not =20
necessarily though) enumerated in order using the successor operation, =20
which exists if the elements of the interval are well-ordered. It's not =20
exactly an edge case, and distinguishing between ranges with finite and =20
infinite element counts

The issue with strings is that their ordering does _not_ make them such a=
=20
well-ordered countably infinite set. Because strings are compared using =20
textual comparison, as was already noted in this thread, for every two =20
given strings, there exists an infinite amount of strings between them. =20
For the sake of completion, strings in fact _are_ a countably infinite =20
set, but their ordering doesn't respect any mapping of strings to natural=
=20
numbers.

Given this, the string successor operation doesn't even make sense from =20
the mathemathical point of view. It is in this context defined ad hoc, an=
d =20
it's usefulness lies in contexts of text processing.

Using a string Range is a convenient shortcut, but it's a _hack_. Nothing=
=20
more, nothing less. Strings don't have a solid foundation for use in =20
maths. If you expect strings to behave as numbers, you're wrong, they're =
=20
not supposed to, any bugs are your fault, not the fault of the Ruby core =
=20
API. Moan and suffer.

David Vallner
 
S

Stefan Walk

David said:
There's nothing wrong with enumerating ranges, you can well have
intervals of natural numbers or other countably infinite sets. Bounded
intervals of countable or countably infinite sets are finite sets and
may be (not necessarily though) enumerated in order using the successor
operation, which exists if the elements of the interval are
well-ordered. It's not exactly an edge case, and distinguishing between
ranges with finite and infinite element counts

Your messages seems to have been truncated here. Also, you're wrong,
bounded intervals of countably infinite sets do not have to be finite.
Consider [0,1] in the rational numbers. Rational numbers are countable,
the range is bounded, but the set is not finite (it is still countably
infinite).
Using a string Range is a convenient shortcut, but it's a _hack_.
Nothing more, nothing less. Strings don't have a solid foundation for
use in maths. If you expect strings to behave as numbers, you're wrong,
they're not supposed to, any bugs are your fault, not the fault of the
Ruby core API. Moan and suffer.

David Vallner

Range including Enumerable is a fault of the ruby core API, because
(1.0..20) pretends to be enumerable (is_a? returns true, responds_to?
:each returns true), but is not.

Regards, Stefan (who never had problems with string ranges, only with
Range including Enumerable)
 
R

Robert Retzbach

James said:
I'm not understanding what I am seeing here. Can anyone please
explain why the last line of this session gives *false* as an answer?
range = ("1".."10") => "1".."10"
range.to_a => ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
range.member?("1") => true
range.member?("2")
=> false

James Edward Gray II
I don't know why to_a is working here.
I know that a range from '1' to '10' could be like you described, but it
doesn't make a logical sense I think.
like ('a'..'ah') what should that be?
[a-255.chr] and all a[0.chr-255.chr]?

This is what I think, of course.
Dunno if it makes sense :>

MfG
Retze
 
D

David Vallner

[snip]

Range including Enumerable is a fault of the ruby core API, because =20
(1.0..20) pretends to be enumerable (is_a? returns true, responds_to? =20
:each returns true), but is not.

That is, of course, misleading, but ranges are very commonly used in the =
=20
special cases when they are indeed enumerable, so I can understand why =20
it's defined that way in the core API. Library design isn't there to =20
replace programmer brains. You could theorethically check whether the =20
range endpoints are comparable, and whether the starting one defines =20
#succ, and then mix in Enumerable to the Ranges that indeed are to get =20
more strictly correct behaviour.

David Vallner
 
A

ara.t.howard

Enumerable means that you can map the natural numbers to your set in an
invertable way, and that's not possible for ranges of real numbers. Being
enumerable means the possibility of a "succ" operation, and therefore of
traversing the whole set with "each" (which may take infinite time). Being
enumerable implies a working each, now try (1.0..2.0).each...

that is certainly true. however, there are no real numbers in computing -
only rational numbers and rationals are countably infinite

http://mathworld.wolfram.com/CountablyInfinite.html
http://mathworld.wolfram.com/RationalNumber.html

while (1.0..2.0).each{|x| p x} cannot work with real numbers it can certainly
work with a set of rational numbers - in computing terms you can certainly
count from 1.0 to 2.0 by adding the floating point epsilon. my opinion is
that we're talking computer languages here, not mathematicaly ones, and so any
idea of being 'correct' must be in the context of what computers can do.

in any case, ruby makes no guarantee that

(a..b).each{|x| p x}

will complete, so even if we had real numbers that would still be a valid
construct - though mixing in Enumerable into Range would be strange then - but
it not the case.

cheers.

-a
 
L

Logan Capaldo

--Apple-Mail-1-54650762
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
charset=US-ASCII;
delsp=yes;
format=flowed


in any case, ruby makes no guarantee that

(a..b).each{|x| p x}

will complete, so even if we had real numbers that would still be a
valid
construct - though mixing in Enumerable into Range would be strange
then - but
it not the case.

Here's a thought, get rid of #each and Enumerable from Range, but add
in a #to_enum method (no args) that would return an Enumerator for
the range. This way programmers could say, "I solemnly swear that I
know iterating thru a range of strings doesn't make sense with
include, but I want to do it anyway."

("ab".."xyz").to_enum.to_a # etc...

On second thought, that looks unnecessary.

How about:

% cat a.rb
("abc".."wxyz").to_a

% ruby -w a.rb
warning: You fool! By doing this you know include? will make no sense!

Maybe not.

Stupid ranges. Hmph.


--Apple-Mail-1-54650762--
 
A

ara.t.howard

You could theorethically check whether the range endpoints are comparable,
and whether the starting one defines #succ, and then mix in Enumerable to
the Ranges that indeed are to get more strictly correct behaviour.

but that prevents me from doing this

harp:~ > cat a.rb
require "yaml"

e = Float::EPSILON
r = 1.0 .. 1.0000000000000091038288019262836314737796783447265625

class Float
def succ() self + EPSILON end
end

a = r.to_a

y "r.min" => ("%52.52f" % r.min)
y "r.max" => ("%52.52f" % r.max)
y "r.to_a.size" => r.to_a.size



harp:~ > ruby a.rb
---
r.min: "1.0000000000000000000000000000000000000000000000000000"
---
r.max: "1.0000000000000091038288019262836314737796783447265625"
---
r.to_a.size: 42


i can easily imagine uses for code just like that. i think it's best if ruby
will let us run quickly with knives.

regards.

-a
 

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

Forum statistics

Threads
473,773
Messages
2,569,594
Members
45,120
Latest member
ShelaWalli
Top