Optional arguments with default value in Ruby

后端 未结 3 1819
既然无缘
既然无缘 2020-12-10 19:13

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

相关标签:
3条回答
  • 2020-12-10 19:50

    So you're trying to implement keyword arguments? This is supposed to be a new feature in Ruby 2.0, but you can try to mimic it in 1.9.x with a hash of arguments instead. Here's a post that discusses how you can accomplish that, which gives the following code sample:

    def foo(options = {})
      options = {bar: 'bar'}.merge(options)
      puts "#{options[:bar]} #{options[:buz]}"
    end
    
    foo(buz: 'buz') # => 'bar buz'
    
    0 讨论(0)
  • 2020-12-10 20:02

    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}
    
    0 讨论(0)
  • 2020-12-10 20:05

    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)
    
    0 讨论(0)
提交回复
热议问题