论坛首页 入门技术论坛

ruby 参考手册

浏览 2594 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2007-01-04  
卷首语

Ruby是一种简便快捷的面向对象编程的脚本语言.Ruby可以(像Perl一样)进行文本处理及系统管理,其功能十分丰富.同时,Ruby简单易懂,便于扩充.

若您正在寻找一种简单的面向对象语言,或者觉得Perl不够简便,又或者虽然喜欢Lisp的思想却讨厌到处都是括号的话,Ruby正是您不二的选择.

Ruby有下列优点.

解释器

    Ruby是解释型语言,因此执行程序时无需编译.
变量无类型(动态地确定类型)

    Ruby的变量可以接收各种类型的数据,因此没有必要担心变量类型的问题.另一方面,这弱化了编译检查的功能.
无需声明变量

    Ruby中无需声明变量即可使用.可以根据变量名分辨变量的种类(局部变量,全局变量,实例变量等).
语法简单

    受Eiffel影响,Ruby的语法十分简单.
内存管理无需用户干预

    Ruby自动进行内存管理.解释器内置的垃圾回收器会自动回收不再使用的对象.
一切都是对象

    从一开始Ruby就被设计成为纯粹的面向对象语言.包括整数这种基本的数据类型在内所有数据都被看是对象,进而得到统一的处理.
类,继承,方法

    Ruby当然具有面向对象语言的基本功能,包括类,继承和方法等.
特殊方法

    可向某对象添加方法.例如,可以把GUI按钮被按下时的动作作为方法记述下来,还可以用它来进行原型库(prototypebase)的面向对象编程(只要您想这样的话).
使用模块进行糅合(Mix-in)

    Ruby认为多重继承会导致问题复杂化,因此故意舍弃了多重继承,但可以使用模块超越类的界限来共享数据和方法等.这就是"Mix-in"糅合功能
迭代器

    迭代器功能可以将流程控制结构抽象化.
闭包

    可以将某过程片段对象化,对象化后的该过程片段就称作闭包.
功能强大的字符串操作/正则表达式

    以Perl为样板创造出了功能强大的字符串操作和正则表达式检索功能.
超长整数

    内置了处理超长整数的功能,所以只要内存允许就可以计算非常大的整数.例如计算400的阶乘等也轻而易举.
异常处理功能

    异常处理功能可以使您编写代码处理异常情况.
可以直接访问OS

    Ruby可以使用(UNIX的)绝大部分的系统调用.即使单独使用Ruby也可以进行系统编程.
动态加载

    若OS支持的话,可以在运行时读入对象文件.



请使用如下命令行启动Ruby解释器.

ruby [ option ...] [ -- ] [ programfile ] [ argument ...]

这里的"option"指下文将要提到的命令行选项中的一个。"--"则显式地表明选项字符串到此结束。"programfile"是装载Ruby脚本的文件。若省略不写或者写成"-"时,Ruby会把标准输入当做Ruby脚本进行处理。

programfile若以“#!”开始,则进行特殊的处理。详细情况请参考下文的关于解释器行。

argument中的字符串将变成内部常数ARGV的初始值。在有的环境中(Win32),标准shell不会展开通配符,这时Ruby解释器将自行展开通配符然后赋值给ARGV。此时,可使用的通配符有“*”、“?”、“[]”和“**/”(与Dir.glob不同,这里不能使用“{..}”)。在Win32环境中,若不想展开通配符的话,请使用单引号(')将参数括起来。
命令行选项

Ruby解释器可接受下列命令行选项。基本上与Perl的类似。

-0数字

    以8进制数指定输入记录分隔符('$/')。

    若不指定数字的话,分隔符是空字符(等同于$/="\0")。数字后面可以有其他的开关(switch)。

    -00代表段落模式(等同于$/=""),-0777(因为这个代码不代表任何文字)表示将文件的全部内容一次性读入(相当于$/=nil)。
-a

    与'-n'或'-p'一起使用时,可以打开自动拆分模式(auto split mode)。自动拆分模式将在各个循环前执行以下动作。

    $F = $_.split

    若没有同时指定'-n'或'-p'选项的话将不起作用。
-C directory

    执行脚本之前,先移动到指定目录。
-c

    只对脚本进行编译,而并不执行。编译后若没发现语法错误,则显示“Syntax OK”。
--copyright

    显示版权信息。
-Kc

    指定Ruby要处理的汉字编码。若是'E'或'e',则Ruby认定字符串或访问文件中的汉字编码为EUC。同样,若是'S'或's'的话则认定为SJIS。若是'U'或'u'则当作UTF-8处理。'N'表示不对汉字进行处理。该选项的默认值就是N(NONE)。

    将来有可能会改变文字编码处理方式,届时该选项的内容也会有所变化。
-d
--debug

    以调试模式执行脚本。将$DEBUG设置成true。
-e script

    在命令行中指定脚本。添加-e选项后,就不会从参数中抽取脚本文件名了。

    若多次使用-e选项时,系统会按照以下方式处理。

    下列各表达式的意义相同。
    ruby -e "5.times do |i|" -e "puts i" -e "end"

    ruby -e "5.times do |i|
      puts i
    end"

    ruby -e "5.times do |i|; puts i; end"

-Fregexp

    将regexp指定给输入域分隔符(field separator)。
-h
--help

    显示命令行选项的简介。
-i[extension]

    对参数中指定的文件内容进行替换(in-place edit)。原始文件将被加上扩展名并保存下来。若没有扩展名的话,将不会进行备份,而且只有替换后的文件会被保留下来。

    例:

    % echo matz > /tmp/junk
    % cat /tmp/junk
    matz
    % ruby -p -i.bak -e '$_.upcase!' /tmp/junk
    % cat /tmp/junk
    MATZ
    % cat /tmp/junk.bak
    matz

-I directory

    指定(追加)加载文件的路径。指定的目录将被追加到Ruby的数组变量($:)中。
-l

    进行行尾自动处理。首先,将$\改为$/的值,在print输出时添加换行。若使用了-n标志或-p标志的话,将对gets读入的各行末尾进行String#chop!处理。
-n

    若使用了该标志,则整个程序会像sed -n或awk一样,被

    while gets
     ...
    end

    括起来运行。
-p

    与-n标志相仿,在各循环后输出变量$_的值。

    例:

    % echo matz | ruby -p -e '$_.tr! "a-z", "A-Z"'
    MATZ

-r feature

    执行脚本前,先对feature指定的库执行require操作。与'-n'选项、'-p'选项一起使用时特别奏效。
-s

    对跟在脚本名后并且以'-'开头的参数进行解释,并将其值赋值给同名的全局变量。遇到以'--'开头的参数就停止解释,并将该参数从ARGV中删除。

    例:

    #! /usr/local/bin/ruby -s
    # prints "true" if invoked with `-xyz' switch.
    print "true\n" if $xyz

-S

    该选项表明,当脚本名不是以'/'开头的时候,要使用环境变量PATH的值搜索脚本。若您的机器不支持#!的话,可以使用下列方法模拟#!的运行:

    #!/bin/sh
    exec ruby -S -x $0 "$@"
    #! ruby

    因为第1行的关系,系统把脚本交给/bin/sh。/bin/sh执行第2行后启动Ruby解释器。在-x选项的作用下,Ruby解释器把从'#!'到包含'ruby'的行的内容全部读入。

    根据系统的不同,$0未必包含完整路径,因此有必要使用'-S'选项来告诉Ruby在必要时搜索脚本。
-T[level]

    执行不纯度测试。若给level指定了一个值之后,安全等级也会使用这个值。省略level时,其值为1。对于CGI程序来说,将其指定为-T1比较合适。$SAFE的等级也将被设定。
-v
--verbose

    冗长模式。启动时显示版本信息,然后将内部变量$VERBOSE设为true。当此变量为true时,众多的方法在运行时会显示冗长的信息。若只设定'-v'选项,而没有其他参数时,启动后会先显示版本信息,然后就结束运行(不会等待来自标准输入的脚本)。
--version

    显示Ruby的版本信息。
-w

    不显示版本信息的冗长模式。
-W[level]

    ruby 1.8 特性

    可以指定3种级别的冗长模式,如下所示。

        * -W0: 不显示警告
        * -W1: 只显示重要警告(默认)
        * -W2 or -W: 显示所有警告

    内部变量$VERBOSE被分别设置为nil,false,true。
-x[directory]

    从message中取出脚本并执行。读入脚本的范围是从'#!'开始,直到包含'ruby'的行为止。用EOF(文件结束),^D(controlD),^Z(controlZ)或保留字_END_来指定脚本结束。

    若指定了目录名的话,则在执行脚本前移动到该指定目录。
-y
--yydebug

    编译器调试模式。编译脚本时显示语法分析的过程。该显示过程会很漫长,可能只对那些想调试编译器的人有用。

关于解释器行

命令行指定的脚本是以'#!'开头的文件,当该行中不包含'ruby'时,将替代OS把'#!'后面的字符串看成命令行,然后启动解释器。

例如,用Ruby运行下面的shell脚本时将启动sh。

#!/bin/sh -vx
echo "$@"

若此行中包含'ruby'的话,则'ruby'左侧的部分将被忽略,右侧以'-'开头的部分被视为选项。

这里指定的选项将被追加到以命令行方式指定的选项之中。这主要是为了嵌入那些本该在脚本中指定的选项。例如,下面脚本的作用等价于使用命令行方式指定-Ke选项。

#! ruby -Ke


环境变量

Ruby解释器使用下列环境变量。

RUBYOPT

    指定默认情况下传给Ruby解释器的选项。

        *

          sh系

          RUBYOPT='-Ke -rkconv'
          export RUBYOPT

        *

          csh系

          setenv RUBYOPT '-Ke -rkconv'

        *

          MS-DOS系

          set RUBYOPT=-Ke -rkconv

RUBYPATH

    指定-S选项后,Ruby会搜索环境变量PATH指定的Ruby脚本。此时,该环境变量指定的目录也被纳入搜索范围(其优先程度高于PATH)。

        *

          sh系

          RUBYPATH=$HOME/ruby:/opt/ruby
          export RUBYPATH

        *

          csh系

          setenv RUBYPATH $HOME/ruby:/opt/ruby

        *

          MS-DOS系

          set RUBYPATH=%HOME%\ruby:\opt\ruby

RUBYLIB

    该环境变量把自己的值添加在Ruby库的搜索路径$:的默认值之前。

        *

          sh系

          RUBYLIB=$HOME/ruby/lib:/opt/ruby/lib
          export RUBYLIB

        *

          csh系

          setenv RUBYLIB $HOME/ruby/lib:/opt/ruby/lib

        *

          MS-DOS系

          set RUBYLIB=%HOME%\ruby\lib:\opt\ruby\lib

RUBYLIB_PREFIX

    该环境变量只在DJGPP版、Gygwin版、mswin32版和mingw32版的Ruby中有效。

    该环境变量的值是path1;path2或path1 path2。若Ruby库的搜索路径$:的前面部分与path1一致时,则将其替换为path2。

        *

          MS-DOS系

          set RUBYLIB_PREFIX=/usr/local/lib/ruby;d:/ruby

RUBYSHELL

    该环境变量只在OS2版、mswin32版和mingw32版的Ruby中有效。

    指定用system执行命令时所使用的shell。若省略此环境变量,则使用COMSPEC的值。
PATH

    用system等执行命令时的搜索路径。若没有设定时(其值为nil),将按照“/usr/local/bin:/usr/ucb:/usr/bin:/bin:.”进行搜索。

对象
对象概述

Ruby所能处理的值都是对象。Ruby操作对象时的办法只有一种,那就是方法调用。对象所属的类决定了对象所包含的方法。生成对象时,对象的所属类也就确定了,除非导入特殊类以外,对象的所属类不会改变。可以说对象是某个特定类的“实例”。

生成对象一般是通过调用别的对象的方法来实现的。


类决定了归属其下的对象的方法。若能调用归属对象的某方法时,我们就说该方法是“由类定义的”。方法由类和方法名决定。

每个类都必然拥有一个“超类”。若在超类中定义了一个方法,则在当前类中必然也会出现一个相同的方法,这叫做“继承”。继承是发展的,所以超类的超类中的方法也会出现在当前类中。
模块

include

这个有点特殊,它并不是类,但功能相同。
方法

方法是可以执行的。通常我们把执行方法的动作叫做“调用”。调用方法时可以传递对象,这个对象就是“参数”。可接受的参数数量是在定义方法时确定的,不可改变。



类和对象

在Ruby程序中,类也可以作为对象来处理。您可以进行下列基本操作。

    * 查询某方法是否存在
    * 定义方法
    * 取消方法
执行
Ruby 程序

Ruby程序的执行就是对代码进行计算的过程。先编译程序文本,遇到BEGIN就对其作出计算;然后计算顶层的一系列的表达式;若遇到END的话,将在最后对其进行处理然后结束程序(关于结束程序时处理步骤的详细过程请参考结束程序时的相关处理)。
语句
if

if句的计算过程如下:先对条件表达式进行计算,若为真则执行相应代码段,若为假则依次计算elseif部分的条件表达式,若遇到值为真的表达式则执行相应的代码段。若所有的条件表达式的值都为假的话,就执行else部分的代码段。

语句的值取决于最后执行的代码块的值。若最后的代码块中没有表达式,或者所有条件表达式的值都是假而且没有else部分的话,则语句的值为nil。
until
if 修饰部分
unless 修饰部分
while
until
while 修饰部分
until 修饰部分
for
begin ~ end
类定义句

定义类的内容。在执行时(而并非编译时)进行计算。

书写样式

class ClassName [< 超类表达式]
  表达式
end

在对类定义句进行计算时,将先试图生成类。若有超类表达式就加以计算,其值作为ClassName类的父类,然后生成ClassName类的实例.若没有超类表达式,就把Object作为其父类.

另一方面,若有同名类的话,就先使用那个同名类。然后处理超类表达式,若新生成的超类(在equal?)有所不同的话,就再生成一个新的类。

得到类之后就将其代入常数“ClassName”中,由此决定类名。此时,若同名的常数中被代入一个非Class的实例的话,就会引发异常TypeError。

最后生成新的框架(frame),向顶层块的self以及class设定要进行定义的类,然后在框架的基础上对定义句中的表达式进行计算。我们无法得到类定义句的值。

也就是说,在Ruby中我们可以多次“追加类定义”。
模块定义句

定义模块的内容。在执行时(而并非编译时)进行计算。

书写样式

module ModuleName
  模块内容
end

对模块定义句进行计算时,首先会生成新的无名模块。但是,若已经有了一个名为ModuleName的模块的话,就使用该模块。此时就变成“追加模块的定义”了。

得到模块后就将其代入常数ModuleName中。这个常数就成为模块的名称。此时,若向同名常数代入非模块的话就会引发异常TypeError。

最后生成新的框架(frame),向顶层块的self以及class中设定模块ModuleName,然后在框架的基础上对定义句中的表达式进行计算。模块定义句的值就是模块内容的最后一个表达式的值。若模块内容中没有可计算的表达式时,其值为nil。
特殊类定义句

定义对象的特殊类。在执行时(而并非编译时)进行计算。

书写样式

class << EXPR
  类的内容
end

先计算想定义特殊类的对象的表达式EXPR。然后生成该对象的特殊类(若尚未生成的话)。最后生成新框架,向顶层块的self和class中设定新生成的特殊类。在新框架的基础上对定义句中的表达式进行计算。

特殊类定义句的值取决于类的内容中的最后一个表达式的值。若没有可计算的表达式时,其值为nil。

请注意,Fixnum Symbol的实例以及 true false nil 不能定义特殊类
方法定义句

定义方法的内容。在执行时(而并非编译时)进行计算。

书写样式

def method_name(arg, argwithdefault=expr, *restarg, &block)
  方法内容
end

对其进行计算时,将向运行块的class中定义该方法。若class中已经存在同名的方法的话,则抛弃旧方法,添加新方法。

方法定义句的值为nil。
特殊方法定义句

向对象的特殊类中定义方法。在执行时(而并非编译时)进行计算。

书写样式

def expr.method_name(arg, argwithdefault=expr, *restarg, &block)
  方法内容
end

首先计算表达式expr。然后生成对象的特殊类(若尚未生成的话)。最后向特殊类中定义方法method_name。

特殊方法定义句的值为nil。

请注意,Fixnum Symbol的实例以及true false nil不能定义特殊方法。
BEGIN

编译时会用到(执行时首先计算)
END

编译时会用到(执行时最后计算)
方法
方法的调用

首先计算被调(receiver)表达式,得到被调用对象。省略被调表达式时,调用块的self将成为被调。

接下来从左到右地对参数表达式进行计算,检索被调里面的方法。若检索失败则引发异常NameError,成功的话就执行方法。

另外,执行方法的时候还可以添加块(block)。若向方法添加块时,只有当运行中的方法执行yield时才会对块进行计算。若没有执行yield的话,块将被忽视,不会执行。

将块传给方法时,该块将会继承调用方的块的self和class。只有Module#module_eval/class_eval和Object#instance_eval这三个例外,如下所示。

Module#module_eval, class_eval

    self和class都是被调(receiver)
Object.instance_eval

    self是被调,class是被调的特殊类

eval

把Proc对象和Binding对象传给eval的第二参数时,将在生成时的块的基础上对字符串进行计算。
方法的执行

当框架上只有一个块的情况下,才开始执行方法。下面我们暂时把这个块称作顶层块(top level block)。顶层块的self是被调,class尚未被定义。

首先,若有必选参数的话,就把得到值代入顶层块的局部变量。

若存在可选参数,且已被省略的话,则在顶层块上对默认值表达式进行计算,然后将得到的默认值代入顶层块的局部变量。若可选参数没被省略的话,就把得到的值代入顶层块的局部变量。

若存在*args这样的参数的话,则将剩下的所有参数以数组的形式代入局部变量。

另外,若存在块参数blockvar的话,则将传给方法的块进行Proc对象化,然后代入顶层块的局部变量blockvar中。若没向方法传递块的话,就代入nil。

接下来对方法内容进行计算,先计算方法层(method level)的rescue以及else部分,最后计算ensure部分。

整个方法的值取决于传递给return的值。若没有调用return的话,则取决于 方法内容/rescue/else 中最后被计算的表达式的值。若三个都为空的话,值为nil。
带块的方法调用

若向方法传递一个块的话,这个方法就叫做带块的方法。带块方法遇到yield时会转向块。

可以使用块参数。

break...若块位于堆栈框架(stack frame)上的话,就跳到框架的块的后面。break并结束带块方法,其值为nil。若块不在堆栈框架上,则引发异常LocalJumpError。

next 跳到块的终点

retry 这个就复杂了...
eval, instance_eval, module_eval


赋值

赋值是指让变量或常数记住某个对象。从语法的角度来看,虽然[]=和属性赋值的方法调用看起来很像是赋值,但却并非这里谈到的赋值。

我们可以反复地将各种对象赋值给变量。也可以将各种对象赋值给常数,但却只能赋值一次。也就是说,一旦将对象赋值给常数,就不能再更改。但这并不意味着赋值给常数的对象本身不允许更改,请您注意这点。
多重赋值

暂无
变量和常数

我们可以让变量或常数记住一个对象。这叫做“变量(常数)的赋值”。

当对变量或常数进行计算时,它就会返回记住的对象。这叫做“变量(常数)的调用”。

下面我们就分别来看一看变量和常数的赋值与调用过程。
局部变量

局部变量只属于一个块。块是指与代码的某个范围相对应的运行时的结构,可以嵌套。具体说来,它伴随带块的方法调用以及eval系方法的执行而出现。我们只能在局部变量的所属块以及该块的嵌套块中对局部变量进行赋值和引用。

同时,块被放在特定的“框架”上,并归属于该框架。因此,不能调用其他框架上的局部变量。所谓框架是指开始执行下列语句时生成的运行时的结构。

    * 程序文本的顶层(传递给ruby的文件名、-e、load)
    * 执行方法
    * 类/模块的定义句
    * BEGIN和END句

生成框架时自动搭载一个块,因此可以在这些语句中使用局部变量。

编译时,写入程序代码的局部变量将赋值给框架中的尚未定义的局部变量。局部变量被赋值时所在的块就是它的归属块。由此可知,编译时局部变量的定义过程已经完成(请注意,eval系的方法在执行过程中进行编译)。定义的变量的初始值为nil。

局部变量在定义和调用时,先是在块中从外到内地进行搜索。其结果就是,局部变量不能进行嵌套和屏蔽(shadowing)。但是,当若干的块处于非主从关系时,其内部可以包含不同的局部变量。

调用未定义(即没有在代码中标出)的局部变量时,Ruby会试图把它当作对self的(无参数的)方法调用来处理。若搜索方法失败则引发异常NameError。

再来看一下调用块的执行情况,块也可以带参数,但常被看做是在将要执行的块上进行的多重赋值.例如,下面代码的块在开始执行时

some_iterator do |a,b|
  ....
end

首先会进行下列操作。

a, b = <some_iterator 被yield的值 >

实例变量

实例变量属于一个对象,在self代表的块的范围内可以进行赋值和调用。实例变量的赋值过程同时也就是该变量的定义过程,若调用未定义的实例变量则返回nil。

remove_instance_variable
类变量

类变量为一个特定的类、该类的子类以及该类的实例所拥有。在以这些对象为self的块的范围内,可对其进行赋值和调用。最初的赋值过程也兼做定义。若调用一个未经定义的类变量的话就会引发异常NameError。

类变量的继承和“继承中止”
全局变量

在任何地方都可以对全局变量进行赋值和调用。最初的赋值过程兼做变量的定义,若调用一个未经定义的全局变量的话,就会返回nil。

可跟踪(?)
常数

常数属于类/模块。我们可以使用除method以外的方式对其进行赋值。最初的赋值兼做定义。对常数赋值时所在的块的class就是常数的归属类。有个非常特殊的例外,我们可以使用Module#const_set方法来定义常数,同时,还可以使用Module#remove_const来取消常数。

无法对已定义的常数进行再定义或赋值。实际上,只使用警告还可以进行赋值,但这只是一时的应急措施,并不符合规范。所以要少写这样的程序。

可调用范围因写法不同而有所差异。

只写出常数名时 (例: Const)

    可调用范围有:常数所属的类、子类、嵌套类的框架内的代码
写出完整路径时 (例: Mod::Cls::Const)

    可在任何地方调用

另外,像"::Const"这种前置"::"的写法,只有写成"Object::Const"时才能进行调用。
伪变量

下面这些变量看起来好像是局部变量,但实际上是保留字,返回确定的值。不可赋值。
self

返回该块的self。
nil

返回NilClass的唯一的实例--nil。
true

返回TrueClass的唯一的实例--true。
false

返回FalseClass的唯一的实例--false。


Ruby结束时的相关处理

当到达脚本尾部或出现了未能捕捉到的异常时,Ruby会结束程序(当调用函数exit、abort或对主线程发出Thread.kill命令时,会引发SystemExit异常)。结束程序时会依次进行下列处理。

   1. 对所有线程执行Thread.kill。
   2. 若注册了Ruby的伪信号SIGEXIT的处理程序的话,就执行它(请参考trap)。
   3. 若注册了END块(END {...}或at_end函数指定的块)的话,就以与记录相反的顺序执行它.若在该块的执行过程中发生大规模退出(non-local exit)的话,就中断该块的运行.但脚本仍将继续运行.
   4.

      在ObjectSpace.define_finalizer中,若注册了finalizer的话就执行它(执行顺序不定).若在执行finalizer的过程中,发生了大规模退出的话,就中断finalizer的运行,但脚本仍将继续运行.
   5.

      使用exit(3)结束程序.

      此时传来的状态值取决于程序的结束方法.例如,
          * 到达脚本尾部时 值为0
          * 传递给exit的参数
          * 若非由SystemExit引起的异常的话,其值为1
          * 若明显是SystemExit的话,其值为0

使用exit!函数时,将只执行exit(2),而不会执行上述任何步骤.

论坛首页 入门技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics