Range and the mysteries that it covers going out of my range - ruby

I am trying to understand how range.cover? works and following seems confusing -
("as".."at").cover?("ass") # true and ("as".."at").cover?("ate") # false
This example in isolation is not confusing as it appears to be evaluated dictionary style where ass comes before at followed by ate.
("1".."z").cover?(":") # true
This truth seems to be based on ASCII values rather than dictionary style, because in a dictionary I'd expect all special characters to precede even digits and the confusion starts here. If what I think is true then how does cover? decide which comparison method to employ i.e. use ASCII values or dictionary based approach.
And how does range work with arrays. For example -
([1]..[10]).cover?([9,11,335]) # true
This example I expected to be false. But on the face of it looks like that when dealing with arrays, boundary values as well as cover?'s argument are converted to string and a simple dictionary style comparison yields true. Is that correct interpretation?
What kind of objects is Range equipped to handle? I know it can take numbers (except complex ones), handle strings, able to mystically work with arrays while boolean, nil and hash values among others cause it to raise ArgumentError: bad value for range

Why does ([1]..[10]).cover?([9,11,335]) return true
Let's take a look at the source. In Ruby 1.9.3 we can see a following definition.
static VALUE
range_cover(VALUE range, VALUE val)
{
VALUE beg, end;
beg = RANGE_BEG(range);
end = RANGE_END(range);
if (r_le(beg, val)) {
if (EXCL(range)) {
if (r_lt(val, end))
return Qtrue;
}
else {
if (r_le(val, end))
return Qtrue;
}
}
return Qfalse;
}
If the beginning of the range isn't lesser or equal to the given value cover? returns false. Here lesser or equal to is determined in terms of the r_lt function, which uses the <=> operator for comparison. Let's see how does it behave in case of arrays
[1] <=> [9,11,335] # => -1
So apparently [1] is indeed lesser than [9,11,335]. As a result we go into the body of the first if. Inside we check whether the range excludes its end and do a second comparison, once again using the <=> operator.
[10] <=> [9,11,335] # => 1
Therefore [10] is greater than [9,11,335]. The method returns true.
Why do you see ArgumentError: bad value for range
The function responsible for raising this error is range_failed. It's called only when range_check returns a nil. When does it happen? When the beginning and the end of the range are uncomparable (yes, once again in terms of our dear friend, the <=> operator).
true <=> false # => nil
true and false are uncomparable. The range cannot be created and the ArgumentError is raised.
On a closing note, Range.cover?'s dependence on <=> is in fact an expected and documented behaviour. See RubySpec's specification of cover?.

Related

Ruby: Idiom for "create and increment"

I am looking for a concise way to deal with the following situation: Given a variable (in practince, an instance variable in a class, though I don't think this matters here), which is known to be either nil or hold some Integer. If it is an Integer, the variable should be incremented. If it is nil, it should be initialized with 1.
These are obvious solutions to this, taking #counter as the variable to deal with:
# Separate the cases into two statements
#counter ||= 0
#counter += 1
or
# Separate the cases into one conditional
#counter = #counter ? (#counter + 1) : 1
I don't like these solutions because they require to repeat the name of the variable. The following attempt failed:
# Does not work
(#counter ||= 0) += 1
This can't be done, because the result of the assignment operators is not an lvalue, though the actual error message is a bit obscure. In this case, you get the error _unexpected tOP_ASGN, expecting end_.
Is there a good idiom to code my problem, or do I have to stick with one of my clumsy solutions?
The question is clear:
A variable is known to hold nil or an integer. If nil the variable is to be set equal to 1, else it is to be set equal to its value plus 1.
What is the best way to implement this in Ruby?
First, two points.
The question states, "If it is nil, it should be initialized with 1.". This contradicts the statement that the variable is known to be nil or an integer, meaning that it has already been initialized, or more accurately, defined. In the case of an instance variable, this distinction is irrelevant as Ruby initializes undefined instance variables to nil when they are referenced as rvalues. It's an important distinction for local variables, however, as an exception is raised when an undefined local variable is referenced as an rvalue.
The comments largely address situations where the variable holds an object other than nil or an integer. They are therefore irrelevant. If the OP wishes to broaden the question to allow the variable to hold objects other than nil or an integer (an array or hash, for example), a separate question should be asked.
What criteria should be used in deciding what code is best? Of the various possibilities that have been mentioned, I do not see important differences in efficiency. Assuming that to be the case, or that relative efficiency is not important in the application, we are left with readability (and by extension, maintainability) as the sole criterion. If x equals nil or an integer, or is an undefined instance variable, perhaps the clearest code is the following:
x = 0 if x.nil?
x += 1
or
x = x.nil? ? 1 : x+1
Ever-so-slightly less readable:
x = (x || 0) + 1
and one step behind that:
x = x.to_i + 1
which requires the reader to know that nil.to_i #=> 0.
The OP may regard these solutions as "clumsy", but I think they are all beautiful.
Can an expression be written that references x but once? I can't think of a way and one has not been suggested in the comments, so if there is a way (doubtful, I believe) it probably would not meet the test for readability.
Consider now the case where the local variable x may not have been defined. In that case we might write:
x = (defined?(x) ? (x || 0) : 0) + 1
defined? is a Ruby keyword.

`<=': comparison of String with nil failed (ArgumentError)

I am testing whether array elements are greater than or equal to elements of smaller indices.
I get the error message from the subject line if I use the following loop
return true if order.each_index {|i| order[i ] <= order[i+1]}
I understand the last element of my array(order) can't be compared to a non-existant element.
Comparing a value to nil is impossible.
I don't, however understand why the following loop doesn't return the same error
(0...(order.length - 1)).all? do |i|
order[i] <= order[i + 1]
end
It seems that at some point, i = order.length-1
This means order[i+1] is a nil value (order.length)
Apparently not?
No, because three dots ... here (0...(order.length - 1)) mean 'without last element', so last value would be order.length - 2.
You'll encounter the same error if you try (0..(order.length - 1)).
Check Range documentation:
Ranges may be constructed using the s..e and s...e literals
Those created using ... exclude the end value

Does Ruby auto-promote numeric values for comparison?

Why on earth does comparing a float value of 1.0, to an integer value of 1, return true?
puts '1.0'.to_i
puts '1.0'.to_i == 1.0 #so 1 == 1.0 is true?
puts 1.0 == 1 #wtf?
Does Ruby only read the first part of the floatin point value and then short circuit? Wuld someone be able to explain with a link to some documentation please? I have flipped through the API but I don't even know what to look for in this case...
== compares the value, the value of 1.0 is equal to 1 in math, so it's not much surprising. To compare value as well as type, you can use eql?:
1.0 == 1
#=> true
1.0.eql? 1
#=> false
In Ruby, == is a method. That means to understand it you need to look at the specific class calling it.
1 == 1.0
The caller is 1, a Fixnum. So you need to look at Fixnum#==.
1.0 == 1
The caller is 1.0, a Float. So you need to look at Float#==.
A surprising result of this is that == is not necessarily symmetric: a == b and b == a could call completely different methods and return completely different results. In this case though, both == methods end up calling the C function rb_integer_float_eq which converts both operands to the same data type before comparing them.
Actually there already is a nice answer "What's the difference between equal?, eql?, ===, and ==?" about equality in Ruby, with references and stuff. There is a surprising amount of ways to compare for equality in Ruby, each with its own purpose.
Since mathematical meaning is not enough for you, you can compare differently. Like, say, eql? that is heavily used in Hashes to determine if two keys are the same. And it turns out that 1.0 and 1 are different keys! his is what I get in IRB of Ruby 2.1.2:
> 1.0.eql?(1.0)
=> true
> 1.eql?(1)
=> true
> 1.eql?(1.0)
=> false
Ruby is coercing the operands of == (as needed) to the same type, then performing a comparison of their numeric values. This is normally what you want for numeric comparisons.
Most languages will automatically increase the precision of a variable's type in order to perform operations on them such as compare, add, multiply, etc. They "usually" do not decrease the precision, nor unnecessarily increase it. E.g. 1/2 = 0, but 1.0/2.0 = 0.5.

How to find the "effective" end of a Range?

The .end of a Ruby range is the number used to specify the end of the range, whether or not the range is "exclusive of" the end. I don't understand the rationale for this design decision, but never the less, I was wondering what the most idiomatic mechanism is for determining the "effective end" of the range (i.e. greatest integer n such that range.include?(n) is true. The only mechanism I know of is last(1)[0], which seems pretty klunky.
Use the Range#max method:
r = 1...10
r.end
# => 10
r.max
# => 9
max won't work for exclusive ranges if any of the endpoints is Float (see range_max in range.c for details):
(1.0...10.0).max
# TypeError: cannot exclude non Integer end value
(1.0..10.0).max
# => 10.0
As an exception, if begin > end, no error is raised and nil is returned:
(2.0...1.0).max
# => nil
Update: max works in costant time for any inclusive range and exclusive ranges of integers, it returns the same value as end and end-1, respectively. It is also O(1) if for the range holds that begin > end, in this case nil is returned. For exlusive ranges of non integer objects (String, Date, ...) or when a block is passed, it calls Enumerable#max so it have to travel all the elements in the range!

ruby bitwise or

Here is code
def tmp
a = ancestors.first(ancestors.index(ActiveRecord::Base))
b = a.sum([]) { |m| m.public_instance_methods(false) |
m.private_instance_methods(false) |
m.protected_instance_methods(false) }
b.map {|m| m.to_s }.to_set
end
I thought | is bitwise OR operator. So how come b contains non boolean values?
It would have helped if you said what your code was supposed to do, but I think I finally got it. The | that you have is not an OR at all, neither bitwise nor logical. It is an array union operation. Look it up under Array rubydoc. It takes Array arguments and gives an Array result consisting of all elements that are in either array.
Since pretty much everything in Ruby is an object (aside from blocks, not relevant here), there are no absolute "operators" except for simple assignment. Every operator is in fact a method defined on some class, and therefore contextual.
Also, as someone rightly pointed out (deleted now), bitwise OR deals with integers, not booleans: 7 | 12 == 15. Logical or || deals with boolean values, but it too can return a non-boolean, since strictly speaking everything except for nil and false is true. Thus, 7 || 12 returns 7, not true (which is still equivalent to true in most contexts).
UPDATE: I've overlooked the fact that || and &&, when used on a Boolean object, can not actually be defined in Ruby, because of their short-circuit semantics. This nevertheless does not change the fact that they behave like methods of Boolean.

Resources