Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Optional arguments with default value in Ruby

I would like to create a function that has optional arguments with default values

def my_function(a = nil, b=nil, c=500)

end

and call the function with the arguments I would like to specify only

my_function(b=100)

How do I accomplish this in Ruby 1.9.2?

like image 890
AdamNYC Avatar asked May 01 '13 16:05

AdamNYC


People also ask

How do I set the default value of an argument in Ruby?

Default arguments are easy to add, you simply assign them a default value with = ("equals") in the argument list. There's no limit to the number of arguments that you can make default. Let's take a look at the different ways we can call this method: greeting # > Hello, Ruby programmer.

What is * args in Ruby?

In the code you posted, *args simply indicates that the method accepts a variable number of arguments in an array called args . It could have been called anything you want (following the Ruby naming rules, of course).

What parameters are optional?

What are Optional Parameters? By definition, an Optional Parameter is a handy feature that enables programmers to pass less number of parameters to a function and assign a default value. Firstly, let us first understand what the word Optional Parameter means. Parameters are the names listed in the function definition.

What is default Ruby?

The default values are used only when you don't pass any other arguments. This kind of parameters is useful when we have parameters that most of the time have same values. A sequence of arguments must correspond to a sequence of parameters.


2 Answers

Arguments are bound to parameters like this:

  1. As long as there are unbound mandatory parameters at the beginning of the parameter list, bind arguments left-to-right
  2. As long as there are unbound mandatory parameters at the end of the parameter list, bind arguments right-to-left
  3. Any leftover arguments are bound to optional parameters left-to-right
  4. Any leftover arguments are collected into an array and bound to the splat argument
  5. A block is wrapped up into a Proc and bound to the block argument
  6. If there are any unbound parameters or leftover arguments, raise an ArgumentError

Here's an example:

def foo(mand1, mand2, opt1=:opt1, opt2=:opt2, *splat, mand3, mand4, &block)
  p local_variables.map {|v| "#{v} = #{eval(v.to_s)}" }
end

foo 1, 2, 3
# ArgumentError: wrong number of arguments (3 for 4+)

foo 1, 2, 3, 4
# mand1 = 1
# mand2 = 2
# opt1 = opt1
# opt2 = opt2
# splat = []
# mand3 = 3
# mand4 = 4
# block = 

foo 1, 2, 3, 4, 5
# mand1 = 1
# mand2 = 2
# opt1 = 3
# opt2 = opt2
# splat = []
# mand3 = 4
# mand4 = 5
# block = 

foo 1, 2, 3, 4, 5, 6
# mand1 = 1
# mand2 = 2
# opt1 = 3
# opt2 = 4
# splat = []
# mand3 = 5
# mand4 = 6
# block = 

foo 1, 2, 3, 4, 5, 6, 7
# mand1 = 1
# mand2 = 2
# opt1 = 3
# opt2 = 4
# splat = [5]
# mand3 = 6
# mand4 = 7
# block = 

foo 1, 2, 3, 4, 5, 6, 7, 8 do end
# mand1 = 1
# mand2 = 2
# opt1 = 3
# opt2 = 4
# splat = [5, 6]
# mand3 = 7
# mand4 = 8
# block = #<Proc:0x007fdc732cb468@(pry):42>

So, as you can see both from step 3 above and from the example, you cannot do this, because optional parameters are bound left-to-right, but you want to specify the middle argument.

Note that this has implications on API design: you should design your parameter lists in such a way that the most "unstable" optional parameters, i.e. the ones that a user most likely wants to supply themselves, are furthest to the left.

Ruby 2.0 now has keyword arguments, which is exactly what you are looking for:

def foo(m1, m2, o1=:o1, o2=:o2, *s, m3, m4, key1: :key1, key2: :key2, **keys, &b)
  puts local_variables.map {|v| "#{v} = #{eval(v.to_s)}" }
end

foo 1, 2, 3
# ArgumentError: wrong number of arguments (3 for 4+)

foo 1, 2, 3, 4
# m1 = 1
# m2 = 2
# o1 = o1
# o2 = o2
# s = []
# m3 = 3
# m4 = 4
# key1 = key1
# key2 = key2
# b = 
# keys = {}

foo 1, 2, 3, 4, 5
# m1 = 1
# m2 = 2
# o1 = 3
# o2 = o2
# s = []
# m3 = 4
# m4 = 5
# key1 = key1
# key2 = key2
# b = 
# keys = {}

foo 1, 2, 3, 4, 5, 6
# m1 = 1
# m2 = 2
# o1 = 3
# o2 = 4
# s = []
# m3 = 5
# m4 = 6
# key1 = key1
# key2 = key2
# b = 
# keys = {}

foo 1, 2, 3, 4, 5, 6, 7
# m1 = 1
# m2 = 2
# o1 = 3
# o2 = 4
# s = [5]
# m3 = 6
# m4 = 7
# key1 = key1
# key2 = key2
# b = 
# keys = {}

foo 1, 2, 3, 4, 5, 6, 7, 8
# m1 = 1
# m2 = 2
# o1 = 3
# o2 = 4
# s = [5, 6]
# m3 = 7
# m4 = 8
# key1 = key1
# key2 = key2
# b = 
# keys = {}

foo 1, 2, 3, 4, 5, 6, 7, 8, key1: 9
# m1 = 1
# m2 = 2
# o1 = 3
# o2 = 4
# s = [5, 6]
# m3 = 7
# m4 = 8
# key1 = 9
# key2 = key2
# b = 
# keys = {}

foo 1, 2, 3, 4, 5, 6, 7, 8, key1: 9, key2: 10
# m1 = 1
# m2 = 2
# o1 = 3
# o2 = 4
# s = [5, 6]
# m3 = 7
# m4 = 8
# key1 = 9
# key2 = 10
# b = 
# keys = {}

foo 1, 2, 3, 4, 5, 6, 7, 8, key1: 9, key2: 10, key3: 11
# m1 = 1
# m2 = 2
# o1 = 3
# o2 = 4
# s = [5, 6]
# m3 = 7
# m4 = 8
# key1 = 9
# key2 = 10
# b = 
# keys = {:key3=>11}

foo 1, 2, 3, 4, 5, 6, 7, 8, key1: 9, key2: 10, key3: 11, key4: 12 do end
# m1 = 1
# m2 = 2
# o1 = 3
# o2 = 4
# s = [5, 6]
# m3 = 7
# m4 = 8
# key1 = 9
# key2 = 10
# b = #<Proc:0x007fdc75135a48@(pry):77>
# keys = {:key3=>11, key4=>12}
like image 126
Jörg W Mittag Avatar answered Oct 26 '22 23:10

Jörg W Mittag


You cannot do that (or something similar) in Ruby < 2.0. The best you could do is:

def my_function(h = {})
  h[:c] ||= 500
  # Use h[:a], h[:b], h[:c]
  ...
end

my_function(b: 100)
like image 29
sawa Avatar answered Oct 27 '22 01:10

sawa