Multiple Kernel#local_variables entries with block local parameters - ruby

I am using this version of Ruby on Arch Linux. I also tried the first code snippet in ruby 1.9, which had the same results.
ruby -v
ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-linux]
uname -a
Linux ryantm0j132 3.12.7-2-ARCH #1 SMP PREEMPT Sun Jan 12 13:09:09 CET 2014 x86_64 GNU/Linux
These three snippets below are separate programs.
When I use block local variables that shadow a variable the local_variables array contains 3 entries:
a = 1
puts local_variables.inspect #=> [:a]
proc { |;a|
puts local_variables.inspect #=> [:a,:a,:a]
}.call
If I don't shadow, anything it contains 1 entry:
puts local_variables.inspect #=> []
proc { |;b|
puts local_variables.inspect #=> [:b]
}.call
Another example of the block local variable not shadowing anything:
a = 1
puts local_variables.inspect #=> [:a]
proc { |;b|
puts local_variables.inspect #=> [:b,:a]
}.call
Is there some reason for these extra entries in the first case? Is it a bug in ruby?

It looks like I finally got why there are three of them. That’s out of my competence to decide whether this is a bug.
Let’s take a look at the bindings:
b1 = binding
a = 1
puts proc { |b2=binding; a|
a = 3
"T: #{b1}, B: #{b2}, L: #{binding}\n" +
"TV: #{b1.eval('a')}, BV: #{b2.eval('a')}, LV: #{binding.eval('a')}"
}.call
# ⇒ T: #<Binding:0x0000000294ef88>,
# ⇒ B: #<Binding:0x0000000294de58>,
# ⇒ L: #<Binding:0x0000000294dd68>
# ⇒ T: 1, B: 3, L: 3
It seems there are three Binding objects, each having the local variable name added to the list if and only it was shadowed. Binding b2, though it is a separate instance, has affected by a = 3 setting.
Probably it was made to simplify the local_variables discounting.

This has been confirmed as a bug on the Ruby issue tracker page where I reported it.

Related

Ruby local_variables returns :symbols?

I was looking through the ruby Kernel doc and saw this method:
a = 2
local_variables # => [:a, :_]
Why does it return :a and not a?
I thought the ":" was reserved for symbols, but the symbol :a doesn't point to the variable a nor to it's assigned value, 2.
Furthermore, how would I go about accessing the actual variables through this method? As in b=local_variables.first (would be 2, but is :a).
Is there a reason behind this behavior, what is it?
Thanks/
Why does it return :a and not a? I thought the ":" was reserved for symbols
It's the expected behavior. According to the docs:
Returns the names of the current local variables.
So yes, this just returns an array of symbols.
Furthermore, how would I go about accessing the actual variables through this method?
As noted by Jonathan Camenisch, Ruby 2.1 introduced Binding#local_variable_get:
a = 2
binding.local_variable_get(:a)
#=> 2
For older Rubies, you could use eval:
a = 2
eval(:a.to_s)
#=> 2
Is there a reason behind this behavior, what is it?
In Ruby symbols are used for references:
"foo".methods
#=> [:<=>, :==, :===, :eql?, :hash, :casecmp, ...]
Module.constants
#=> [:Object, :Module, :Class, :BasicObject, :Kernel, :NilClass, ...]
Why does it return :a and not a?
It can't return a, because a is a variable and variables aren't objects in Ruby. Methods can only take, return, and manipulate objects.
how would I go about accessing the actual variables through this method?
Humm.here you can go:-
a = 2
b = 10
local_variables.each{|e| p eval(e.to_s)}
# >> 2
# >> 10
Why does it return :a and not a?
That answer has been given by #Stefan. But you can get here some more taste:-
13 Ways of Looking at a Ruby Symbol
Out of these the below is related to your answer:-
7. A Ruby symbol is a Ruby identifier
In Ruby, we can look up identifiers (variable, methods and constant names) while the program is running. This is typically done using symbols.
class Demo
# The stuff we'll look up.
DEFAULT = "Hello"
def initialize
#message = DEFAULT
end
def say() #message end
# Use symbols to look up identifiers.
def look_up_with_symbols
[Demo.const_get(:DEFAULT),
method(:say),
instance_variable_get(:#message)]
end
end
Demo.new.look_up_with_symbols

Named parameters in Ruby 2

I don't understand completely how named parameters in Ruby 2.0 work.
def test(var1, var2, var3)
puts "#{var1} #{var2} #{var3}"
end
test(var3:"var3-new", var1: 1111, var2: 2222) #wrong number of arguments (1 for 3) (ArgumentError)
it's treated like a hash. And it's very funny because to use named parameters in Ruby 2.0 I must set default values for them:
def test(var1: "var1", var2: "var2", var3: "var3")
puts "#{var1} #{var2} #{var3}"
end
test(var3:"var3-new", var1: 1111, var2: 2222) # ok => 1111 2222 var3-new
which very similar to the behaviour which Ruby had before with default parameters' values:
def test(var1="var1", var2="var2", var3="var3")
puts "#{var1} #{var2} #{var3}"
end
test(var3:"var3-new", var1: 1111, var2: 2222) # ok but ... {:var3=>"var3-new", :var1=>1111, :var2=>2222} var2 var3
I know why is that happening and almost how it works.
But I'm just curious, must I use default values for parameters if I use named parameters?
And, can anybody tell me what's the difference between these two then?
def test1(var1="default value123")
#.......
end
def test1(var1:"default value123")
#.......
end
I think that the answer to your updated question can be explained with explicit examples. In the example below you have optional parameters in an explicit order:
def show_name_and_address(name="Someone", address="Somewhere")
puts "#{name}, #{address}"
end
show_name_and_address
#=> 'Someone, Somewhere'
show_name_and_address('Andy')
#=> 'Andy, Somewhere'
The named parameter approach is different. It still allows you to provide defaults but it allows the caller to determine which, if any, of the parameters to provide:
def show_name_and_address(name: "Someone", address: "Somewhere")
puts "#{name}, #{address}"
end
show_name_and_address
#=> 'Someone, Somewhere'
show_name_and_address(name: 'Andy')
#=> 'Andy, Somewhere'
show_name_and_address(address: 'USA')
#=> 'Someone, USA'
While it's true that the two approaches are similar when provided with no parameters, they differ when the user provides parameters to the method. With named parameters the caller can specify which parameter is being provided. Specifically, the last example (providing only the address) is not quite achievable in the first example; you can get similar results ONLY by supplying BOTH parameters to the method. This makes the named parameters approach much more flexible.
The last example you posted is misleading. I disagree that the behavior is similar to the one before. The last example passes the argument hash in as the first optional parameter, which is a different thing!
If you do not want to have a default value, you can use nil.
If you want to read a good writeup, see "Ruby 2 Keyword Arguments".
As of Ruby 2.1.0, you no longer have to set default values for named parameters. If you omit the default value for a parameter, the caller will be required to provide it.
def concatenate(val1: 'default', val2:)
"#{val1} #{val2}"
end
concatenate(val2: 'argument')
#=> "default argument"
concatenate(val1: 'change')
#=> ArgumentError: missing keyword: val2
Given:
def test1(var1="default value123")
var1
end
def test2(var1:"default value123")
var1
end
They'll behave the same way when not passed an argument:
test1
#=> "default value123"
test2
#=> "default value123"
But they'll behave much differently when an argument is passed:
test1("something else")
#=> "something else"
test2("something else")
#=> ArgumentError: wrong number of arguments (1 for 0)
test1(var1: "something else")
#=> {:var1=>"something else"}
test2(var1: "something else")
#=> "something else"
I agree with you that it's weird to require default values as the price for using named parameters, and evidently the Ruby maintainers agree with us! Ruby 2.1 will drop the default value requirement as of 2.1.0-preview1.
This is present in all the other answers, but I want to extract this essence.
There are four kinds of parameter:
Required
Optional
Positional
def PR(a)
def PO(a=1)
Keyword
def KR(a:)
def KO(a:1)
When defining a function, positional arguments are specified before keyword arguments, and required arguments before optional ones.
irb(main):006:0> def argtest(a,b=2,c:,d:4)
irb(main):007:1> p [a,b,c,d]
irb(main):008:1> end
=> :argtest
irb(main):009:0> argtest(1,c: 3)
=> [1, 2, 3, 4]
irb(main):010:0> argtest(1,20,c: 3,d: 40)
=> [1, 20, 3, 40]
EDIT: the required keyword argument (without a default value) is new as of Ruby 2.1.0, as mentioned by others.
Leaving this here because it helped me a lot.
Example
Suppose you have this:
def foo(thing, to_print)
if to_print
puts thing
end
end
# this works
foo("hi", true)
# hi
# => nil
so you try adding the argument names, like so:
foo(thing: "hi", to_print: true)
# foo(thing: "hi", to_print: true)
# ArgumentError: wrong number of arguments (given 1, expected 2)
# from (pry):42:in `foo'
but unfortunately it errors.
Solution
Just add a : to the end of each argument:
def foo2(thing:, to_print:)
if to_print
puts thing
end
end
foo2(thing: "hi", to_print: true)
# hi
# => nil
And it works!
According to "Ruby 2.0.0 by Example" you must have defaults:
In Ruby 2.0.0, keyword arguments must have defaults, or else must be captured by **extra at the end.
def test(a = 1, b: 2, c: 3)
p [a,b,c]
end
test #=> [1,2,3]
test 10 #=> [10,2,3]
test c:30 #=> [1,2,30] <- this is where named parameters become handy.
You can define the default value and the name of the parameter and then call the method the way you would call it if you had hash-based "named" parameters but without the need to define defaults in your method.
You would need this in your method for each "named parameter" if you were using a hash.
b = options_hash[:b] || 2
as in:
def test(a = 1, options_hash)
b = options_hash[:b] || 2
c = options_hash[:c] || 3
p [a,b,c]
end
You can define named parameters like
def test(var1: var1, var2: var2, var3: var3)
puts "#{var1} #{var2} #{var3}"
end
If you don't pass one of the parameters, then Ruby will complain about an undefined local variable or method.

use the correct number of arguments for new in Ruby

I am working on a gem that can uses different version of Gherkin, but I'm facing a problem:
in the 2.4.0 version Gherkin::Formatter::Model::Scenario.new takes 6 arguments but in 2.6.5 it takes 7 arguments.
So my question is what is a best practice in this case ? Should I do:
case Gherkin::Version
when '2.4.0'
do the init with 6 arguments
else
with the 7
end
I was thinking also of creating a new_with_arity method:
class Object
def new_with_arity(*params)
puts method(:initialize).arity # => -1
puts method(:new).arity # => -1
new(*(params + [nil] * (params.count - method(:new).arity)))
end
end
However this does not work, the arity of new and initialize is -1.
Do you have an idea ?
I would recommend following Jim Deville's advice. Saying that it's quite an interesting idea and you were pretty close. The problem is you can't get the method without having an instance, so the trick is to use allocate first.
class Object
def new_with_arity(*params)
new *(params + [nil] * (allocate.method(:initialize).arity - params.size))
end
end
class One
def initialize a
[a]
end
end
class Two
def initialize a, b
[a, b]
end
end
One.new_with_arity 1 #=> [1]
Two.new_with_arity 1, 2 #=> [1, 2]
Two.new_with_arity 1 #=> [1, nil]
I would build 2 Gherkin adapters and load up the proper one for the proper version. Or, you are using Rubygems, so you can force a specific version of the Gherkin parser

String/Range comparison problem

This make sense for things like :
irb(main):001:0> ["b", "aa", "d", "dd"].sort
=> ["aa", "b", "d", "dd"]
But doesn't for :
irb(main):002:0> ("B".."AA").each{ |x| print "#{x}," }
=> "B".."AA"
should produce :
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,AA,=> "B".."AA" but "B" > "AA" => true
Unlike "B".."BA" ("B" > "BA" => false) :
irb(main):003:0> ("B".."BA").each{ |x| print "#{x}," }
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,AA,AB,AC,AD,AE,AF,AG,AH,AI,AJ,AK,AL,AM,AN,AO,AP,AQ,AR,AS,AT,AU,AV,AW,AX,AY,AZ,BA,=> "B".."BA"
Any advice to make "b".."aa" work as expected in ruby ?
I use
irb 0.9.5(05/04/13) ruby 1.8.7
(2009-06-12 patchlevel 174) [i486-linux]
Linux 2.6.31-19-generic #56-Ubuntu SMP Thu Jan 28 01:26:53 UTC 2010 i686 GNU/Linux
The best way to do this is to subclass String and redefine the comparison operator to meet your needs. Then use your new class to make the range.
class MyString < String
def initialize str=""
super str
end
def <=>(other)
length_cmp = self.length <=> other.length
return length_cmp unless length_cmp == 0
super other
end
end
Now you can ensure that a column appears before another.
"b" < "aa" #=> false
MyString.new("b") < MyString.new("aa") #=> true
N.B.: Only the string on the left side of any comparison operator needs to be of class MyString:
MyString.new("b") < "aa" #=> true
"aa" > MyString.new("b") #=> false
It looks like Ruby is using succ, but first it checks for end>=start, and since that is false here, it doesn't even try.
Admittedly String#succ is a weird beast here: a string's successor isn't always greater than the string, using its own comparison methods. So I'm not sure if this is technically a bug or not. It does look pretty confusing if you don't know this undocumented check, though.
Then again, judging by some of the other answers here, it looks like it does work as you expect in some versions of Ruby, so maybe it was fixed in 1.9?
It is true that in class String, <=> and String#succ are not completely harmonized
I suppose it would be nice if for each a, b where b eventually is produced from a.succ.succ..., it was also true that a <=> b returned -1. One reason this would be nice is that it is in fact precisely <=> and succ that are used to implement ranges in the first place. Consequently, as you have noted, a Ruby String range where .succ would eventually complete the expansion doesn't work because a <=> test contradicts it and terminates the loop.
So yes, the ordering, at least for String, that is defined by <=> doesn't match the #succ method ordering. This is one reason that some developers avoid using succ.
Any advice to make "b".."aa" work as expected in ruby ?
This DOESN'T work in ruby 1.8.7 (2009-06-12 patchlevel 174) nor in ruby 1.9.1p376 (2009-12-07 revision 26041) [i486-linux]
"b".."ba" does work...
irb(main):001:0> ("b".."ba").each {|x| print "#{x} "}
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 aa ab ac ad ae af ag ah ai aj ak al am an ao ap aq ar as at au av aw ax ay az ba => "b".."ba"
This was reported as a bug in Ruby here. The results depend on which version of Ruby you are running. There is a difference between versions 1.8.6 and 1.9.1.

Obtaining number of block parameters

I need to obtain the number of parameters a given block takes. For example:
foobar(1,2,3) { |a, b, c|
}
def foobar(x, y, z, &block)
# need to obtain number of arguments in block
# which would be 3 in this example
end
This is possible in the 1.9 trunk, but not in any official release. I was hoping if there's any way to do this without having to download a separate gem/extension module.
When you materialize a block with &, it becomes a Proc object, which has an arity method. Just be careful - it returns the one's complement if the proc takes a *splat arg.
def foobar(x, y, z, &block)
p block.arity
end
(Answer via "The Ruby Programming Language" book.)
Is this what you're looking for...
def foobar(x, y, z, &block)
# need to obtain number of arguments in block
# which would be 3 in this example
case block.arity
when 0
yield "i have nothing"
when 1
yield "I got ONE block arg"
when 2
yield "I got TWO block args"
when 3
yield "I got THREE block args"
end
end
foobar(1,2,3) { |a, b, c|
puts a
}
Outputs:
D:\ruby\bin>ruby -v
ruby 1.8.6 (2007-09-24 patchlevel 111) [i386-mswin32]
D:\ruby\bin>ruby c:\Temp.rb
I got THREE block args
See also - A Ruby HOWTO: Writing A Method That Uses Code Blocks from codahale.com

Resources