第七章方法
Ruby中的方法使用关键字def来定义。方法名应该以小写字母开始,如果你使用大写字母开始,Ruby解释器会认为它是一个常量,这样可能会带来名称解析错误。
在定义方法时可以使用圆括号也可以不用。
def method1
puts "Hello World!"
end
def method2 arg1, arg2
puts "The arguments is: #{arg1}, #{arg2}"
end
一般的习惯是如果方法含有参数,那么就使用圆括号将参数括起来,否则的不要需要圆括号。
def method2(arg1, arg2)
puts "The arguments is: #{arg1}, #{arg2}"
end
定义方法时可给方法默认参数,注意默认参数必须位于方法的尾部。
def method3(arg1=5, arg2=9)
puts "The arguments is: #{arg1}, #{arg2}"
end
def method3(arg1=5, arg2) #错误
方法的返回值为方法最后一个表达式的值,或者由return语句的返回的值。
和C/C++不同,Ruby中的方法总是从属于某一个对象。Ruby中没有全局函数。虽然Ruby中可以象全局函数一样定义和使用方法,但是你应当明白,Ruby中的方法总是会从属于某一个对象。
看到这里,细心的读者会提出一个问题,如果在顶层定义一个方法,那么这个方法属于谁?
def meth
end
在本书中,我们多次说,Ruby是一种面向对象的语言,在Ruby中的一切都是对象。那么meth方法从属于什么对象呢?我们看一个例子:
public #为什么使用public看后边的解释
def meth
puts self.class
end
puts self.class
self.meth
执行结果为:
Object
Object
在顶层,当我们定义方法时,将自动将我们定义的方法作为Object类的私有实例方法。所以这些方法可以在任何地方调用。所以我们可以在任何地方使用Object类的任何方法和Object类所包含的模块中的任何方法,例如Kernel模块中的方法在任何地方可以随意使用。
上面的例子中,meth将作为Object类的私有方法,所以我们使用public改变它的存取属性,否则self.meth将会产生无法访问私有对象的错误。
在Ruby语言中,方法是存放到Hash表中,而键值就是方法名。定义一个方法就是在Hash表中添加一项的过程,所以,后定义的同名方法就会替换掉之前定义的方法。
def meth
puts "first"
end
meth
def meth
puts "second"
end
meth
执行结果为:
first
second
Ruby语言支持缺省参数,但不支持方法重载。方法重载会加重解释器语法解析的复杂度,影响执行速度。C++的选择是二者都支持,而Java的选择刚好与Ruby相反,即支持方法重载,而不支持缺省参数。
方法名可以以问号“?”,叹号“!”,等于号“=”结尾。这样的方法有约定的含义。以问号结尾的方法返回布尔值,以叹号结尾的方法表示会改变调用者的内部数据,以等于号结尾的方法表示可以作为左值。问号“?”,叹号“!”,等于号“=”是方法名的一部分。
(to-do 例子)
当一个方法被调用时,运行时环境按照如下顺序搜索:
1、 在当前对象的特殊方法中搜索
2、 在当前对象类中定义的实例方法中搜索
3、 在当前对象所包含的模块中搜索
4、 在当前对象所在类的父类中搜索
5、 在当前对象所在类的父类所包含的模块中搜索
6、 继续4和5的过程,直到顶层Object类
§7.1 运算符重定义
Ruby支持重定义运算符,下表列出了Ruby支持重定义的运算符和他们默认的含义:
运算符 | 默认含义 |
[] []= | 元素索引和赋值 |
** | 幂运算 |
! ~ | 否定,求补 |
+ - | 正,负,注意他们的方法名为+@,-@以便和加减运算区分 |
* / % | 乘法,除法,取余运算 |
+ - | 加减运算 |
>> << | 左移,右移 |
& | ^ | 按位与,按位或,按位异或 |
<= < > >= | 比较运算 |
=== | Case表达式中的相等测试 |
<=> | 范围测试 |
== != | 正则表达式匹配相等和不等测试 |
=~ !~ | 相等和不等测试 |
注意,!=和!~ 作为==和=~的否定形式,并没有相应的方法。他们经过语法分析后将被转化为肯定形式调用。例如:
a != b实际是调用 !(a==b),同样,a !~ b将被转化为 !(a=~b)。
§7.2 变长参数
Ruby语言支持定义方法时,方法的参数数目不确定。只要给参数前加星号就表示参数数目不定。所有的不定数目的参数被作为一个数组。
def varadd(*num)
sum = 0
num.each { |i| sum+=i }
puts sum
end
varadd()
varadd(1,2,3)
varadd(1,2,3,4,5,6)
执行结果为:
0
6
21
你也可以反过来使用星号,在调用方法时,如果一个类型为数组的参数前有星号作为前缀,那么这个数组将被展开。
def meth1(arg1, arg2, arg3, arg4, arg5)
print "Parameters:#{arg1},#{arg2},#{arg3},#{arg4},#{arg5} \n"
end
meth1(1, 2, 3, 4, 5)
meth1(1, 2, 3, *['4', '5'])
meth1(*(10..14).to_a)
执行结果为:
Parameters:1,2,3,4,5
Parameters:1,2,3,4,5
Parameters:10,11,12,13,14
§7.3 块调用
当调用一个方法时,可以在方法后连接一个块。在方法内可以使用yield执行连接的块。
def test_block1(arg1)
print arg1,"\n"
yield
end
test_block1("Test") { print "Hello World!" }
执行结果为:
Test
Hello World!
Kernel模块有一个block_given?的方法,可以判断方法后是否有块连接。
def test_block2(arg1)
if block_given?
print yield(arg1), "\n"
else
print arg1, "\n"
end
end
test_block2("no block")
test_block2("no block") {|s| s.sub(/no /, '') }
执行结果为:
no block
block
如果方法的最后一个参数以&开始,表示方法后连接的块会被转化为Proc对象,然后传递给这个参数。
def meth1(count, &block)
value = 1
1.upto(count) do | i |
value = value * i
block.call(i, value)
end
end
meth1(4) do | i, f_i | puts "meth1(#{i}) = #{f_i}" end
执行结果为:
meth1(1) = 1
meth1(2) = 2
meth1(3) = 6
meth1(4) = 24
§7.4 方法返回值
每一个方法都有返回值,当然,你不一定要使用这个返回值。方法的返回值就是在调用方法时方法内最后一个表达式执行的结果。你也可以使用return语句,这样方法的返回值就是return语句的参数。Ruby语言习惯省略return语句,能少写的尽量少写,这也是Ruby的哲学J。
def meth1()
"meth1"
end
def meth2(arg)
if arg == 0
"Zero"
else if arg > 0
"Positive"
else
"Negative"
end
end
使用return语句时返回值可以是多个,这时候返回值会被转化为一个数组,你可以用多重赋值的形式来使用这个返回值。
def meth1
return "meth1", 6
end
a, b = meth1