我可以在 Ruby 2.x 中要求命名参数吗?

Posted

技术标签:

【中文标题】我可以在 Ruby 2.x 中要求命名参数吗?【英文标题】:Can I have required named parameters in Ruby 2.x? 【发布时间】:2012-10-26 08:54:48 【问题描述】:

Ruby 2.0 正在添加命名参数,如下所示:

def say(greeting: 'hi')
  puts greeting
end

say                     # => puts 'hi'
say(greeting: 'howdy')  # => puts 'howdy'

如何在不提供默认值的情况下使用命名参数,以便它们是必需的?

【问题讨论】:

【参考方案1】:

Ruby 2.0.0 中没有具体的方法,但是你can do it Ruby 2.1.0,语法类似于def foo(a:, b:) ...

在 Ruby 2.0.x 中,您可以通过放置任何引发异常的表达式来强制执行它,例如:

def say(greeting: raise "greeting is required")
  # ...
end

如果您打算经常这样做(并且不能使用 Ruby 2.1+),您可以使用如下辅助方法:

def required
  method = caller_locations(1,1)[0].label
  raise ArgumentError,
    "A required keyword argument was not specified when calling '#method'"
end

def say(greeting: required)
  # ...
end

say # => A required keyword argument was not specified when calling 'say'

【讨论】:

我也尝试将其与@Ben Taitelbaum 的回答结合起来:gist.github.com/rdp/5390849 :)【参考方案2】:

目前(Ruby 2.0.0-preview1)您可以使用以下方法签名:

def say(greeting: greeting_to_say)
  puts greeting
end

greeting_to_say 只是一个占位符,如果您向命名参数提供参数,则不会对其进行评估。如果你不传入它(只调用say()),ruby 将引发错误:

NameError: undefined local variable or method `greeting_to_say' for (your scope)

但是,该变量没有绑定到任何东西,据我所知,不能从您的方法内部引用。您仍然可以使用 greeting 作为局部变量来引用为命名参数传入的内容。

如果您真的要这样做,我建议您使用def say(greeting: greeting),这样错误消息就会引用您为参数指定的名称。我只在上面的示例中选择了不同的,以说明 ruby​​ 将在您收到的错误消息中使用什么,因为您没有为所需的命名参数提供参数。

如果你调用say('hi'),ruby 会引发ArgumentError: wrong number of arguments (1 for 0),我认为这有点令人困惑,但这只是预览1。

【讨论】:

现在我看到了,这很明显。它与 Ruby 2.0 或命名参数完全无关,它与“普通”可选参数的工作方式相同,并且始终具有:def m(a = b) end; m # NameError: undefined local variable or method 'b' for main:Object @Adam,我觉得你的回答有点误导。您收到这些错误的原因是您可以将任意 Ruby 代码作为参数的默认值。这也适用于常规位置参数,与必需的关键字参数无关。尝试将Time.now 作为默认值,看看会发生什么。 Mark-Andre's answer 在这里更重要。 @Dimitar 我认为机制很清楚,并且它是一个很好的 hack 解决了仍然产生有用错误的缺失功能。但我注意到 2.1.0-preview1 发行说明和 Marc-Andre 的答案确实是官方的方式,所以我改变了接受的答案(对不起,@Adam)。 非常感谢“切线”部分。我正在定义我的方法为第二个使用命名参数,但实际上以序数样式调用它,并且当有两个非常明显的参数并且我有时收到消息“参数数量错误(2 比 1)”两者都指定。因为输出令人困惑,所以很难知道如何寻求帮助!【参考方案3】:

结合@awendt 和@Adam 的解决方案,

def say(greeting: -> raise ArgumentError.new("greeting is required") .call)
  puts greeting
end

你可以用类似的东西把它弄干:

def required(arg)
  raise ArgumentError.new("required #arg")
end

def say(greeting: required('greeting'))
  puts greeting
end

并将其与@Marc-Andre 的解决方案相结合:https://gist.github.com/rdp/5390849

【讨论】:

【参考方案4】:

在 Ruby 2.3 中,我可以做到

def say(greeting:)
  puts greeting
end

然后将它与...一起使用

say(greeting: "hello there")

【讨论】:

这应该被标记为正确答案,因为新版本的 Ruby 允许这样做。您可以免费获得 ArgumentError。【参考方案5】:

怎么样:

def say(greeting: nil)
  greeting or raise ArgumentError
  puts greeting
end

say                     # => raises ArgumentError
say(greeting: 'howdy')  # => puts 'howdy'

除此之外,这将是困难的。根据this site,关键字参数“是具有默认值的命名参数。”

【讨论】:

以上是关于我可以在 Ruby 2.x 中要求命名参数吗?的主要内容,如果未能解决你的问题,请参考以下文章

Ruby 中没有命名参数?

如何在子类中添加命名参数或在 Ruby 2.2 中更改它们的默认值?

为啥不能在 Ruby 3 中结合 `...` 和命名参数?

命名这个 python/ruby 语言结构(使用数组值来满足函数参数)

命名这个 python/ruby 语言结构(使用数组值来满足函数参数)

何时在 Ruby 中使用关键字参数,也就是命名参数