6

我是 Ruby 和 Rails 的新手,在阅读各种教程时,我偶尔会遇到一些我无法理解的 Ruby 语法。

例如,这实际上是做什么的?

root to: "welcome#index"

我可以推测这可能是一个名为“root”的方法,但在那之后我迷路了。“到”不是一个符号,是吗?冒号应该在前面,如果是的话,就像在 ":to" 中一样。这是某种形式的使用哈希的关键字参数吗?在使用 ruby​​1.9.3 在 irb 中尝试时,我无法使此语法工作。

我知道这可能是一个 RTFM 问题,但我什至想不出用谷歌搜索什么。

谢谢!

我还在玩这个语法,

def func(h)
    puts h[:to]
end

x = { :to => "welcome#index" }
y = :to => "welcome#index"
z = to: "welcome#index" 

func to: "welcome#index"

我看到这个例子只适用于定义“y”和“z”的行被注释掉。那么无括号和“冒号后”语法仅在调用方法的上下文中有效?

4

5 回答 5

5

首先,这是正确的 -root是一个方法调用。现在

to: 'welcome#index' 

相当于

:to => 'welcome#index'

它的Hash键是:to符号,值是'welcome#index'字符串。您可以在定义散列时使用此语法,因为Ruby 1.9.

于 2013-07-04T19:30:50.977 回答
4

相当于

root(:to => "welcome#index")

我很难找到有关新哈希语法的官方文档,但是当您看到 时foo: bar,这意味着它foo是用作哈希中的键的符号并且具有值bar

于 2013-07-04T19:28:55.580 回答
3

这是一个定义函数的示例,该函数foo接受哈希并打印到屏幕上。

def foo(hash)
  puts hash.inspect
  puts hash[:to]
end

foo to: "wecome#index" #method call without paratheses

上面方法调用的输出

{:to=>"welcome#index"}
welcome#index

等效声明:

h = {:to => "welcome#index"}   
h = {to: "wecolme#index"}

此外,您可以使用Ripper(Ruby 标准库的一部分)来了解 Ruby 如何解析代码。在下面的例子中,我已经定义foo如上。现在,我foo在不使用 Ripper 的情况下调用。然后我使用 Ripper 来看看 Ruby 是如何解析方法调用的。

[2] pry(main)> foo to: "welcome#index"
{:to=>"welcome#index"}
welcome#index
=> nil
[3] pry(main)> require 'ripper'
=> true
[4] pry(main)> Ripper.sexp 'foo to: "welcome#index"'
=> [:program,
 [[:command,
   [:@ident, "foo", [1, 0]],
   [:args_add_block,
    [[:bare_assoc_hash,
      [[:assoc_new,
        [:@label, "to:", [1, 4]],
        [:string_literal,
         [:string_content, [:@tstring_content, "welcome#index", [1, 9]]]]]]]],
    false]]]]
于 2013-07-04T19:40:14.713 回答
2

在 ruby​​ 中,方法调用中的大括号是可选的,因此可以重写为:

root(to: "welcome#index")

并且可以再次重写为

root(:to => "welcome#index")

哈希作为关键字参数(ruby 1.9)也在这里解释:hash-constructor-parameter-in-1-9

PS,顺便说一下,rails-newcomers 的一般经验法则是“先学习 ruby​​,然后再学习 rails”;)

于 2013-07-04T19:33:36.567 回答
1

正如您正确收集的那样,root是一个方法调用。或者更确切地说,它是一个消息发送。Ruby 与 Smalltalk 一样,建立在消息传递隐喻之上,其中对象向其他对象发送消息,而这些对象(称为接收器)响应这些消息。

在这种情况下,您将参数传递给root,这就是您知道它是消息发送的方式。消息发送是唯一可以接受参数的东西,如果你看到一个参数,那么它一定是一个消息发送。没有函数,没有静态方法,没有构造函数,没有过程,只有方法和消息发送。

那么,论据是什么?嗯,在 Ruby 中,许多其他语言在语法上需要的东西都是可选的。例如,参数列表周围的括号:

foo.bar(baz)
# can also be written as
foo.bar baz

如果消息发送的最后一个参数是Hash文字,则可以省略花括号:

foo.bar({ :baz => 23, :quux => 42 })
# can also be written as
foo.bar(:baz => 23, :quux => 42)

将两者放在一起,你会得到:

foo.bar({ :baz => 23, :quux => 42 })
# can also be written as
foo.bar :baz => 23, :quux => 42

在 Ruby 1.9 中,Hash引入了一种新的替代文字语法。与原始语法相比,这种文字语法非常有限,因为它只能表示Hash键为 s 的 esSymbol也是有效的 Ruby 标识符,而使用原始语法,您可以Hash用任意对象作为键来写 a。但是,对于那个有限的用例,它是非常可读的:

{ :baz => 23, :quux => 42 }
# can also be written as
{ baz: 23, quux: 42 }

如果我们将该功能与其他两个功能放在一起,我们会得到您所询问的消息发送语法:

foo.bar baz: 23, quux: 42

如果我们有这样一个参数声明的方法:

def foo.bar(opts) p opts end

opts将绑定到Hash具有两个键值对的单个。

这些功能通常用于模拟其他语言中的关键字参数。长期以来,Ruby 社区一直希望获得对真正关键字参数的支持。这种支持分两步实现:首先,Hash在 Ruby 1.9 中引入了新的文字语法,它允许您发送看起来像是在使用关键字参数的消息发送,即使它们实际上只是一个Hash. 然后在第二步中,在 Ruby 2.0中引入了真正的关键字参数。修改后的方法签名如下所示:

def foo.bar(baz: nil, quux: nil) p baz, quux end

请注意,目前不可能有必需的关键字参数,它们总是需要有一个默认值,因此总是可选的。但是,您可以使用默认值可以是任意表达式的事实并执行以下操作:

def foo.bar(baz: raise ArgumentError '`baz` must be supplied!', 
  quux: raise ArgumentError '`quux` must be supplied!') p baz, quux end

在 Ruby 的未来版本中(实际上已经在 2 月份实现,并且可能会在 2.1 中实现),可以通过省略默认值来指定所需的关键字参数:

def foo.bar(baz:, quux:) p baz, quux end

请注意,现在存在语法歧义:

foo.bar baz: 23, quux: 42
# is this sending the message `bar` to `foo` with *one* `Hash` or *two* keywords?

这种歧义实际上是有意的,因为它允许针对使用参数的 API 编写的旧客户端代码Hash与使用关键字参数的新 API 保持不变地工作。有一些半复杂的规则可以确定该语法是否将被解释为 aHash或关键字,但大多数这些规则会按照您期望的方式运行。

于 2013-07-05T00:53:37.507 回答