Pass arguments by reference to a block with the splat operator
In Ruby when you write
x = value
you are creating a new local variablex
whether it existed previously or not (if it existed the name is simply rebound and the original value remains untouched). So you won't be able to change a variable in-place this way.Integers are immutable. So if you send an integer there is no way you can change its value. Note that you can change mutable objects (strings, hashes, arrays, ...):
def method
a = [1, 2, "hello"]
yield(*a)
p a
end
method { |x,y,z| z[1] = 'u' }
# [1, 2, "hullo"]
Note: I've tried to answer your question, now my opinion: updating arguments in methods or blocks leads to buggy code (you have no referential transparency anymore). Return the new value and let the caller update the variable itself if so inclined.
Why do Ruby procs/blocks with splat arguments behave differently than methods and lambdas?
There are two types of Proc
objects: lambda
which handles argument list in the same way as a normal method, and proc
which use "tricks" (Proc#lambda?). proc
will splat an array if it's the only argument, ignore extra arguments, assign nil
to missing ones. You can partially mimic proc
behavior with lambda
using destructuring:
->((x, y)) { [x, y] }[1] #=> [1, nil]
->((x, y)) { [x, y] }[[1, 2]] #=> [1, 2]
->((x, y)) { [x, y] }[[1, 2, 3]] #=> [1, 2]
->((x, y)) { [x, y] }[1, 2] #=> ArgumentError
How do I pass an array to a method that accepts an attribute with a splat operator?
Just put a splat when calling the method?
sum(*[1,2,3])
Map splat arguments over method parameters
def f(params,*args)
# elements to be assigned to splat parameter
splat = args.count - params.count + 1
# will throw an error if splat < 0 as that means not enough inputs given
params.map{ |p|
[ p[1] , ( p.first == :rest ? args.shift(splat) : args.shift ) ]
}
end
Examples
def splatter(x,*y,z)
# some code
end
f(method(:splatter).parameters, 1,2,3,4)
#=>[[:x, 1], [:y, [2, 3]], [:z, 4]]
def splatter(x,y,*z)
# some code
end
f(method(:splatter).parameters, 1,2,3,4)
# => [[:x, 1], [:y, 2], [:z, [3, 4]]]
def splatter(x,*z)
# some code
end
f(method(:splatter).parameters, 1)
# => [[:x, 1], [:z, []]]
splat operator on hash for keyword arguments in ruby method definition
Yeah... that's not a thing you can do.
**foo
in an argument list is how you collect a kwargs hash, so it can't also be how you inject one.
More importantly, the main point of kwargs is that they explode the hash into local variables -- that can't work if it's expanding a hash at runtime.
The closest you could get would be something like:
def initialize(**values)
values = DEFAULTS.merge(values)
raise "..." unless (values.keys - DEFAULTS.keys).empty?
@a = values[:a]
@b = values[:b]
end
Get value into Ruby code block without it being the last statement
You can implicitly return values using instance variables, like it is done in Rails:
def bar
yield
puts "The block has assigned @value to #{@value}"
end
bar do
@value = 42
nil # Do not return value intentionally
end
This code outputs:
The block has assigned @value to 42
UPDATE
Another nice option is to use helper methods, many frameworks do it too:
def bar
def render(value)
@value = value
end
yield
puts "The block has rendered #{@value}"
end
bar do
render 42
nil # Do not return value intentionally
end
This code outputs:
The block has rendered 42
UPDATE 2
Please see an important addition from @Marek Lipka https://stackoverflow.com/a/19542149/203174
Examples of using splat operator *
I can show you a few examples of how it's used.
Say you have a method which takes a few arguments
def foo(a,b,c)
And you want to call this method, provide the values for arguments using an array. You can write:
foo(*[1,2,3])
Another situation is you want to monkey patch a record but don't want to break the original functionality. For example, overwriting save
in a rails model:
def save(*args)
# do something custom here
super(*args)
end
This is to say "I don't care about the arguments to this function, but I want to make sure they are all passed to the super
call. "
How to annotate type using the splat operator
Because it seems that in the above use-case, macro versions of reflection functions couldn't reach the right argument types, using original function instead of macro, could be helpful:
f(x, y) = x^2 + y^2
vec = [1.0, 2.0, 'a']
@code_warntype(f(vec[1:2]...)) # => Nothing
code_warntype(f,map(typeof,vec[1:2]))
# Variables:
# x::Float64
# y::Float64
# .....
This logic is true for all reflection macros, using their variant function with a (function, collection of types)
.
references:
- The macro @code_warntype has function variant: @code_warntype
- How macros generated: macro generator
- Util function to reach types: gen_call_with_extracted_types
Related Topics
Getting a Rogue Iteration from My .Each Loop
Instance_Eval Does Not Work with Do/End Block, Only with {}-Blocks
How to Convert Character Code to What I Want
Return Values Were Not Extracted in Ruby
Ruby Parenthesis Syntax Exception with I++ ++I
Using Poltergeist with a Proxy
How to 'Join' an Array Adding to the Beginning of the Resulting String the First Character to Join
Ruby Facebook Graph API Appsecret_Proof
How to Understand the #Dup and #Clone Operate on Objects Which Referencing Other Objects
Rails 404 Error for Stylesheet or JavaScript Files
Calling Instance Variables Without @
Stubbing Controller Actions in Rspec Request Specs
How to Sort So That "Vitamin B12" Is Not in Front of "Vitamin B6"
Undefined Method Error When Creating Delayed_Job Workers with Script/Delay_Job
Why Are Parenthesis Sometimes Required in Ruby