Is ruby's rand method supposed to accept Range objects? - ruby

I've only seen something like rand(1..5) work in MRI ruby 1.9.3 (haven't tried 1.9.2). Jruby doesn't support it, even in 1.9 mode - it raises a TypeError.
Even ruby-doc doesn't mention that Ranges are supported. What's the official behavior?
UPDATE
Well as the answers and comments point out, only 1.9.3 supports it. Jruby is only at 1.9.2.

The docs do say that it is supported
If max is Range, returns a pseudorandom number where range.member(number) == true.
It's also in the 1.9.3 changelog

Verbatim Copy-paste from docs
If max is Range, returns a pseudorandom number where range.member(number) == true.
Or else converts max to an integer using max1 = max.to_i.abs.
so, yes. it is supported from ruby 1.9.3

Related

Is the frozen behavior on Fixnum a specified behavior?

Here was the behavior that somewhat surprised me:
1.9.3-p392 :001 > n = 1
=> 1
1.9.3-p392 :002 > n.frozen?
=> false
1.9.3-p392 :003 > 1.freeze
=> 1
1.9.3-p392 :004 > n.frozen?
=> true
I tried grepping through the RubySpec source code for frozen and I didn't see anything about Fixnums at all.
Is this behavior the same as what would be expected on non-MRI Ruby implementations? If I run n = 1; 1.freeze; n.frozen?, will the final result always be true on any Ruby implementation? Why or why not?
It looks like it is an intended feature. In Ruby Forum, a question is raised:
Isn't technically all Fixnums "frozen" (because you can't modify the state)?
to which matz answers:
Not really, since fixnums could have instance variables.
Thus, theoretically, there's room for freezing fixnums. I don't think it's worth the cost though.
There seems to have been unintended inconsistency between different MRI Ruby versions, but as far as MRI is concerned, they look like they were identified as bugs, and were fixed. The specification you observed seems to be the intended one.
Since everything in Ruby is an object, it perhaps shouldn't be surprising that you can freeze 1. Symbol has the same behavior. As mentioned in the comments, this behavior changed from 1.8.7 (freezing didn't do anything) to 1.9+ (freezing works).
What might be more surprising is that freezing 1 also freezes n. According to the documentation for Fixnum,
There is effectively only one Fixnum object instance for any given
integer value
This means that n and 1 are references to the same object, and modifying one will modify the other.
You can check this for yourself by looking at the object_id.
1.object_id
=> 3
n=1
n.object_id
=> 3
n.equal? 1
=> true
I tried this code in MRI 1.8.7, MRI 2.0.0, JRuby, and Rubinius and got the same result.

Does seed generate same random sequence across different Rubys?

Suppose I call srand(1234), then call rand() repeatedly. Am I guaranteed to get the same sequence of random numbers regardless of my environment?
For instance,
Ruby 1.8.7 vs 1.9.3 vs 2.0
MRI vs JRuby
Windows vs Mac vs Linux
The answer in my experience is "yes"
I do this exact same thing when testing a new gem. The gem is not ready for real-world use, but relies heavily on random numbers, and most of the tests involve running Ruby's srand() beforehand so I get predictable numbers for assertions. All in all I probably test a few hundred small integers generated by rand() every time I run the test suite.
So far I have tested:
On Windows: MRI 1.9.3
On Mac: MRI 1.8.7, MRI 1.9.3 and MRI 2.0.0
On Travis (see build https://travis-ci.org/neilslater/games_dice), I test all these:
"1.8.7"
"1.9.3"
"2.0.0"
jruby-18mode # JRuby in 1.8 mode
jruby-19mode # JRuby in 1.9 mode
rbx-18mode
rbx-19mode
The last two of which I don't even know what they are :-)
The test suite has never failed due to an unexpected number from Ruby's rand method.
The underlying mechanism in Ruby is called the Mersenne Twister http://en.wikipedia.org/wiki/Mersenne_twister and this will generate same values from the same seeds, even across different languages, where it has been implemented. As far as I know, this algorithm is the one used by Ruby's rand() (and srand()) in all the standard implementations.
Well, this is what I get inside my irb - does it match what you get in yours? If so, then I think you can safely say "yes".
BTW, this is the whole point of seeding, so I expect the answer will definitely be "yes", and if not I'll be surprised.
ruby 1.9.3p327 (2012-11-10) [x86_64-darwin12.2.0]
irb(main):001:0> srand(1234)
=> 312936473923896776645464224839555650313
irb(main):002:0> rand
=> 0.1915194503788923
irb(main):003:0> rand
=> 0.6221087710398319
irb(main):004:0> rand
=> 0.4377277390071145
irb(main):006:0> rand
=> 0.7853585837137692
irb(main):007:0> rand
=> 0.7799758081188035

Rubinius: how to generate enumerator as the official way?

I have this simple code to generate a lazy array:
lazy_arr = Enumerator.new { |y|
i = 1
loop {
y << i
i+=1
}
}
p lazy_arr.take(5)
In official Ruby 1.9.3, the output is [1,2,3,4,5], which is what I want.
But in Rubinius, it gives error and tells me cannot find Enumerator constant.
So I looked it up, and find Enumerator defined in Enumerable module instead of kernel, and when it is generated, it needs a few arguments in the brackets:
http://rubydoc.info/github/evanphx/rubinius/master/Enumerable/Enumerator
I tried to change Enumerator.new to Enumerable::Enumerator.new, or include Enumerable, neither works because it needs more arguments.
How can I do the example above in Rubinius? Is there any way around to make the code work in both official and Rubinius?
You're using Rubinius in 1.8 mode, which doesn't have Enumerator in the global namespace. Please use Rubinius in 1.9 mode and the example works fine then. You can use 1.9 by passing -X19 when starting Rubinius, or setting RBXOPT=-X19 for example.
It's also possible to make 1.9 mode the default with configure during compile time.
Sounds like a bug/missing class in Rubinius. Open up an issue on github and it will get added. Or dig in and send a pull request!

How can I splattify an anonymous object so I can use &method on it?

I'm wanting to use the &method(:method_name) idiom when there's more than one object required by method_name. Can I do this under Ruby 1.9?
For example, if I've got
def move_file(old_filename, new_filename)
STDERR.puts "Moving #{old_filename.inspect} to #{new_filename.inspect}"
# Implementation for careful moving goes here
end
old_filenames = ["foo.txt", "bar.txt", "hoge.ja.txt"]
new_filenames = ["foo_20110915.txt", "bar_20110915.txt", "hoge_20110915.ja.txt"]
the code
old_filenames.zip(new_filenames).each(&method(:move_file))
works under Ruby 1.8, but not under Ruby 1.9. Under Ruby 1.9, it's trying to do move_file(["foo.txt", "foo_20110915.txt"]) instead of move_file("foo.txt", "foo_20110915.txt").
How do I splattify it so it has the correct arity?
Workarounds I'm aware of:
Replace def move_file(old_filename, new_filename) with def move_file(*arguments)
Replace each(&method(:move_file)) with
each{|old_filename, new_filename| move_file(old_filename, new_filename)}
Instead
each{|old_filename, new_filename| move_file(old_filename, new_filename)}
you should be able to do
each{|pair| move_file(*pair)}
But I don't know how you'd pull off blockless variant (I needed it couple of times as well). I guess &-shorthand was made to make the syntax simpler, and is not meant to be clogged much (whether it will be passed an array as an array, or splatted, for example). :)
How do I splattify it so it has the correct arity?
I don't think there is a way to do this while being compatible to both Ruby versions. What you could do is wrap it into a lambda
move_from_to = Proc.new {|*both| move_files(*both) }
The thing is - block and proc arity is something that got addressed in Ruby 1.9 so there might be a difference in behavior there. Also see prc.lambda? here http://www.ruby-doc.org/core/classes/Proc.html for info on what it does to the arity.
This question is also related to what you want to do (the solution there is to resplat and unsplat manually): Inconsistency of arity between Hash.each and lambdas

Error in rounding off values using .round in Ruby

The following piece of code works perfectly in script/console but returns the following error when i compile the same in a ruby script.:
:in `round': wrong number of arguments (1 for 0) (ArgumentError)
tf={"ph"=>{0=>1.33333333333333, 1=>1.5}, "fee"=>{0=>1.66666666666667}, "test"=>{0=>1.16666666666667, 1=>1.25}, "what"=>{0=>2.0, 1=>2.0}, "for"=>{0=>1.5}, "is"=>{0=>1.83333333333333, 1=>1.75}}
tf.each{|k,v| v.each{|k1,v1| tf[k][k1]=(v1.round(5))}}
Any Ideas ? Cheers !
Float#round seems to work differently in Ruby 1.8 and Ruby 1.9: in 1.8 it complains about the given argument, in 1.9 returns back float properly rounded to the given number of decimals.
But, as the article linked in the other answer wisely says:
you should consider the reason you’re
performing the rounding (or
equivalent) operation. If it’s for
presentation reasons only a better way
might be to use a format string
instead, and leave the original data
intact.
From what it looks like, you are not supposed to pass an argument to the round method. You have passed 5 to it.
If you are trying to round it to 5 decimal places, there is no builtin method for that (that I'm aware of). This is a page that explains how to do so: http://solutions.hans-eric.com/rounding-off-floating-point-numbers-in-ruby

Resources