第七章 方法

来源:互联网 发布:看图软件 安卓版 编辑:程序博客网 时间:2024/06/07 16:11

第七章方法

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