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 库可能会或可能不会。
按照惯例以问号结尾的方法返回布尔值,但它们不一定总是返回正确true
或false
。通常,他们会返回一个对象来表示真值(或“真值”)。
以等号结尾的方法表示分配方法。对于赋值方法,返回值被忽略,而是返回参数。
这些是各种 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 + 1
。return
关键字可用于使之明确,一个方法返回一个值。
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。
异常处理
方法有一个隐含的异常处理块,所以你不需要使用begin
或end
处理异常。这个:
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
如果您希望仅拯救部分方法的异常,请使用begin
和end
。有关更多详细信息,请参阅异常处理页面。