I have an array of a reasonable size,
a = ("a".."z").to_a
and I want to use a certain default number that would (practically) always return nil when used to call a value as an index.
a[some_default_index] # => nil
For a fixed a, I can set a fixed number equal to or larger than a's size as the default number, but a varies.
I tried Float::NAN and Float::Infinity, which did not work as I intended.
a[Float::NAN] # => out of range error
a[Float::Infinity] # => out of range error
What number can I use as an index?
Here's an adapted version of @AndrewGrimm's excellent answer :
a = ("a".."z").to_a
start = Time.now
largest_correct_index = 1
smallest_incorrect_index = nil
until smallest_incorrect_index == largest_correct_index + 1
if smallest_incorrect_index.nil?
next_number_to_try = largest_correct_index * 1000
else
next_number_to_try = (smallest_incorrect_index + largest_correct_index) / 2 # Geometric mean would be more efficient, but more risky
end
if next_number_to_try <= largest_correct_index ||
smallest_incorrect_index && next_number_to_try >= smallest_incorrect_index
raise "Can't happen case"
end
begin
a[next_number_to_try]
largest_correct_index = next_number_to_try
rescue RangeError
smallest_incorrect_index = next_number_to_try
end
end
finish = Time.now
puts "The largest correct index is #{largest_correct_index}"
puts "The smallest incorrect index is #{smallest_incorrect_index}"
puts "Calculation took #{finish - start} seconds"
On 32-bit Ruby, it returned 2**31-1:
The largest correct index is 2147483647
The smallest incorrect index is 2147483648
Calculation took 0.0 seconds
On 64-bit Ruby and JRuby, it returned 2**63-1:
The largest correct index is 9223372036854775807
The smallest incorrect index is 9223372036854775808
Calculation took 0.000250378 seconds
So it looks like @akuhn's answer should be good enough in most cases.
Depending on your needs, you could also use a hash :
a = ('a'..'z').to_a
# ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
h = ('a'..'z').map.with_index { |l, i| [i, l] }.to_h
# {0=>"a", 1=>"b", 2=>"c", 3=>"d", 4=>"e", 5=>"f", 6=>"g", 7=>"h", 8=>"i", 9=>"j", 10=>"k", 11=>"l", 12=>"m", 13=>"n", 14=>"o", 15=>"p", 16=>"q", 17=>"r", 18=>"s", 19=>"t", 20=>"u", 21=>"v", 22=>"w", 23=>"x", 24=>"y", 25=>"z"}
require 'fruity'
compare do
_array { a[rand(26)] }
_hash { h[rand(26)] }
end
It doesn't seem to impact performances negatively :
Running each test 16384 times. Test will take about 1 second.
_array is similar to _hash
All those :
h[-1]
h[:default]
h[Float::INFINITY]
h[2**1024-1]
would return nil.
Try this
max_index = 2**30 - 1
a[max_index] # => nil
The error message gives the hint RangeError: bignum too big to convert into `long'
That is, the index must fit into long value in the underlying machine-level representation of Ruby. On a 32-bit system that would be 2**31 - 1 and 2**63 - 1 on a 64-bit system.
However, these values are to big to be represented as a Fixnum in Ruby so if you want to take advantage of the immediate speedup of fixnums best use their maximum value instead. Ruby uses tag pointers to represent Fixnum integers, hence they are one bit smaller that native integers and thus 2**30 - 1 for a 32-bit system and 2**62 - 1 for a 64-bit system.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With