【问题标题】:Named parameters in Ruby 2Ruby 2 中的命名参数
【发布时间】:2013-02-24 20:00:39
【问题描述】:

我不完全理解 Ruby 2.0 中的命名参数是如何工作的。

def test(var1, var2, var3)
  puts "#{var1} #{var2} #{var3}"
end

test(var3:"var3-new", var1: 1111, var2: 2222) #wrong number of arguments (1 for 3) (ArgumentError)

它被视为哈希。这很有趣,因为要在 Ruby 2.0 中使用命名参数,我必须为它们设置默认值:

def test(var1: "var1", var2: "var2", var3: "var3")
  puts "#{var1} #{var2} #{var3}"
end

test(var3:"var3-new", var1: 1111, var2: 2222) # ok => 1111 2222 var3-new

这与 Ruby 之前使用默认参数值的行为非常相似:

def test(var1="var1", var2="var2", var3="var3")
  puts "#{var1} #{var2} #{var3}"
end

test(var3:"var3-new", var1: 1111, var2: 2222) # ok but ... {:var3=>"var3-new", :var1=>1111, :var2=>2222} var2 var3

我知道为什么会发生这种情况以及它是如何工作的。

但我只是好奇,必须如果我使用命名参数,我必须为参数使用默认值吗?

那么,谁能告诉我这两者有什么区别?

def test1(var1="default value123")
  #.......
end

def test1(var1:"default value123")
  #.......
end

【问题讨论】:

标签: ruby ruby-2.0


【解决方案1】:

您发布的最后一个示例具有误导性。我不同意这种行为与以前的行为相似。最后一个示例将参数哈希作为第一个可选参数传入,这是另一回事!

如果不想有默认值,可以使用nil

如果您想阅读一篇好的文章,请参阅“Ruby 2 Keyword Arguments”。

【讨论】:

【解决方案2】:

把它留在这里是因为它对我帮助很大。

示例

假设你有这个:

def foo(thing, to_print)
  if to_print
    puts thing
  end
end


# this works
foo("hi", true)
# hi
# => nil

所以你尝试添加参数名称,如下所示:

foo(thing: "hi", to_print: true)
# foo(thing: "hi", to_print: true)
# ArgumentError: wrong number of arguments (given 1, expected 2)
# from (pry):42:in `foo'

但不幸的是它出错了。

解决方案

只需在每个参数的末尾添加一个:

def foo2(thing:, to_print:)
  if to_print
    puts thing
  end
end


foo2(thing: "hi", to_print: true)
# hi
# => nil

它有效!

【讨论】:

    【解决方案3】:

    这在所有其他答案中都有,但我想提取这个本质。

    有四种参数:

                 Required     Optional
    Positional | def PR(a)  | def PO(a=1) |
    Keyword    | def KR(a:) | def KO(a:1) |
    

    定义函数时,位置参数在关键字参数之前指定,必填参数在可选参数之前。

    irb(main):006:0> def argtest(a,b=2,c:,d:4)
    irb(main):007:1> p [a,b,c,d]
    irb(main):008:1> end
    => :argtest
    
    irb(main):009:0> argtest(1,c: 3)
    => [1, 2, 3, 4]
    
    irb(main):010:0> argtest(1,20,c: 3,d: 40)
    => [1, 20, 3, 40]
    

    编辑:正如其他人所提到的,必需的关键字参数(没有默认值)是 Ruby 2.1.0 中的新参数。

    【讨论】:

      【解决方案4】:

      根据“Ruby 2.0.0 by Example”你必须有默认值:

      在 Ruby 2.0.0 中,关键字参数必须具有默认值,否则必须在末尾被 **extra 捕获。

      【讨论】:

      【解决方案5】:

      我认为可以通过明确的示例来解释您更新后的问题的答案。在下面的示例中,您有明确顺序的可选参数:

      def show_name_and_address(name="Someone", address="Somewhere")
        puts "#{name}, #{address}"
      end
      
      show_name_and_address
      #=> 'Someone, Somewhere'
      
      show_name_and_address('Andy')
      #=> 'Andy, Somewhere'
      

      命名参数方法不同。它仍然允许您提供默认值,但它允许调用者确定要提供哪些参数(如果有):

      def show_name_and_address(name: "Someone", address: "Somewhere")
        puts "#{name}, #{address}"
      end
      
      show_name_and_address
      #=> 'Someone, Somewhere'
      
      show_name_and_address(name: 'Andy')
      #=> 'Andy, Somewhere'
      
      show_name_and_address(address: 'USA')
      #=> 'Someone, USA'
      

      虽然这两种方法在不提供参数时确实相似,但当用户为方法提供参数时它们会有所不同。使用命名参数,调用者可以指定提供哪个参数。具体来说,最后一个示例(仅提供地址)在第一个示例中不太可实现;您只能通过向该方法提供两个参数来获得类似的结果。这使得命名参数方法更加灵活。

      【讨论】:

      • 我觉得这个答案比选择的正确答案要好很多,应该标记为正确答案。
      • 为了清楚起见,我只想补充一点,如果您调用第一种方式(使用默认但不是 named 参数),就好像它被命名为参数一样,您不会收到错误消息。相反,ruby 将其解释为传入的单个 hash 参数(到第一个 arg)
      • @HariKaramSingh 也许你可以解释得更多,但我相信你已经离题了。在第一种方法中,Ruby 使用 positional 匹配:作为调用的第一个参数传入的任何对象在方法本地都称为“名称”,而第二个传入的任何对象都称为“地址”。如果您尝试传递哈希(例如,show_name_and_address(address: 'Somewhere', name: 'Someone")),结果将是 'name' 将接收整个 has 而 'address' 将具有默认值。跨度>
      • @AndyV 我认为我们在说同样的话。另一种说法可能是,在 ruby​​ 中,带有哈希字面量的函数调用看起来与带有命名参数的函数调用完全相同 - 唯一的区别在于函数定义 - 这对于新手来说可能有点混乱。
      【解决方案6】:

      Ruby 2.1.0 开始,您不再需要为命名参数设置默认值。如果您省略参数的默认值,调用者将需要提供它。

      def concatenate(val1: 'default', val2:)
        "#{val1} #{val2}"
      end
      
      concatenate(val2: 'argument')
      #=> "default argument"
      
      concatenate(val1: 'change')
      #=> ArgumentError: missing keyword: val2
      

      给定:

      def test1(var1="default value123")
        var1
      end
      
      def test2(var1:"default value123")
        var1
      end
      

      不传递参数时它们的行为方式相同:

      test1
      #=> "default value123"
      
      test2
      #=> "default value123"
      

      但是当参数被传递时,它们的行为会大不相同:

      test1("something else")
      #=> "something else"
      
      test2("something else")
      #=> ArgumentError: wrong number of arguments (1 for 0)
      
      
      test1(var1: "something else")
      #=> {:var1=>"something else"}
      
      test2(var1: "something else")
      #=> "something else"
      

      【讨论】:

        【解决方案7】:

        我同意你的观点,要求默认值作为使用命名参数的代价是很奇怪的,显然 Ruby 维护者同意我们的观点! Ruby 2.1 将drop the default value requirement as of 2.1.0-preview1

        【讨论】:

        • 猜猜这很糟糕!如果你定义了一个接受关键字参数的函数,然后有人用一个普通参数调用它......你会得到ArgumentError: wrong number of arguments (1 for 0),而不是,哦,说ArgumentError: missing keyword for keyword argument (argument 1 of 1)
        • 我认为一个常见的模式是在参数列表太长或选项太多时使用命名参数。在这些情况下,您可能希望阻止人们只是将值作为参数传递并假设他们知道正确的顺序。
        • 我使用它们来避免这种反模式:def f(arg1, arg2, flag = true) 我改为使用def f(arg1, arg2, flag: true),以便调用函数的人必须知道标志的性质。
        • 我的评论主要是错误消息的性质应该更明确地说明出了什么问题:您没有使用错误数量的参数,您只是没有正确命名其中一些。
        • @Ziggy 在关键字 args 具有默认值的情况下,实际上无法知道您是否使用了错误数量的 args,或者只是没有正确命名它们。例如def func(flag: true), func() 是完全有效的。然而,func(1) 包含太多参数,就像func(1, flag: false) 一样。我想错误消息可能会更有帮助,尽管通过提示错误指定参数的可能性,或者通过打印方法签名。
        【解决方案8】:
        def test(a = 1, b: 2, c: 3)
          p [a,b,c]
        end
        
        test #=> [1,2,3]
        test 10 #=> [10,2,3]
        test c:30 #=> [1,2,30] <- this is where named parameters become handy. 
        

        您可以定义参数的默认值和名称,然后调用方法,如果您有基于散列的“命名”参数,但无需在方法中定义默认值。

        如果您使用散列,则您的方法中需要为每个“命名参数”使用此参数。

        b = options_hash[:b] || 2
        

        如:

          def test(a = 1, options_hash)
            b = options_hash[:b] || 2
            c = options_hash[:c] || 3
            p [a,b,c]
          end
        

        【讨论】:

          【解决方案9】:

          您可以定义命名参数,如

          def test(var1: var1, var2: var2, var3: var3)
            puts "#{var1} #{var2} #{var3}"
          end
          

          如果你没有传递其中一个参数,那么 Ruby 会抱怨 undefined local variable or method

          【讨论】:

            猜你喜欢
            • 2011-07-21
            • 2012-03-25
            • 1970-01-01
            • 2012-10-26
            • 2011-01-03
            • 1970-01-01
            • 2013-04-28
            • 1970-01-01
            • 2011-01-02
            相关资源
            最近更新 更多