由于您是专门询问语法而不是语义,因此我将回答您有关语法的问题。
您在此处看到的内容称为消息发送。(在 Java 或 C# 等其他编程语言中,它可能被称为方法调用。)更准确地说,它是带有隐式接收器的消息发送。
消息总是发送给特定的接收者(就像现实世界中的消息一样)。消息发送的一般语法如下所示:
foo.bar(baz, quux: 23) {|garple| glorp(garple) }
这里,
foo
是接收者,即接收消息的对象。请注意,foo
它当然可以是任意 Ruby 表达式,例如(2 + 3).to_s
.
bar
是消息选择器,或者只是消息。它告诉接收者对象要做什么。
- 消息选择器后面的括号包含参数列表。在这里,我们有一个位置参数,它是表达式
baz
(可以是局部变量或另一个消息发送,稍后会详细介绍),以及一个关键字参数quux
,它是具有值的关键字23
。(同样,该值可以是任意 Ruby 表达式。)注意:除了位置参数和关键字参数之外,还有一些其他类型的参数,即splat 参数和可选的显式块参数。
- 在参数列表之后是文字块参数。在 Ruby 中发送的每条消息都可以有一个文字块参数……取决于被调用的方法来忽略它、使用它,或者用它做任何它想做的事情。
- 块是一段轻量级的可执行代码,因此,就像方法一样,它有一个参数列表和一个主体。参数列表由
|
管道符号分隔——在这种情况下,只有一个名为 的位置参数garple
,但它可以具有方法可以具有的所有相同类型的参数,以及块局部变量。当然,主体可以包含任意 Ruby 表达式。
现在,重要的是其中很多元素是可选的:
- 您可以省略括号:
foo.bar(baz, quux: 23)
与 相同foo.bar baz, quux: 23
,这也意味着foo.bar()
与 相同foo.bar
。
- 您可以省略显式接收器,在这种情况下,隐式接收器是
self
,即self.foo(bar, baz: 23)
与 相同foo(bar, baz: 23)
,当然也与 相同foo bar, baz: 23
。
- 如果将两者放在一起,这意味着 eg与我之前提到
self.foo()
的 相同foo
:如果您只是foo
在没有上下文的情况下单独使用,您实际上并不知道它是局部变量还是消息发送。只有当您看到接收者或参数(或两者)时,您才能确定它是消息发送,并且只有当您看到同一范围内的赋值时才能确定它是变量。如果你没有看到这些东西,它也可能是。
- 您可以省略不使用的块参数列表,也可以完全省略块。
因此,让我们剖析一下您在这里看到的语法。第一层是
sig {
# stuff
}
我们知道这是消息发送而不是局部变量,因为有一个字面量块参数,并且变量不带参数,只有消息发送可以。
因此,这是将消息发送sig
到隐式接收器self
(在模块定义主体中只是模块本身),并将文字块作为唯一参数传递。
该块没有参数列表,只有一个主体。正文的内容是
params(
# stuff
).returns(Types::Order)
同样,我们知道这params
是一条消息发送,因为它需要一个参数。因此,这是将消息发送params
到隐式接收器self
(这里仍然是模块本身,因为块词法捕获self
,尽管这是语言语义的一部分,并且您严格询问语法)。它还将一个参数传递给消息发送,我们稍后会看到。
然后我们发送另一条消息。我们怎么知道?好吧,它需要一个参数并且有一个接收器。我们将消息发送到消息发送returns
返回的对象,params
将表达式Types::Order
作为唯一的位置参数传递。
Types::Order
依次是一个常量引用( Types
)、命名空间解析运算符( ::
),然后是另一个常量引用 ( Order
)。
接下来,让我们看一下 的参数params
:
params(order: T::Hash[
# stuff
])
这里我们有一个关键字参数order
,其值为表达式T::Hash[ … ]
。T::Hash
当然又是一个常量引用、命名空间解析运算符和另一个常量引用。
那么,什么是[]
?实际上,这只是另一个消息发送。Ruby为有限的、固定的特殊消息列表提供语法糖。一些例子:
foo.call(bar)
是一样的foo.(bar)
。
foo.+(bar)
是一样的foo + bar
。(对于*
, **
, /
, -
, <<
, >>
, |
, &
, ==
, ===
, =~
, !=
, !==
,!~
和其他几个我可能忘记的类似。)
foo.+@
是一样的+foo
。(和类似的-@
。)
foo.!
是一样的!foo
。(和类似的~
。)
self.`("Hello")
与 相同`Hello`
,这有点晦涩难懂。
foo.[](bar, baz)
是一样的foo[bar, baz]
。
foo.[]=(bar, baz, quux)
是一样的foo[bar, baz] = quux
。
因此,这只是将消息发送[]
到Hash
通过解引用常量获得的对象命名空间内的常量解引用结果T
,并传递两个位置参数。
第一个位置参数是String
,它又是一个常量引用。第二个位置参数是表达式T.any( … )
,它是对常量的常量引用T
,然后将消息发送any
到该常量引用的对象,并传递两个位置参数。
第一个参数是表达式T.nilable(String)
,它取消引用常量T
,将消息发送nilable
到取消引用常量的结果T
,传递单个位置参数,这是取消引用常量的结果String
。
第二个参数是表达式T::Hash[ … ]
……我将在这里停下来,因为这里真的没有什么要解释的了。有常量、消息发送和参数,我们之前已经多次看到所有这些。
因此,总而言之,关于您关于语法的问题:我们在这里看到的语法元素是
- 消息发送
- 论据
- 常数
- 命名空间解析运算符(实际上并不是一个单独的语法元素,而只是众多运算符之一)
- 和一个块文字