One of the examples in Peter Cooper's Beginning Ruby for polymorphism involves the to_s method. He gives this example:
puts 1000.to_s
puts [1, 2, 3].to_s
puts ({ :name => 'Fred', :age => 10 }).to_s
and shows this as the output:
1000
123
age10nameFred
but the output I get is:
1000
[1, 2, 3]
{:name=>"Fred", :age=>10}
Does anyone know why this would be the case? Was there a change in ruby, or is there something I'm doing wrong? Or not enough info to tell? How can I find it out?
The examples work using ruby 1.8.7, which is getting a bit dated. Ruby 1.9.3 (the current version) changed the to_s implementation for Arrays and Hashes.
EDIT: See Ruby 1.9 Array.to_s behaves differently?
Related
Under certain conditions Hash#keys does not work correctly in Ruby before version 2.4
Demo code:
h = { a: 1, b: 2, c: 3 }
h.each do |k, v|
h.delete(:a)
p h
p h.keys
break
end
Ruby 2.3.8 output:
{:b=>2, :c=>3}
[:b]
Ruby 2.5.1 output:
{:b=>2, :c=>3}
[:b, :c]
I agree it is not good to modify hash when iterating. But I did not see the relation between the modification the hash and the work keys method.
Why is this happening?
Interesting question. This isn't an answer yet, but it's too long for a comment and it could help others answer the question.
Which Rubies are affected?
I created a GitHub repository with a very simple spec:
describe Hash do
it "should always know which keys are left" do
h = { a: 1, b: 2, c: 3 }
h.each do |k, v|
h.delete :a
expect(h.keys).to eq [:b, :c]
end
end
end
Thanks to Travis, it's easy to see which Ruby versions have this bug:
Ruby 2.1
Ruby 2.2
Ruby 2.3
When did the bug appear?
The bug wasn't in ruby-2.1.0-preview2
The bug was in ruby-2.1.0-rc1
When was the bug fixed?
https://github.com/ruby/ruby/tree/v2_4_0_preview2 was the last tag with this bug.
https://github.com/ruby/ruby/tree/v2_4_0_preview3 is the first tag without this bug.
I just spent an hour using git bisect and make install in order to find that the bug has been fixed in this commit (75775157).
Introduce table improvement by Vladimir Makarov
.
[Feature #12142] See header of st.c for improvment details.
You can see all of code history here:
https://github.com/vnmakarov/ruby/tree/hash_tables_with_open_addressing
This improvement is discussed at
https://bugs.ruby-lang.org/issues/12142 with many people,
especially with Yura Sokolov.
st.c: improve st_table.
include/ruby/st.h: ditto.
internal.h, numeric.c, hash.c (rb_dbl_long_hash): extract a
function.
ext/-test-/st/foreach/foreach.c: catch up this change.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk#56650
b2dd03c8-39d4-4d8f-98ff-823fe69b080e
It has been confirmed by #Vovan, who found this commit 1 minute before I did.
A basic example of what is confusing me:
def [](row, col)
self[row][col]
end
x = [[1,3], [4,5]]
x[0][0] #normal
x[0, 0] #syntactic sugar?
I have been told that these are equivalent statements, but when I run the sugar one, I get a different answer. How am I supposed to write it in syntactic sugar?
You need to put your def [](row, col) method in a class that contains your data. So something like:
$ irb
2.3.0 :001 > class MyData
2.3.0 :002?> attr_accessor :my_array
2.3.0 :003?> def [](row, col)
2.3.0 :004?> my_array[row][col]
2.3.0 :005?> end
2.3.0 :006?> end
=> :[]
2.3.0 :007 > x = MyData.new
=> #<MyData:0x007f96dc8024b8>
2.3.0 :008 > x.my_array = [[1, 3], [4, 5]]
=> [[1, 3], [4, 5]]
2.3.0 :009 > x[1,1]
=> 5
2.3.0 :010 >
There are two problems with this:
You are adding your method to the Object class. But Array has its own [] which overrides the one in Object, so your method never gets called …
… which is a good thing, because it doesn't work anyway: all the method does is call itself, that would lead to a stackoverflow because of runaway recursion, but thankfully, the method calls itself with one argument, but is defined to take two parameters, and thus all you get is an ArgumentError.
Now, forgetting for a moment that monkey patching a core class is a terrible idea, you could to this:
module TwoDArrayExtension
def [](x, y)
super(x).method(__callee__).super_method.(y)
end
end
class Array
prepend TwoDArrayExtension
end
x = [[1, 3], [4, 5]]
x[0][0] #normal
# in `[]': wrong number of arguments (given 1, expected 2) (ArgumentError)
# Hey, we just re-defined [] to take two arguments, so obviously this cannot work!
x[0, 0] #syntactic sugar?
#=> 1
This "works" in the sense that it gets your syntactic sugar example to pass. But your "normal" example now breaks: you have re-defined how arrays work, so you can no longer use it like an array. Tinkering with core classes like this has serious ramifications. For example:
Irb is written in Ruby, and uses arrays internally, among other things. You cannot test your code in Irb, because it will just crash.
The same applies to Pry.
The same applies to any and all existing code that uses arrays … which is pretty much all Ruby code ever written. You cannot use any gems. You cannot use and standard libraries.
In fact, in an implementation like Rubinius, where pretty much everything, including large parts of the core libraries themselves, and even the compiler are written in Ruby and thus use arrays internally, you probably won't even be able to get your code to run at all.
In short: yes, you can do this, but you really, really, really, REALLY don't want to.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Getting Argument Names In Ruby Reflection
Is it possible to get the parameter names of a method ?
Example with:
def method_called(arg1, arg2)
puts my_method.inspect
end
I would like to know what method (my_method) should I call to get the following output:
["arg1", "arg2"]
In Ruby 1.9.2, you can trivially get the parameter list of any Proc (and thus of course also of any Method or UnboundMethod) with Proc#parameters:
def foo(a, b=nil, *c, d, &e); end
p method(:foo).parameters
# => [[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:block, :e]]
The format is an array of pairs of symbols: type (required, optional, rest, block) and name.
For the format you want, try
method(:foo).parameters.map(&:last).map(&:to_s)
# => ['a', 'b', 'c', 'd', 'e']
1
If you are on Ruby 1.9.1 you can use the MethoPara gem. This allows you to do the following:
def method_called(arg1, arg2)
method(caller[0][/`([^']*)'/, 1].to_sym).parameters
end
2
You can use the approach proposed by Michael Grosser at his blog.
3
Merb Action Args
Within a method, you can get the names you've given the arguments by calling local_variables at the beginning (since no other local variables will have been defined at the time).
However, I don't think any good will come of this for any purpose besides maybe logging info. If you have a specific goal in mind, we could probably find something more idiomatic.
if you want the value for the default values, too, there's the "arguments" gem
$ gem install rdp-arguments
$ irb
>> require 'arguments'
>> require 'test.rb' # class A is defined here
>> Arguments.names(A, :go)
I'd like to have my array items scrambled.
Something like this:
[1,2,3,4].scramble => [2,1,3,4]
[1,2,3,4].scramble => [3,1,2,4]
[1,2,3,4].scramble => [4,2,3,1]
and so on, randomly
Built in now:
[1,2,3,4].shuffle => [2, 1, 3, 4]
[1,2,3,4].shuffle => [1, 3, 2, 4]
For ruby 1.8.6 (which does not have shuffle built in):
array.sort_by { rand }
For ruby 1.8.6 as sepp2k's example, but you still want use "shuffle" method.
class Array
def shuffle
sort_by { rand }
end
end
[1,2,3,4].shuffle #=> [2,4,3,1]
[1,2,3,4].shuffle #=> [4,2,1,3]
cheers
Code from the Backports Gem for just the Array for Ruby 1.8.6. Ruby 1.8.7 or higher is built in.
class Array
# Standard in Ruby 1.8.7+. See official documentation[http://ruby-doc.org/core-1.9/classes/Array.html]
def shuffle
dup.shuffle!
end unless method_defined? :shuffle
# Standard in Ruby 1.8.7+. See official documentation[http://ruby-doc.org/core-1.9/classes/Array.html]
def shuffle!
size.times do |i|
r = i + Kernel.rand(size - i)
self[i], self[r] = self[r], self[i]
end
self
end unless method_defined? :shuffle!
end
The Ruby Facets library of extensions has a Random module which provides useful methods including shuffle and shuffle! to a bunch of core classes including Array, Hash and String.
Just be careful if you're using Rails as I experienced some nasty clashes in the way its monkeypatching clashed with Rails'...
I'm new to Ruby, and I'm trying the following:
mySet = numOfCuts.times.map{ rand(seqLength) }
but I get the 'yield called out of block' error. I'm not sure what his means. BTW, this question is part of a more general question I asked here.
The problem is that the times method expects to get a block that it will yield control to. However you haven't passed a block to it. There are two ways to solve this. The first is to not use times:
mySet = (1..numOfCuts).map{ rand(seqLength) }
or else pass a block to it:
mySet = []
numOfCuts.times {mySet.push( rand(seqLength) )}
if "numOfCuts" is an integer,
5.times.foo
is invalid
"times" expects a block.
5.times{ code here }
You're combining functions that don't seem to make sense -- if numOfCuts is an integer, then just using times and a block will run the block that many times (though it only returns the original integer:
irb(main):089:0> 2.times {|x| puts x}
0
1
2
map is a function that works on ranges and arrays and returns an array:
irb(main):092:0> (1..3).map { |x| puts x; x+1 }
1
2
3
[2, 3, 4]
I'm not sure what you're trying to achieve with the code - what are you trying to do? (as opposed to asking specifically about what appears to be invalid syntax)
Bingo, I just found out what this is. Its a JRuby bug.
Under MRI
>> 3.times.map
=> [0, 1, 2]
>>
Under JRuby
irb(main):001:0> 3.times.map
LocalJumpError: yield called out of block
from (irb):2:in `times'
from (irb):2:in `signal_status'
irb(main):002:0>
Now, I don't know if MRI (the standard Ruby implementation) is doing the right thing here. It probably should complain that this does not make sense, but when n.times is called in MRI it returns an Enumerator, whereas Jruby complains that it needs a block.
Integer.times expects a block. The error message means the yield statement inside the times method can not be called because you did not give it a block.
As for your code, I think what you are looking for is a range:
(1..5).map{ do something }
Here is thy rubydoc for the Integer.times and Range.