Ruby中没有命名参数?

35

这个问题看起来非常简单,但我却不敢相信它抓住了我。

def meth(id, options = "options", scope = "scope")
  puts options
end

meth(1, scope = "meh")

-> "meh"

我倾向于使用哈希表来处理参数选项,只是因为这是大众惯用的方式- 这样做相当简洁明了。我认为这是标准的。今天,在花费了大约3个小时进行错误调试后,我追踪到一个宝石(gem)存在一个假设:假设被命名的参数将被认可,但事实并非如此。

因此,我的问题是:在Ruby(1.9.3)中,命名参数是否被正式认可,还是这是我的一些遗漏所导致的副作用?如果没有被认可,为什么没有呢?


3
有关Ruby 2.0中的命名参数正在讨论:http://bugs.ruby-lang.org/issues/5474 - Andrew Grimm
2
Ruby 2.0.0已经发布,现在你可以使用命名参数了。 - knut
相关:https://dev59.com/hmsz5IYBdhLWcg3wQFe2 “如何使用命名参数” - Ciro Santilli OurBigBook.com
可能是ruby方法的关键字参数的重复问题。 - Ciro Santilli OurBigBook.com
4个回答

39

实际发生的情况:

# Assign a value of "meh" to scope, which is OUTSIDE meth and equivalent to
#   scope = "meth"
#   meth(1, scope)
meth(1, scope = "meh")

# Ruby takes the return value of assignment to scope, which is "meh"
# If you were to run `puts scope` at this point you would get "meh"
meth(1, "meh")

# id = 1, options = "meh", scope = "scope"
puts options

# => "meh"

没有命名参数的支持(请参见下面的2.0更新)。您看到的只是将"meh"分配给scope,作为meth中传递的options值的结果。当然,该分配的值是"meh"

有几种方法可以做到这一点:

def meth(id, opts = {})
  # Method 1
  options = opts[:options] || "options"
  scope   = opts[:scope]   || "scope"

  # Method 2
  opts = { :options => "options", :scope => "scope" }.merge(opts)

  # Method 3, for setting instance variables
  opts.each do |key, value|
    instance_variable_set "@#{key}", value
    # or, if you have setter methods
    send "#{key}=", value
  end
  @options ||= "options"
  @scope   ||= "scope"
end

# Then you can call it with either of these:
meth 1, :scope => "meh"
meth 1, scope: "meh"

等等。它们都是解决方案,但都是因为缺少命名参数而产生的。


编辑(2013年2月15日):

* 好消息是,至少在即将到来的Ruby 2.0之前,它支持关键字参数!截至本文撰写时,它已经发布了RC2版本,这是正式发布之前的最后一个版本。虽然您需要了解上面介绍的方法才能使用1.8.7、1.9.3等版本,但那些能够使用更新版本的人现在有以下选项:

def meth(id, options: "options", scope: "scope")
  puts options
end

meth 1, scope: "meh"
# => "options"

是的,我猜想就是这样。我之前从未考虑过这个问题,因为我从未在 Ruby 中实际使用过命名参数。我总是使用散列。然后当我在这个否则编码良好的 gem 中看到它时,我感到很惊讶。 - JohnMetta
@JohnMetta 是的,听起来像是开发人员的精神失常。让他或她知道可能是个好主意,但我不能责怪开发人员有一些美好的想法。 ;) - brymck
1
我确实这样做了。Github问题。我正在考虑重建它并提交一个pull请求,但我需要先推进我的项目,所以现在只能绕过它。 - JohnMetta

5

我认为这里发生了两件事情:

  1. 你正在定义一个名为“scope”的方法参数,其默认值为“scope”
  2. 当你调用该方法时,你将值“meh”赋给一个名为“scope”的新的本地变量,该变量与你调用的方法的参数名称无关。

3

虽然 Ruby 语言不支持命名参数,但您可以通过将函数参数传递给哈希表来模拟它们。例如:

def meth(id, parameters = {})
  options = parameters["options"] || "options"
  scope = parameters["scope"] || "scope"

  puts options
end

以下是使用方法:

meth(1, scope: "meh")

你现有的代码只是简单地给一个变量赋值,然后将该变量传递到你的函数中。要了解更多信息,请参阅:http://deepfall.blogspot.com/2008/08/named-parameters-in-ruby.html

0

Ruby 没有命名参数。

这个例子的方法定义带有默认值的参数。

调用示例将一个值分配给调用方范围内的局部变量 scope,然后将其值 (meh) 传递给 options 参数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接