Ruby 2.4

Methods

Methods

方法实现程序的功能。这是一个简单的方法定义:

def one_plus_one 1 + 1 end

方法定义由def关键字,方法名称,方法的主体,return值和end关键字组成。当被调用时,该方法将执行该方法的主体。此方法返回2

本节仅介绍定义方法。另请参阅调用方法的语法文档。

方法名称

方法名称可能是操作员之一,或者必须以8位设置开始字母或字符。它可能包含字母,数字,_(下划线或下划线)或八位设置的字符。约定是使用下划线来分隔多字方法名称中的单词:

def method_name puts "use underscores to separate words" end

Ruby 程序必须使用 US-ASCII 兼容字符集编写,例如 UTF-8,ISO-8859-1等。在这些字符集中,如果设置了8位,则表示扩展字符。Ruby 允许方法名称和其他标识符包含这些字符。Ruby 程序不能包含一些像 ASCII NUL(\x00)这样的字符。

以下是有效的 ruby 方法的例子:

def hello "hello" end def こんにちは puts "means hello in Japanese" end

通常方法名称与 US-ASCII 兼容,因为键入它们的键存在于所有键盘上。

方法名称可以以!(bang 或感叹号),?(问号)或=等号结尾。

bang 方法(!在方法名称末尾)与其他方法一样被调用和执行。但是,按照惯例,带有感叹号或爆炸的方法被认为是危险的。在 ruby 核心库中,危险的方法意味着当一个方法以爆炸(!)结束时,它表明不像其非爆炸等价物,永久修改其接收器。几乎总是,ruby 核心库将有一个不会修改接收者!的每一个爆炸方法(方法名称结束!)的非爆炸对象(方法名称不会结束)。这个约定对于 ruby 核心库来说通常是正确的,但是对于其他 ruby 库可能会或可能不会。

按照惯例以问号结尾的方法返回布尔值,但它们不一定总是返回正确truefalse。通常,他们会返回一个对象来表示真值(或“真值”)。

以等号结尾的方法表示分配方法。对于赋值方法,返回值被忽略,而是返回参数。

这些是各种 ruby 运营商的方法名称。这些运营商中的每一个只接受一个参数。操作员之后是操作员的典型用途或名称。为操作符创建一个可选的含义可能会导致混淆,因为用户期望 plus 加上东西,减去减去东西等。另外,不能改变操作符的优先级。

+

-

减去

*

**

指数

/

%

模数除法,字符串#%

&

AND

^

XOR (exclusive OR)

>>

右移

<<

左移,追加

==

等于

!=

不等于

===

相等。请参阅对象#===

=~

模式匹配。(不仅适用于正则表达式)

!~

不匹配

<=>

比较 aka 太空船操作员。看比较

<

less-than

<=

less-than or equal

>

greater-than

>=

大于或等于

要定义一元方法 minus,plus,tilde和not(!),请使用@as +@或in !@

class C def -@ puts "you inverted this object" end end obj = C.new -obj # prints "you inverted this object"

一元方法接受零参数。

另外,对于元件的参考和分配方法可以被定义为:[][]=分别。两者都可以带一个或多个参数,而元素引用可以不带任何参数。

class C def [](a, b) puts a + b end def []=(a, b, c) puts a * b + c end end obj = C.new obj[2, 3] # prints "5" obj[2, 3] = 4 # prints "10"

返回值

默认情况下,方法返回在方法主体中评估的最后一个表达式。在上面的例子中,最后(也是唯一)的表达式是简单的总和1 + 1return关键字可用于使之明确,一个方法返回一个值。

def one_plus_one return 1 + 1 end

它也可以用来在最后一个表达式求值之前返回一个方法。

def two_plus_two return 2 + 2 1 + 1 # this expression is never evaluated end

请注意,对于赋值方法,返回值将始终被忽略。相反,参数将被返回:

def a=(value) return 1 + value end p(a = 5) # prints 5

范围

定义方法的标准语法:

def my_method # ... end

将该方法添加到类中。您可以使用class关键字在特定类上定义实例方法:

class C def my_method # ... end end

可以在另一个对象上定义一个方法。你可以像这样定义一个“类方法”(一个在类中定义的方法,而不是类的一个实例):

class C def self.my_method # ... end end

然而,这仅仅是 Ruby 中一种更强大的语法能力的特例,它可以将方法添加到任何对象中。类是对象,因此添加类方法只是将方法添加到 Class 对象。

将方法添加到对象的语法如下所示:

greeting = "Hello" def greeting.broaden self + ", world!" end greeting.broaden # returns "Hello, world!"

self是一个关键字,用于引用编译器正在考虑的当前对象,这可能会使得self在定义类方法时更加清晰。事实上,在hello类中添加方法的例子String可以这样重写:

def String.hello "Hello, world!" end

这样定义的方法称为“单例方法”。broaden将只存在于字符串实例上greeting。其他字符串不会有broaden

重写

当 Ruby 遇到def关键字时,如果方法已经存在,它不会认为它是错误的:它只是重新定义它。这被称为覆盖。与扩展核心类相似,这是一种潜在的危险能力,应谨慎使用,因为它可能会导致意想不到的结果。例如,考虑这个 irb 会话:

>> "43".to_i => 43 >> class String >> def to_i >> 42 >> end >> end => nil >> "43".to_i => 42

这将有效地破坏任何使用该方法String#to_i从字符串解析数字的代码。

参数

一个方法可以接受参数。参数列表在方法名称后面:

def add_one(value) value + 1 end

当被调用时,add_one方法的用户必须提供一个参数。参数是方法体中的局部变量。然后该方法将为此参数添加一个并返回值。如果给定1此方法将返回2

参数周围的括号是可选的:

def add_one value value + 1 end

多个参数用逗号分隔:

def add_values(a, b) a + b end

被调用时,参数必须按照正确的顺序提供。换句话说,论据是有争议的。

默认值

参数可能有默认值:

def add_values(a, b = 1) a + b end

默认值不需要首先显示,但具有默认值的参数必须组合在一起。还行吧:

def add_values(a = 1, b = 2, c) a + b + c end

这会引发一个 SyntaxError:

def add_values(a = 1, b, c = 1) a + b + c end

数组分解

您可以使用参数中的额外括号分解(从数组中解压缩或提取值):

def my_method((a, b)) p a: a, b: b end my_method([1, 2])

这打印:

{:a=>1, :b=>2}

如果参数在数组中有额外的元素,它们将被忽略:

def my_method((a, b)) p a: a, b: b end my_method([1, 2, 3])

这与上面的输出相同。

您可以使用 *来收集剩余的参数。这将数组分为第一个元素和其余的元素:

def my_method((a, *b)) p a: a, b: b end my_method([1, 2, 3])

这打印:

{:a=>1, :b=>[2, 3]}

如果它响应 to_ary,则参数将被分解。如果可以使用对象代替 Array,则只应定义 to_ary。

使用内部括号仅使用发送的参数之一。如果参数不是数组,则它将被分配给分解中的第一个参数,分解中的其余参数将为nil

def my_method(a, (b, c), d) p a: a, b: b, c: c, d: d end my_method(1, 2, 3)

这打印:

{:a=>1, :b=>2, :c=>nil, :d=>3}

你可以任意嵌套分解:

def my_method(((a, b), c)) # ... end

Array/Hash 参数

在参数前加上一个参数,可以*将任何剩余的参数转换为一个数组:

def gather_arguments(*arguments) p arguments end gather_arguments 1, 2, 3 # prints [1, 2, 3]

数组参数必须是最后一个位置参数,它必须出现在任何关键字参数之前。

如果在所有位置参数之后由调用方发送哈希,则数组参数将捕获哈希作为最后一个条目。

gather_arguments 1, a: 2 # prints [1, {:a=>2}]

但是,只有当方法没有声明任何关键字参数时才会发生这种情况。

def gather_arguments_keyword(*positional, keyword: nil) p positional: positional, keyword: keyword end gather_arguments_keyword 1, 2, three: 3 #=> raises: unknown keyword: three (ArgumentError)

另外请注意,*可以使用 bare 来忽略参数:

def ignore_arguments(*) end

关键字参数

关键字参数与具有默认值的位置参数相似:

def add_values(first: 1, second: 2) first + second end

任意关键字参数将被接受**

def gather_arguments(first: nil, **rest) p first, rest end gather_arguments first: 1, second: 2, third: 3 # prints 1 then {:second=>2, :third=>3}

当调用带有关键字参数的方法时,参数可以以任何顺序出现。如果调用者发送未知关键字参数,则引发 ArgumentError。

混合关键字参数和位置参数时,所有位置参数必须出现在任何关键字参数之前。

块参数

块参数由&最后指示并且必须最后一个:

def my_method(&my_block) my_block.call(self) end

大多数情况下,块参数用于将块传递给另一个方法:

def each_item(&block) @items.each(&block) end

如果您只打算调用该块,并且不会以其他方式处理该块或将其发送到另一种方法,yield而不使用明确的块参数,则为首选。此方法等同于本节中的第一种方法:

def my_method yield self end

使用 yield 来调用 block 参数也有性能优势。当一个块参数被分配给一个变量时,一个 Proc 对象被创建并保存该块。当使用 yield 时,这个 Proc 对象不会被创建。

如果您只需要使用该块,则有时可以使用 Proc.new 从传递给您的方法的块创建一个 proc。有关更多详细信息,请参阅 Proc.new。

异常处理

方法有一个隐含的异常处理块,所以你不需要使用beginend处理异常。这个:

def my_method begin # code that may raise an exception rescue # handle exception end end

可写成:

def my_method # code that may raise an exception rescue # handle exception end

如果您希望仅拯救部分方法的异常,请使用beginend。有关更多详细信息,请参阅异常处理页面。