(or for that matter, specifications how ‘splat’ behaves
and the ‘&’ operator before blocks)
Splat turns an array into an argument list, or vice versa.
def test1(*args)
p args
end
test1(1,2,3) # prints the array [1, 2, 3]
def test2(a, b, c)
p a, b, c
end
arg2 = [4,5,6]
test2(*arg2) # prints 4 then 5 then 6
You can use splat to “mop up” remaining arguments:
def test3(a, b, *rest)
p a, b, rest
end
test3(7, 8) # prints 7 then 8 then []
test3(7, 8, 9) # prints 7 then 8 then [9]
You can also use it for multiple assignments:
a, b = *[10, 11]
and you can use it in blocks: foo { |x,*y| … }
Where it gets confusing is special “autosplat” magic which happens for
multiple assignments and blocks where one side is a single array.
a, b = [10, 11]
p a, b # [10, 11]
foo = 12, 13, 14
p foo # [12, 13, 14]
arr = [ [“foo”,1], [“bar”,2], [“baz”,3] ]
arr.each { |x,y| p x, y }
p arr.inject(0) { |accum,(k,v)| accum + k.length + v }
Fortunately, autosplat doesn’t happen for method calls, AFAIK. See
http://innig.net/software/ruby/closures-in-ruby.rb (section 4) for more
info.
As for the & operator, this is pretty simple. It’s just a way of turning
the “hidden” block argument into a concrete argument.
The following two are equivalent:
def doit1(x,y)
yield x+y
end
def doit2(x,y,&blk)
blk.call(x+y)
end
Conversely, if you have an existing proc/lambda type of object, you can
pass it instead of an inline block:
arr = [“foo”, “bar”]
arr.each { |elem| puts “hello #{elem}” }
callback = lambda { |elem| puts “hello #{elem}” }
arr.each(&callback)
And hence the blk itself can be passed around as an object. So:
def doit3(arr)
arr.each { |elem| yield elem }
end
becomes simply
def doit4(arr,&blk)
arr.each(&blk)
end
Personally, I found blocks and yield much easier to understand once I
saw they were just passing a function as an extra argument, and calling
that argument.