Splat parameters behave differently for attribute writers compared to regular method - ruby

I have the following two methods, which I believe should have the same behaviour disregarding their names:
def a=(*params)
params
end
def b(*params)
params
end
But when in fact I use them:
a=(1) # => 1
b(1) # => [1]
(a=1) == b(1) # => false
while interestingly:
(a=1,2) == b(1,2) # => true
Why isn't their behaviour the same?
Edit: forgot to wrap the above in a class / call with self. which accidentally produces the same behaviour but for a different reason. It has been pointed out in the answers.

It has nothing to do with splat. It's the assignment operator. In ruby, the assignment operator returns the value assigned. The return value from the method is ignored.
So a=1 return 1, not [1].
But, as mentioned by #mudasobwa, you're not even calling the method here. But if you were, that's what would happen (ignoring the return value).
class Foo
def a=(*params)
params
end
end
f = Foo.new
f.a = 1 # => 1
f.a = 1,2 # => [1, 2]
To not ignore the return value, call that setter without using assignment operator.
f.send 'a=', 1 # => [1]

The thing is that
a = 1
sets the local variable and does not call your method at all. Try with
def a=(*param)
puts "I AM HERE"
end
var= methods require an explicit receiver. To call your method, call it with an explicit receiver:
self.a = 1
It still won’t return anything but 1, because assignment methods return the value (the same way as initialize called through MyClass.new returns an instance, no matter what.) But you might check that splat works with:
def a=(*param)
puts param.inspect
end
self.a = 1
# [1]
#⇒ 1

Related

Call a Ruby method explicitly using the default for a parameter

An unfortunately large number of methods are written in the following form:
def my_method(foo = {}, bar = {})
# Do stuff with foo and bar
end
While I appreciate not having to write my_method({}, {}) everywhere I reference the method, using something other than the default for the second parameter makes me use something other than the default for the first parameter too - my_method({}, foo: 'bar').
Is there a way to tell Ruby to use the default for a parameter when other, later parameters need to use something other than the default? I'm hoping for something in the form of my_method(__default__, foo: 'bar'), but would welcome any other solutions that address the core of this problem.
This would be particularly useful when APIs undergo minor (but significant) changes. It can lead to hard to find bugs occasionally:
# Original method
def foo(value = 'true', options = {})
# ...
end
# Defaults are updated slightly in a new version of the library
def foo(value = true, options = {})
# ...
end
# My code might break in an unexpected way now
foo('true', bar: 'baz')
This is the problem which keyword arguments (new to ruby 2) were made to solve (provided that you control the method definition).
def foo(a: {}, b: {})
"a: #{a}, b: #{b}"
end
foo # => "a: {}, b: {}"
foo(a: 1, b: 2) # => "a: 1, b: 2"
foo(a: 3) # => "a: 3, b: {}"
foo(b: 4) # => "a: {}, b: 4"
You could set defaults to nil then handle the actual defaulting of values within the body of the method. ie.,
def my_method(first=nil, second=nil)
first_value = first || 1
second_value = second || 2
end
This allows you to pass 'nil' when you want that value to be its default. For example,
my_method(nil, 'second')
Now 'first_value' is 1 and second_value is 'second'.
edit: though 'nil' is really non-descriptive of the action of making the method use its default value. Consider:
def my_method(first=:default, second=:default)
first_value = (first == :default ? 1 : first)
second_value = (second== :default ? 2 : second)
end
Then you can do:
my_method(:default, 'second')
(but really Sergio's answer is the best =) )
You can just refactor the code to something like this, so it gets assigned to the default value only if the named parameter isn't provided a value.
def my_method(foo, bar)
foo ||= {}; bar ||= {};
#=> Do something with foo and bar now.
end
What ||= operator does is, it assigns the value on the right to the variable on the left if the variable isn't initialized or has nil value.
You can now call it like this
my_method(nil, "I'm bar");
If by any chance, you want to pass nil as a value, then this will fail. Sergio Tulentsev's answer is the way to go. I'd have suggested the same had I known it.

What do c == self and yield do?

Can you help me understand what this class does and how we can make use of it?
class Integer
def myt
c=0
until c == self
yield(c)
c+=1
end
self
end
end
Thank you.
x = Integer.new
x.myt
I tried to test it but it doesn't work. Error is: "no block given (yield)"
Also, in my book it says to test like this:
5.myt (|| puts "I'm on iteration #{i}! "} but it also gives an error - not sure why or what this line of code means.
allonhadaya and PNY did a good job explaining the purpose (enumeration) of the myt method.
Regarding your two questions mentioned in the title:
1.) What does 'c == self' do?
The '==' operator checks whether the integer c and Integer object you instantiate, are equal in value. If they are, the expression evaluates to true.
2.) What does 'yield' do?
The 'yield' statement passes control from the current method to a block which has been provided to the method. Blocks are ruby's implementation of a closure which, simple put, means that a method can be "extended" by calling the method with a block of additional code as long as the method supports a block (ie. incorporates yield statements)
The method seems to be a times implementation.
Basically 5.times { |i| puts i } and 5.myt { |i| puts i } will do exactly the same thing.
First, it sets a counter to 0, c = 0. Then you have a conditional where it checks if c is equal with self which will always be the integer attached to the method myt. It, then yields the counter and return self when is done.
Looks like it enumerates the values between zero inclusively and self exclusively.
allon#ahadaya:~$ irb
irb(main):001:0> class Integer
irb(main):002:1> def myt
irb(main):003:2> c=0
irb(main):004:2> until c == self
irb(main):005:3> yield(c)
irb(main):006:3> c+=1
irb(main):007:3> end
irb(main):008:2> self
irb(main):009:2> end
irb(main):010:1> end
=> nil
irb(main):011:0> 5.myt { |i| puts i }
0
1
2
3
4
=> 5
irb(main):012:0>
Using the example your book gave --
5.myt {|i| puts "I'm on iteration #{i}! "}
#You were missing an object in the pipes and a curly bracket before the pipes (not parentheses)
Allows you to see the internal workings of your myt method. Initializing variable c with a value of 0 the method executes an until look until the condition "c == self" is satisfied. Self references the object, here 5, which the method is acting on.
Therefore ...
def myt
until c == 5 #Until this is true
yield(c) #Do this .. here yield will do whatever the block specified
c+=1 #Increment on each iteration the value of variable c by 1
end #closing the until loop
self #return self
end
The yield within the method passes control from your method to the parameter, a block, back to the method.
Yield therefore allows you to build methods which can have similar patterns but with block you customize it to do your particular need.
If instead of putting each number maybe all you want to do is put the odd integers between 0 and the integer you call the method on --
5.myt {|i| puts i if i.odd?} # returns I am odd: 1 and I am odd: 3
I would suggest that you write your own blocks here to see how yield works and how you can keep the same method but pass in different blocks and create different method outputs!

Use of yield and return in Ruby

Can anyone help me to figure out the the use of yield and return in Ruby. I'm a Ruby beginner, so simple examples are highly appreciated.
Thank you in advance!
The return statement works the same way that it works on other similar programming languages, it just returns from the method it is used on.
You can skip the call to return, since all methods in ruby always return the last statement. So you might find method like this:
def method
"hey there"
end
That's actually the same as doing something like:
def method
return "hey there"
end
The yield on the other hand, excecutes the block given as a parameter to the method. So you can have a method like this:
def method
puts "do somthing..."
yield
end
And then use it like this:
method do
puts "doing something"
end
The result of that, would be printing on screen the following 2 lines:
"do somthing..."
"doing something"
Hope that clears it up a bit. For more info on blocks, you can check out this link.
yield is used to call the block associated with the method. You do this by placing the block (basically just code in curly braces) after the method and its parameters, like so:
[1, 2, 3].each {|elem| puts elem}
return exits from the current method, and uses its "argument" as the return value, like so:
def hello
return :hello if some_test
puts "If it some_test returns false, then this message will be printed."
end
But note that you don't have to use the return keyword in any methods; Ruby will return the last statement evaluated if it encounters no returns. Thus these two are equivelent:
def explicit_return
# ...
return true
end
def implicit_return
# ...
true
end
Here's an example for yield:
# A simple iterator that operates on an array
def each_in(ary)
i = 0
until i >= ary.size
# Calls the block associated with this method and sends the arguments as block parameters.
# Automatically raises LocalJumpError if there is no block, so to make it safe, you can use block_given?
yield(ary[i])
i += 1
end
end
# Reverses an array
result = [] # This block is "tied" to the method
# | | |
# v v v
each_in([:duck, :duck, :duck, :GOOSE]) {|elem| result.insert(0, elem)}
result # => [:GOOSE, :duck, :duck, :duck]
And an example for return, which I will use to implement a method to see if a number is happy:
class Numeric
# Not the real meat of the program
def sum_of_squares
(to_s.split("").collect {|s| s.to_i ** 2}).inject(0) {|sum, i| sum + i}
end
def happy?(cache=[])
# If the number reaches 1, then it is happy.
return true if self == 1
# Can't be happy because we're starting to loop
return false if cache.include?(self)
# Ask the next number if it's happy, with self added to the list of seen numbers
# You don't actually need the return (it works without it); I just add it for symmetry
return sum_of_squares.happy?(cache << self)
end
end
24.happy? # => false
19.happy? # => true
2.happy? # => false
1.happy? # => true
# ... and so on ...
Hope this helps! :)
def cool
return yield
end
p cool {"yes!"}
The yield keyword instructs Ruby to execute the code in the block. In this example, the block returns the string "yes!". An explicit return statement was used in the cool() method, but this could have been implicit as well.

how do i pass a block to a method whose name ends with equals sign?

this sounds weird doesn't it?
class Dummy
def foo=(value); end
end
Dummy.new.foo = 1 { |x| x } # => syntax error
Dummy.new.foo=(1) { |x| x } # => syntax error
i tried every permutation of blanks, parenthesis, commas; no luck. i'm puzzled. i never suspected methods ending with '=' were special. is it a bug? is it intended? if intended, why? is it documented? where? please share insight.
thanks
ps. ruby is 1.9.2p290 (2011-07-09 revision 32553) [x86_64-darwin11.0.1]
The syntax sugar for methods ending in = does make it special. You can still do things like pass multiple arguments to that method, or pass a block, but not in any pretty or convenient manner:
class Foo
def bar=(a,b=nil)
p [a,b]
if block_given?
yield "hi"
else
puts "No block"
end
end
end
f = Foo.new
f.bar = 42
#=> [42, nil]
#=> No block
f.bar = 42, 17
#=> [[42,17], nil]
#=> No block
f.send(:bar=,42,17) do |x|
puts "x is #{x.inspect}"
end
#=> [42, 17]
#=> x is "hi"
Another way in which these methods are special is that when called with the syntax sugar they evaluate to the right hand value, not the return value of the method:
class Foo
def bar=(a)
return 17 # really explicit
end
end
f = Foo.new
x = (f.bar = 42)
p x
#=> 42
x = f.send(:bar=,42)
p x
#=> 17
It's not so much that the methods themselves are special, but more to do with how Ruby deals with assignments (like foo = bar). First the right hand side is evalulated, then the left hand side is evaluated and the appropriate action is taken. If the left hand side is an object attribute, then the appropriate setter method is called.
So in your example:
Dummy.new.foo = 1 { |x| x }
First ruby tries to evalulate 1 { |x| x }, which is what causes the syntax error.
Dummy.new.foo=something doesn't actually mean "call the method named foo=", but actually means something more like "evalualate something, and then determine what `Dummy.new.foo is, and if it looks like an object attribute, add = to the name and call that method". This is why Dummy.new.foo= and Dummy.new.foo = both work the same way.
You can call these methods using send, and can pass a block with this:
Dummy.new.send "foo=", 2 do
puts "HI"
end
This is because with send you can explicitly name the method you want to call.
Of course the end result is that methods ending in = seem to have some "special" behaviour you need to be aware of, but it might be useful to understand what's actually going on.

'pass parameter by reference' in Ruby?

In Ruby, is it possible to pass by reference a parameter with value-type semantics (e.g. a Fixnum)?
I'm looking for something similar to C#'s 'ref' keyword.
Example:
def func(x)
x += 1
end
a = 5
func(a) #this should be something like func(ref a)
puts a #should read '6'
Btw. I know I could just use:
a = func(a)
You can accomplish this by explicitly passing in the current binding:
def func(x, bdg)
eval "#{x} += 1", bdg
end
a = 5
func(:a, binding)
puts a # => 6
Ruby doesn't support "pass by reference" at all. Everything is an object and the references to those objects are always passed by value. Actually, in your example you are passing a copy of the reference to the Fixnum Object by value.
The problem with the your code is, that x += 1 doesn't modify the passed Fixnum Object but instead creates a completely new and independent object.
I think, Java programmers would call Fixnum objects immutable.
In Ruby you can't pass parameters by reference. For your example, you would have to return the new value and assign it to the variable a or create a new class that contains the value and pass an instance of this class around. Example:
class Container
attr_accessor :value
def initialize value
#value = value
end
end
def func(x)
x.value += 1
end
a = Container.new(5)
func(a)
puts a.value
You can try following trick:
def func(x)
x[0] += 1
end
a = [5]
func(a) #this should be something like func(ref a)
puts a[0] #should read '6'
http://ruby-doc.org/core-2.1.5/Fixnum.html
Fixnum objects have immediate value. This means that when they are assigned or
passed as parameters, the actual object is passed, rather than a reference to
that object.
Also Ruby is pass by value.
However, it seems that composite objects, like hashes, are passed by reference:
fp = {}
def changeit(par)
par[:abc] = 'cde'
end
changeit(fp)
p fp
gives
{:abc=>"cde"}

Resources