**args as function parameter in Ruby - ruby

I understand that **args is interpreted as a hash containing all key value pairs passed to a function but I don't understand why that would be preferred over a typical parameter. For example, I have the following two functions.
def test(some_string, hash)
puts hash
puts hash.class # => Hash
end
def test_two(some_string, **hash)
puts hash
puts hash.class # => Hash
end
calling test("test string", a: 1, b: 2) or test_two("test string", a: 1, b: 2) produces the exact same result. What is the benefit of using ** as a parameter value?

Ruby 2.7 started more clearly differentiating between keyword arguments and regular hashes. **args is for keyword arguments. Some implications:
def test3(some_string, foo:, **args)
puts args
end
test3('a', foo: 'b', bar: 'c') # => {:bar=>"c"}
works as expected, however
def test3(some_string, foo:, hash)
puts args
end # => syntax error
def test3(some_string, hash, foo:)
puts args
end # works so far
test3('a', foo: 'b', bar: 'c')
# warning: Passing the keyword argument as the last hash parameter is deprecated
# ArgumentError (missing keyword: :foo)
Once you upgrade to ruby 3, the warnings turn to errors.

The benefit of having the double splat operator ** as argument is mainly that you can avoid passing any argument at all. Much like the same of what happens when you use the single splat operator *.
Using your examples and calling:
test "s"
# raises ArgumentError (wrong number of arguments (given 1, expected 2))
test_two "s"
# works, prints `{} Hash`
This is useful in methods where you want to have a "main" argument, and usually some options, without cluttering the arguments space.
For example, imagine a CSV row parser:
def parse_row_1(row, **options)
separator = options.fetch :separator, ","
quote_char = options.fetch :quote_char, null
# ...
end
def parse_row_2(row, separator = ",", quote_char = null)
# ...
end
# To parse a string like this:
s = "'ABC','123','DEF'"
# With ** method you can do just this:
parse_row_1 s, quote_char: "'"
# Without ** you must specify every time the arguments, because they are positional:
parse_row_2 s, ",", "'"

Related

A method with an optional parameter

Is there a way to make a method that can accept a parameter, but can also be called without one, in which case the parameter is regarded nil like the following?
some_func(variable)
some_func
def some_func(variable = nil)
...
end
Besides the more obvious option of parameters with default values, that Sawa has already shown, using arrays or hashes might be handy in some cases. Both solutions preserve nil as a an argument.
1. Receive as array:
def some_func(*args)
puts args.count
end
some_func("x", nil)
# 2
2. Send and receive as hash:
def some_func(**args)
puts args.count
end
some_func(a: "x", b: nil)
# 2
You can also use a hash as argument and have more freedom:
def print_arg(args = {})
if args.has_key?(:age)
puts args[:age]
end
end
print_arg
# =>
print_arg(age: 35, weight: 90)
# => 35

How to convert method or lambda to non-lambda proc

As shown in the ruby example below, I can't call a lambda with wrong number of arguments as Proc created from a Method because it is strict about the number of arguments:
# method with no args
def a; end
instance_eval(&method(:a))
# ArgumentError: wrong number of arguments (1 for 0)
method(:a).to_proc.call(1, 2, 3)
# ArgumentError: wrong number of arguments (3 for 0)
method(:a).to_proc.lambda?
# => true
How do I get a Proc that is not a lambda from either a Proc that is or from a Method?
There is no way to do this.
Besides the argument passing, I wonder what you would expect from a return in the method. It can only behave in lambda way...
If you really have to do this, you will need to build your own block, e.g.
Proc.new{ a }
For a more generic way, you'll have to check the arity of the method and pass only the required parameters.
Try wrapping it in a non-lambda Proc, like so:
l = lambda {|a,b| puts "a: #{a}, b: #{b}" }
p = proc {|a,b| l.call(a,b) }
l.lambda?
#=> true
l.arity
#=> 2
l.call("hai")
#=> ArgumentError: wrong number of arguments (1 for 2)
l.call("hai", "bai", "weee", "womp", "woo")
#=> ArgumentError: wrong number of arguments (5 for 2)
p.lambda?
#=> false
p.arity
#=> 2
p.call("hai")
#=> a: hai, b:
p.call("hai", "bai", "weee", "womp", "woo")
#=> a: hai, b: bai
Convert Lambda to Proc
Here's a work-around that wraps a lambda or a method call in a Proc and uses splat to handle any number of arguments:
def lambda_to_proc(lambda)
Proc.new do |*args|
diff = lambda.arity - args.size
diff = 0 if diff.negative?
args = args.concat(Array.new(diff, nil)).take(lambda.arity)
lambda.call(*args)
end
end
This would always work no matter the number of arguments passed; Extra arguments will be dropped and nil will replace the missing arguments.
Example:
# lambda with two args
some_lambda = -> (a,b) { [a, b] }
# method with no args
def some_method; "hello!"; end
lambda_to_proc(some_lambda).call(5)
# => [5, nil]
lambda_to_proc(method(:some_method)).call(1,2,3)
# => "hello!"
Note: There is no direct way to convert a lambda or a method call to a proc. This is just a workaround and obviously slower than the real deal (because of wrapping one call in another).

What are the different possibilities of passing parameters into ruby methods? param/hashlist/array/aproc?

I am trying to understand Ruby in more depth and was reading:
http://www.zenspider.com/Languages/Ruby/QuickRef.html#25
However, I dont understand what the following means in that definition:
parameters := ( [param]* [, hashlist] [*array] [&aProc] )
I know "param" is any number of parameters specified, and then i get lost what the remainder means?
For example, I have:
def doIt(param1, param2, param3)
end
and in this case [param]* is equal to param1, param2, param3...so where does hashlist come in? and *array and &aProc?
Could someone please clarify this for me
If the last argument of a method is a non-empty hash literal, you can pass it like this
def foo(x, y, the_hash)
p the_hash['key2']
end
foo(0, 0, :key1 => 'val1', 'key2' => 42) # 42
instead of the normal way:
foo(0, 0, { :key1 => 'val1', 'key2' => 42 }) # 42
Usually, the hash is defaulted to {} (def foo(x, y, the_hash = {})) so passing an empty hash fits to this scheme.
Additionally, you can specify one "catch-all" (splat) argument which will become an array of all arguments not already assigned to normal arguments:
def foo(p1, *rest)
p rest
end
foo(0) # "[]"
foo(0, 23, 42) # "[23, 42]"
Or, e.g.
def foo(p1, *rest, p2)
p rest
end
foo(0, 100) # "[]"
foo(0, 100, 23, 42) # "[100, 23]"
You cannot have splat arguments before arguments with default value. Therefore, the hash argument syntax and the splat argument are rarely used in combination.
Finally, in your method definition you can have as last argument an identifier prefixed with & which will point to the block at the method invocation (its Proc object) or be nil if there is none. This is normally not needed if you just want to invoke the block -- you can use yield for that.
def foo(&p)
p.call
end
foo { puts "hello" } # hello
vs.
def foo
yield
end
foo { puts "hello" } # hello

How can I programmatically pass args to yield in Ruby?

How can I pass a variable number of args to a yield.
I don't want to pass an array (as the following code does), I'd actually like to pass them as a programmatic number of args to the block.
def each_with_attributes(attributes, &block)
results[:matches].each_with_index do |match, index|
yield self[index], attributes.collect { |attribute| (match[:attributes][attribute] || match[:attributes]["##{attribute}"]) }
end
end
Use the splat-operator * to turn the array into arguments.
block.call(*array)
or
yield *array
Use the asterisk to expand an array into its individual components in an argument list:
def print_num_args(*a)
puts a.size
end
array = [1, 2, 3]
print_num_args(array);
print_num_args(*array);
Will print:
1
3
In the first case the array is passed, in the second case each element is passed separately. Note that the function being called needs to handle variable arguments such as the print_num_args does, if it expects a fixed size argument list and received more or less than expected you will get an exception.
Asterisk will expand an array to individual arguments in ruby:
def test(a, b)
puts "#{a} + #{b} = #{a + b}"
end
args = [1, 2]
test *args

What do the different brackets in Ruby mean?

In Ruby, what's the difference between {} and []?
{} seems to be used for both code blocks and hashes.
Are [] only for arrays?
The documention isn't very clear.
It depends on the context:
When on their own, or assigning to a variable, [] creates arrays, and {} creates hashes. e.g.
a = [1,2,3] # an array
b = {1 => 2} # a hash
[] can be overridden as a custom method, and is generally used to fetch things from hashes (the standard library sets up [] as a method on hashes which is the same as fetch)
There is also a convention that it is used as a class method in the same way you might use a static Create method in C# or Java. e.g.
a = {1 => 2} # create a hash for example
puts a[1] # same as a.fetch(1), will print 2
Hash[1,2,3,4] # this is a custom class method which creates a new hash
See the Ruby Hash docs for that last example.
This is probably the most tricky one -
{} is also syntax for blocks, but only when passed to a method OUTSIDE the arguments parens.
When you invoke methods without parens, Ruby looks at where you put the commas to figure out where the arguments end (where the parens would have been, had you typed them)
1.upto(2) { puts 'hello' } # it's a block
1.upto 2 { puts 'hello' } # syntax error, ruby can't figure out where the function args end
1.upto 2, { puts 'hello' } # the comma means "argument", so ruby sees it as a hash - this won't work because puts 'hello' isn't a valid hash
Another, not so obvious, usage of [] is as a synonym for Proc#call and Method#call. This might be a little confusing the first time you encounter it. I guess the rational behind it is that it makes it look more like a normal function call.
E.g.
proc = Proc.new { |what| puts "Hello, #{what}!" }
meth = method(:print)
proc["World"]
meth["Hello",","," ", "World!", "\n"]
Broadly speaking, you're correct. As well as hashes, the general style is that curly braces {} are often used for blocks that can fit all onto one line, instead of using do/end across several lines.
Square brackets [] are used as class methods in lots of Ruby classes, including String, BigNum, Dir and confusingly enough, Hash. So:
Hash["key" => "value"]
is just as valid as:
{ "key" => "value" }
The square brackets [ ] are used to initialize arrays.
The documentation for initializer case of [ ] is in
ri Array::[]
The curly brackets { } are used to initialize hashes.
The documentation for initializer case of { } is in
ri Hash::[]
The square brackets are also commonly used as a method in many core ruby classes, like Array, Hash, String, and others.
You can access a list of all classes that have method "[ ]" defined with
ri []
most methods also have a "[ ]=" method that allows to assign things, for example:
s = "hello world"
s[2] # => 108 is ascii for e
s[2]=109 # 109 is ascii for m
s # => "hemlo world"
Curly brackets can also be used instead of "do ... end" on blocks, as "{ ... }".
Another case where you can see square brackets or curly brackets used - is in the special initializers where any symbol can be used, like:
%w{ hello world } # => ["hello","world"]
%w[ hello world ] # => ["hello","world"]
%r{ hello world } # => / hello world /
%r[ hello world ] # => / hello world /
%q{ hello world } # => "hello world"
%q[ hello world ] # => "hello world"
%q| hello world | # => "hello world"
a few examples:
[1, 2, 3].class
# => Array
[1, 2, 3][1]
# => 2
{ 1 => 2, 3 => 4 }.class
# => Hash
{ 1 => 2, 3 => 4 }[3]
# => 4
{ 1 + 2 }.class
# SyntaxError: compile error, odd number list for Hash
lambda { 1 + 2 }.class
# => Proc
lambda { 1 + 2 }.call
# => 3
Note that you can define the [] method for your own classes:
class A
def [](position)
# do something
end
def #rank.[]= key, val
# define the instance[a] = b method
end
end

Resources