`
roki
  • 浏览: 60470 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

《搜索引擎零距离》第三章 IRS虚拟机及编译器实现原理(2)

阅读更多
3.1.4 类和方法的定义
1. 类定义
例子:
class Foo < Super
  def test
     :
  end
     :
end

语法:
class 标识符 [<  superclass ]
  表达式 ..
end
该语法用来定义类的内容。类名是以大写字母开头的标识符。
类定义实际上就是把类赋值给由类名指定的常数(在 Ruby 中,类也是一个对象,它是 Class 类的实例)。
若某个类已经被定义过,此时又用相同的类名进行类定义的话,就意味着对原有的类的定义进行追加。例子:
class Foo < Array
  def foo
  end
end

class Foo
  def bar
  end
end
在类定义表达式中,self 指的是该类本身,这与顶层没有什么不同。可以在类定义表达式中写入任何表达式,在定义类时这些表达式将被执行。
类定义中可以出现嵌套。例子:
class Foo
  class Bar
  end
end
上面的例子中,嵌套外侧的 Foo 类和内侧的 Bar 类之间没有继承关系之类的功能上的联系(除了常数 Bar 是 Foo 中的常数 Foo:Bar 之外)。类的嵌套就是指,把与类有关的类/模块放在该类的外侧,使它们构成一个整体,借以表达某种包含关系。类定义表达式将返回最后被计算的表达式的值。若最后的表达式不返回值,就返回 nil。
2. 模块定义
例子:
module Foo
  def test
     :
  end
     :
end
语法:
module 标识符
  表达式 ..
end
该语法用来定义模块的内容。模块名是以大写字母开头的标识符。模块定义实际上就是把模块赋值给由模块名指定的常数(在 Ruby 中,模块也是一个对象,它是 Module 类的实例)。若某个模块已经被定义过,此时又用相同的模块名来定义模块的话,就意味着对原有的模块定义进行追加。模块定义表达式将返回最后被计算的表达式的值。若该表达式不返回值,则返回 nil。
3. 方法定义
例子:
def fact(n)
  if n == 1 then
     1
  else
    n * fact(n-1)
  end
end
语法:
def 方法名 [(  [arg [=default]] ... [, * arg] )]
  表达式 ..
[rescue [error_type,..] [then]
  表达式 ..]..
[ensure
  表达式 ..]
end
在定义语句所在的区域内定义一个方法。也就是说,若在类/模块的定义部分定义一个方法的话,该方法就属于这个类/模块。若在顶层定义了一个方法的话,您就可以在任何地方调用它。这种方法其实就是其他语言中所说的“函数”。方法名中,除了可以使用通常的标识符以外,还可以使用可重定义的运算符(例如 ==、+、- 等。请参考运算符表达式)。
若给临时参数指定了默认表达式的话,在方法调用过程中如果实参被省略时,该默认表达式的值就会变成默认值(调用方法时,在方法定义内计算默认表达式的值)。若最后一个临时参数的前面带*的话,所有剩下的实参数将被转为数组后传递给该参数。
例子:
# 没有参数的方法。以下省略 end
def foo
end

# 有参数的方法
def foo(arg, arg2)

# 有默认参数的方法
def foo(arg = nil)

# 参数一应俱全
def foo(arg, arg2, arg3 = nil, *rest)

# 运算符表达式
def ==(other)
def +(other)
def *(other)
在方法定义中,只能以下列顺序指定临时参数。其中任何一项都是可选的。
 没有默认表达式的参数(可多选)
 有默认表达式的参数(可多选)
 带 * 的参数(只能有一个)
为了捕捉在方法运行时发生的异常,可以使用同 begin 一样的rescue、ensure 语句。方法定义表达式返回 nil。
4. 方法计算
调用方法时,将按照下列顺序计算各个表达式。
 参数的默认表达式(若有的话)
 方法的内容
 根据发生异常的实际状况,处理方法定义表达式的rescue部分或else部分(如果有的话)
 ensure 部分(如果有的话)
在方法内,根据实际情况来计算这些部分,包括参数的默认表达式在内。方法的返回值就是传给 return 的值。若没有调用 return 时,将返回在 ensure 部分之前最后计算的式子的值。调用未定义的方法会引发 NameError 异常。
5. 特殊方法定义
例子:
def foo.test
  print "this is foo\n"
end
语法:
def 表达式 .标识符 [( [ 参数 [= default]] ... [,* 参数 ])]
  表达式 ..
[rescue [error_type,..] [then]
  表达式 ..]..
[else
  表达式 ..]
[ensure
  表达式 ..]
end
特殊方法就是专属于某个对象的方法。特殊方法的定义可以嵌套。类的特殊方法将被该类的子类所继承。换言之,类的特殊方法所起的作用,与其他面向对象系统中的类方法的作用是相同的。特殊方法定义表达式返回 nil。
6. 类方法的定义
Ruby 中的类方法是指类的特殊方法。在 Ruby 中,类也是对象。因此,若能在类对象中定义方法的话,该方法就会成为类方法。具体的定义方法如下(模块也一样):
class Hoge
  def Hoge.foo
  end
end

# 在类定义的外侧也行
def Hoge.bar
end

# 若使用下面的方法来定义,即使类名改变了,也不必更改方法定义
class Hoge
  def self.baz
  end
end
7. 与定义有关的操作 alias
例子:
alias foo bar
alias :foo :bar
语法:
alias 新方法名 旧方法名
该语法给方法或全局变量添加别名。可以给方法名指定一个标识符或 Symbol。 alias 表达式返回 nil。
3.1.5 运算符表达式
例子:
1+2*3/4
为了便于编程,有些方法调用和控制结构是以运算符的形式出现的。Ruby 语言中有下列运算符:
高     ::
       []
       **
       -(单项)  +(单项)  !  ~
       *  /  %
       +  -
       << >>
       &
       |  ^
       > >=  < <=
       <=> ==  === !=  =~  !~
       &&
       ||
       ..  ...
       ?:(条件运算符)
       =(+=, -= ... )
       not
低     and or
左侧的“高”和“低”表示运算符的优先级。例如&&的优先级要高于||,如下所示:
a && b || c   # => (a && b) || c
a || b && c   # =>  a || (b && c)
大部分运算符都是特殊形式的方法调用,但有些运算符被嵌入在语言之中,无法进行重定义。能够重定义的运算符(方法)包括:

      |  ^  &  <=>  ==  ===  =~  >   >=  <   <=   <<  >>
      +  -  *  /    %   **   ~   +@  -@  []  []=  `
+@、-@ 表示单项运算符 +、-,在方法定义等场合中可以使用这种表示法。由多个运算符组合而成的自运算运算符、“!=”以及“!~”是不能重定义的。
不能重定义的运算符包括:
      =  ?:  ..  ...  !  not  &&  and  ||  or  ::
1. 赋值运算符=
例子:
foo = bar
foo[0] = bar
foo.bar = baz
语法:
变量 '=' 表达式
常量 '=' 表达式
表达式 '['表达式..']' '=' 表达式
表达式 '.' 标识符 '=' 表达式
我们使用赋值表达式向变量等对象进行赋值。赋值表达式也可以用作局部变量和常数的声明。赋值表达式的左边必须是下列表达式之一:
(1) 变量
      变量 '=' 表达式
      若左边是变量的话,就将表达式的结果代入其中。
(2) 数组调用
      表达式1 '[' 表达式2 ... ']' '=' 表达式n
      先计算表达式 1 得到一个对象,再把从表达式 2 到表达式 n 作为参数,来调用该对象的“[]=”方法。
      class C
        def initialize
          @ary = [0,1,2,3,4,5,6,7]
        end
        def [](i)
          @ary[i * 2]
        end
        def []=( i, v )
          @ary[i * 2] = v
        end
      end
      c = C.new
      p c[3]      # 变成 c.[]( 3 ), 结果为6
      p c[3] = 1  # 变成 c.[]=(3,1),结果为1
(3) 属性调用
      表达式1 '.' 标识符 '=' 表达式2
      先计算表达式1得到一个对象,再以表达式2作为参数来调用该对象的“标识符=”方法。
      class C
        def foo
          @foo
        end
        def foo=( v )
          @foo = v
        end
      end
      c = C.new
      c.foo = 5   # 变成 c.foo=( 5 )
      p c.foo     # => 5
还可以使用 attr_accessor 来定义属性。
      class C
        attr_accessor :foo
      end
      c = C.new
      c.foo = 5   # 变成 c.foo=( 5 )
      p c.foo     # => 5
(4) 自运算
例子:
foo += 12       # foo = foo + 12
foo *= 3        # foo = foo * 3
语法:
表达式1 op= 表达式2     # 表达式 1 等同于普通赋值表达式左边的部分
其中,op 为下列运算符中的某一个。运算符与=之间不留间隔。
+, -, *, /, %, **, &, |, ^, <<, >>, &&, ||
这种赋值形式和
表达式1 = 表达式1 op 表达式2
等同。
(5) 多重赋值
例子:
foo, bar, baz = 1, 2, 3
foo, = list()
foo, *rest = list2()
语法:
表达式 [',' [ 表达式 ',' ... ] ['*' [ 表达式 ]]] = 表达式 [, 表达式 ... ]['*' 表达式 ]
'*' [ 表达式 ] = 表达式 [, 表达式 ... ]['*' 表达式 ]
多重赋值是指,在多个表达式以及数组中同时进行的赋值。左边的各个表达式必须是可以被赋值的。若右边只有一个表达式,则将该表达式的结果转为数组后,再把数组中的各单元依次赋值给左边。若右边数组单元的数量超过左边的话,将忽略多余的数组单元。若右边数组单元个数不足,则将向左边多余的单元中代入 nil。
若左边最后一个表达式前带*,则将右边多余的单元以数组的形式代入这个带*的表达式中。若右边没有多余的单元,就把空数组代入其中。
例子:
foo, bar  = [1, 2]      # foo = 1; bar = 2
foo, bar  = 1, 2        # foo = 1; bar = 2
foo, bar  = 1           # foo = 1; bar = nil
foo, bar  = 1, 2, 3     # foo = 1; bar = 2
foo       = 1, 2, 3     # foo = [1, 2, 3]
*foo      = 1, 2, 3     # foo = [1, 2, 3]
foo, *bar = 1, 2, 3     # foo = 1; bar = [2, 3]
2. and运算符
例子:
test && set
test and set
语法:
表达式 && 表达式
表达式 and 表达式
首先计算左边的表达式,若结果为真就接着计算右边的表达式。and 运算符的作用与&&相同但优先度更低。将包含 and 的表达式作为某方法的参数时,必须使用双层括号。
3. or运算符
例子:
demo || die
demo or die
语法:
表达式 '||' 表达式
表达式 or 表达式
首先计算左边的表达式,若结果为假就接着计算右边的表达式。or 运算符的作用与||相同但优先度更低。将包含 or 的表达式作为某方法的参数时,必须使用双层括号。
4. not 运算符
例子:
! me
not me
i != you
语法:
'!' 表达式
not 表达式
若表达式值为真就返回假,若表达式值为假则返回真。也可以使用下面的语法。
表达式 '!=' 表达式          # 等同于 !(表达式 == 表达式)
表达式 '=~' 表达式          # 等同于 !(表达式 =~ 表达式)

5. 条件运算符
例子:
obj == 1 ? foo : bar
语法:
表达式1 ? 表达式2 : 表达式3
根据表达式 1 的结果,选择返回表达式 2 或表达式 3的值。它与
if 表达式1 then 表达式2 else 表达式3 end
完全相同。
3.1.6 变量和常量
Ruby 变量和常量的种类包括全局变量、实变量、局部变量和常量,它们都是根据其名称的第一个字符来区分的。通常,变量名称第二个字符开始是使用字母、数字或_。内部变量的名称中有一部分是$+ 1个符号的变量(详细介绍请参考内部变量)。
1. 全局变量
例子:
$foobar
以$开头的变量就是全局变量,程序的任何地方都能引用(因此应用时要特别注意)。全局变量没有必要事先声明。引用尚未初始化的全局变量其值为 nil。
2. 实变量
例子:
@foobar
以@开头的变量就是实变量,它属于特定的对象。实变量可以在其类或子类的方法中引用。实变量的默认值为 nil。
3. 局部变量
例子:
foobar
以小写字母或“_”开头的标识符就是局部变量或方法调用。在局部变量作用域(类、模块和定义方法的部分)中以小写字母开头的标识符要进行初始赋值,这个赋值属于局部变量的声明。
4. 常量
例子:
FOOBAR
以大写字母开头的标识符就是常量。常量的定义和初始化就是进行代入赋值,但是在方法里是不能定义的。访问未定义的常量会发生 NameError 异常。
常量可在类/模块中定义,除在该类/模块中可以引用外,该类的子类或嵌套该模块的类/模块中也能引用该常量。从外部引用常量要用到::运算符。而且,类和模块的名称也要和常量同时使用。
class Foo
  FOO = 'FOO'
end

class Bar < Foo
  p FOO             # => "FOO"
end

p Foo::FOO          # => "FOO"
类和嵌套外侧存在同名常数时,会先引用嵌套外侧的常数。也就是说,引用常数时会先搜索嵌套关系的外侧,然后才会按照继承关系向上搜索。而顶层常数定义并不是位于嵌套外侧,所以在搜索了继承关系之后才能找到它。可见顶层常数的优先级很低。
5. 伪变量
在普通变量以外,还有一种被称为伪变量的特殊变量。
(1) self
变量self是指当前方法的执行对象本身。
(2) nil
nil是NilClass 类唯一的实例,表示假。
(3) true
tre是TrueClass 类唯一的实例,表示真。
(4) false
    FalseClass 类唯一的实例。表示伪。
不能更改伪变量的值。为伪变量代入赋值会出现语法错误。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics