第一章:Ruby语言最小化
上一篇 / 下一篇 2011-04-30 01:03:40 / 个人分类:JAVA学习&编程相关
谈到Ruby语言,这里只是简单解释了理解第一部分所需的知识。这里并没有完全指出编程中需要注意的地方,本章的目的在于阅读,而非编写Ruby程序。有Ruby经验的人可以放心的跳过这一章。
关于语法,在第二部分中,我们会一直谈到令人厌倦,所以,本章就不做详尽的讨论了。只有那些用得最多的,比如hash字面量,会谈到一些变化。原则上,不省略那些可能省略的东西。因为这样会让语法规则变得简单,不必到处去写“此处省略”。
关于语法,在第二部分中,我们会一直谈到令人厌倦,所以,本章就不做详尽的讨论了。只有那些用得最多的,比如hash字面量,会谈到一些变化。原则上,不省略那些可能省略的东西。因为这样会让语法规则变得简单,不必到处去写“此处省略”。
对象
字符串
Ruby程序中可操作的东西都是对象,没有像Java的int和long这样的基本类型(primitive)。比如,下面这样写就是一个内容为“content”的字符串对象(String对象)。
简单的说,这里调用了一个字符串对象,更准确的说法是,这是一个产生字符串对象的“表达式”。因此,如果你写了多次,那么每次都会生成一个字符串对象。
这里生成了三个内容同为“content”的对象。
对了,仅仅这样,程序员是无法看到对象的。下面是在终端上显示对象的方法。
“#”之后是注释。今后,结果都会以注释的形式给出。
“p(……)”表示调用函数p,任何对象都可以显示出来,基本上,它就是一个调试函数。
严格说来,Ruby没有函数,不过,可以把它认为是函数。这个“函数”可以用在任何地方。
各种各样的字面量(literal)
接下来,稍微说明一下可以直接生成对象的表达式(字面量)。先来看一下整数和小数。
不要忘了,这些都是生成对象的表达式。重复一下,Ruby中没有“基本类型”。
下面的表达式生成数组对象。
这段程序生成了一个数组,它按顺序包含1、2、3三个整数作为其元素。数组元素可以使用任意对象,这样也可以。
下面的表达式生成hash表。
hash表是一种在任意对象间建立一一对应关系的结构。上面表达的是一个如下关系的表。
对这样创建出的一个hash表对象,这样问,“什么与‘key’对应?”,如果它能够听到的话,“是‘value’。” 怎么听?使用方法。
方法调用
方法可以通过对象调用。C++术语称之为成员函数。没有必要深入解释什么是方法, 这里只简单解释一下记法。
这表示对字符串对象(其内容为“content”)调用upcase方法。upcase是这样一个方法,它返回一个新的字符串对象,将小写字母转换为大写字母,其结果如下:
方法可以连续调用。
这里对"content".upcase()的返回值对象调用downcase方法。
另外,不同于Java或C++拥有公有的字段(成员变量),Ruby对象的接口只有方法。
Ruby程序中可操作的东西都是对象,没有像Java的int和long这样的基本类型(primitive)。比如,下面这样写就是一个内容为“content”的字符串对象(String对象)。
"content"
简单的说,这里调用了一个字符串对象,更准确的说法是,这是一个产生字符串对象的“表达式”。因此,如果你写了多次,那么每次都会生成一个字符串对象。
"content" "content" "content"
这里生成了三个内容同为“content”的对象。
对了,仅仅这样,程序员是无法看到对象的。下面是在终端上显示对象的方法。
p("content") # 显示"content"
“#”之后是注释。今后,结果都会以注释的形式给出。
“p(……)”表示调用函数p,任何对象都可以显示出来,基本上,它就是一个调试函数。
严格说来,Ruby没有函数,不过,可以把它认为是函数。这个“函数”可以用在任何地方。
各种各样的字面量(literal)
接下来,稍微说明一下可以直接生成对象的表达式(字面量)。先来看一下整数和小数。
# 整数 1 2 100 9999999999999999999999999 # 多大的数都能用 # 小数 1.0 99.999 1.3e4 # 1.3×10^4
不要忘了,这些都是生成对象的表达式。重复一下,Ruby中没有“基本类型”。
下面的表达式生成数组对象。
[1, 2, 3]
这段程序生成了一个数组,它按顺序包含1、2、3三个整数作为其元素。数组元素可以使用任意对象,这样也可以。
[1, "string", 2, ["nested", "array"]]
下面的表达式生成hash表。
{"key"=>"value", "key2"=>"value2", "key3"=>"value3"}
hash表是一种在任意对象间建立一一对应关系的结构。上面表达的是一个如下关系的表。
"key" → "value" "key2" → "value2" "key3" → "value3"
对这样创建出的一个hash表对象,这样问,“什么与‘key’对应?”,如果它能够听到的话,“是‘value’。” 怎么听?使用方法。
方法调用
方法可以通过对象调用。C++术语称之为成员函数。没有必要深入解释什么是方法, 这里只简单解释一下记法。
"content".upcase()
这表示对字符串对象(其内容为“content”)调用upcase方法。upcase是这样一个方法,它返回一个新的字符串对象,将小写字母转换为大写字母,其结果如下:
p("content".upcase()) # 显示"CONTENT"
方法可以连续调用。
"content".upcase().downcase()
这里对"content".upcase()的返回值对象调用downcase方法。
另外,不同于Java或C++拥有公有的字段(成员变量),Ruby对象的接口只有方法。
程序
顶层(top level)
直接写出Ruby表达式就是程序,不必像Java或C++那样定义main()。
这就是一个完整的程序了。把这段代码放到一个名为first.rb的文件中,就可以在命令行下执行了:
如果使用ruby的-e选项,连创建文件都省了。
对了,p所写的地方在程序的嵌套层次上是最低的,也就是说,它位于程序的最上层,称为“顶层”。拥有顶层是Ruby作为脚本语言的一大特征。
在Ruby中,基本上一行就是一条语句,最后面无需分号。因此,下面的程序可以解释为三条语句。
如果执行的话,结果是这样。
局部变量
在Ruby中,无论是变量还是常量,全都保持着引用(reference)。因此,将一个变量赋值给另一个变量时,不会发生复制。可以考虑一下Java的对象类型变量、C++的对象指针。然而,指针自身的值是无法修改的。
Ruby变量的首字符表示变量的类型(作用域)。以小写字母或下划线开头的表示局部变量。 “=”表示赋值。
最初的赋值兼做变量声明,无需额外声明。变量是无类型的,因此,无论何种类型,都可以无差别的赋值。下面的程序完全合法。
虽说可以,不过没有必要。把不同类型的变量赋值给一个变量通常只能增加阅读的难度。实际的Ruby程序中很少这样做。这里的做法完全是为了举例。
访问变量是一种常见的记法。
随后是一个变量保持引用的例子。
程序执行后,a、b、c三个局部变量指向同一个对象——第一行生成的字符串对象“content”,如图1所示。
图1: Ruby变量保持对象引用
对了,这里的“局部”是某种程度上的局部,暂且还无法解释它的范围。先要说一下,顶层只是一种“局部”作用域。
常量
变量名以大写字母开头的是常量。因为是常量,只能对它进行一次(第一次)赋值。
再次赋值会造成错误。说实话,只是警告,不是错误。这么做是为了让一些操作Ruby程序本身的应用程序,比如开发环境,在加载两次文件的时候,不会报错。也就是说,这是为实用而做的妥协,本来应该报错。实际上,直到1.1版本都会报错。
很多人为“常量”这个词所欺骗,认为常量就是“所指对象一旦被记住,便不再改变”。实际上,并不是常量所指的对象不再改变。如果用英语表达的话,read only比constant更能表现其意图(图2)。顺便说一下,另有一个名为freeze的方法用于让对象本身不变。
图2: 常量read only的含义
实际上,常量的作用域还没有谈到。在下一节中,我们会结合类来谈一下。
控制结构
Ruby的控制结构很丰富,单单罗列出来就很困难了。先来谈谈if和while。
对于条件表达式,只有两个对象——false和nil——为假,其余所有对象都是真。0和空字符串也是真。
顺便说一下,当然不会只有false,还有true。它当然是真。
直接写出Ruby表达式就是程序,不必像Java或C++那样定义main()。
p("content")
这就是一个完整的程序了。把这段代码放到一个名为first.rb的文件中,就可以在命令行下执行了:
% ruby first.rb "content"
如果使用ruby的-e选项,连创建文件都省了。
% ruby -e 'p("content")' "content"
对了,p所写的地方在程序的嵌套层次上是最低的,也就是说,它位于程序的最上层,称为“顶层”。拥有顶层是Ruby作为脚本语言的一大特征。
在Ruby中,基本上一行就是一条语句,最后面无需分号。因此,下面的程序可以解释为三条语句。
p("content") p("content".upcase()) p("CONTENT".downcase())
如果执行的话,结果是这样。
% ruby second.rb "content" "CONTENT" "content"
局部变量
在Ruby中,无论是变量还是常量,全都保持着引用(reference)。因此,将一个变量赋值给另一个变量时,不会发生复制。可以考虑一下Java的对象类型变量、C++的对象指针。然而,指针自身的值是无法修改的。
Ruby变量的首字符表示变量的类型(作用域)。以小写字母或下划线开头的表示局部变量。 “=”表示赋值。
str = "content" arr = [1,2,3]
最初的赋值兼做变量声明,无需额外声明。变量是无类型的,因此,无论何种类型,都可以无差别的赋值。下面的程序完全合法。
lvar = "content" lvar = [1,2,3] lvar = 1
虽说可以,不过没有必要。把不同类型的变量赋值给一个变量通常只能增加阅读的难度。实际的Ruby程序中很少这样做。这里的做法完全是为了举例。
访问变量是一种常见的记法。
str = "content" p(str) # 显示"content"
随后是一个变量保持引用的例子。
a = "content" b = a c = b
程序执行后,a、b、c三个局部变量指向同一个对象——第一行生成的字符串对象“content”,如图1所示。
图1: Ruby变量保持对象引用
对了,这里的“局部”是某种程度上的局部,暂且还无法解释它的范围。先要说一下,顶层只是一种“局部”作用域。
常量
变量名以大写字母开头的是常量。因为是常量,只能对它进行一次(第一次)赋值。
Const = "content" PI = 3.1415926535 p(Const) # 显示"content"
再次赋值会造成错误。说实话,只是警告,不是错误。这么做是为了让一些操作Ruby程序本身的应用程序,比如开发环境,在加载两次文件的时候,不会报错。也就是说,这是为实用而做的妥协,本来应该报错。实际上,直到1.1版本都会报错。
C = 1 C = 2 # 实际中只是给出警告,理论上应该报错
很多人为“常量”这个词所欺骗,认为常量就是“所指对象一旦被记住,便不再改变”。实际上,并不是常量所指的对象不再改变。如果用英语表达的话,read only比constant更能表现其意图(图2)。顺便说一下,另有一个名为freeze的方法用于让对象本身不变。
图2: 常量read only的含义
实际上,常量的作用域还没有谈到。在下一节中,我们会结合类来谈一下。
控制结构
Ruby的控制结构很丰富,单单罗列出来就很困难了。先来谈谈if和while。
if i < 10 then # 主体 end while i < 10 do # 主体 end
对于条件表达式,只有两个对象——false和nil——为假,其余所有对象都是真。0和空字符串也是真。
顺便说一下,当然不会只有false,还有true。它当然是真。
类与方法
类
本来在面向对象系统中,方法属于对象。但那完全是理想世界的事。在普通的程序中,会有大量拥有相同方法集合的程序,坦率的说,以对象为单位去记忆其所拥有的方法并不是件容易的事。通常是用类或多方法(multi method)这样的结构来减少方法的重复定义。
在Ruby中,将对象与方法连在一起的机制采用了传统的”类”的概念。也就是说,所有对象都属于某个类,由类决定可以调用的方法。这时,就称对象是“某某类的实例(instance)”。
比如,字符串“str”是String类的一个实例。并且,String类定义了upcase、downcase、strip,以及其它一些方法,所有字符串对象都可以响应这些方法。
不过,如果调用的方法没有定义怎么办?静态语言的话,会造成编译错误,而Ruby则会成为运行时异常。实际试一下。这种长度的程序还是用-e运行比较方便。
如果找不到方法,就会抛出一个名为NoMethodError的错误。
最后,为那个一遍遍说的令人厌烦的“String的upcase方法”准备了一个专用的记法。 “String#upcase”表示“定义在String类中的upcase方法”。
顺便说一下,写成“String.upcase”在Ruby世界里有完全不同的意思。至于是什么意思?下节分解。
类的定义
迄今为止,都是已经定义的类。当然,还可以定义自己的类。类可以用class语句定义。
这里定义了一个新的类C。定义后,可以如下这样用。
请注意,生成实例用的不是new C。敏锐的读者可能注意到了,C.new()的记法像是一个方法调用。在Ruby中,生成对象的表达式只是一个方法调用。
首先,Ruby的类名与常量名是等价的。那么,与类名同名的常量是什么呢?实际上,就是这个类。在Ruby中,所有能操作的东西都是对象。类自然也是对象。这种对象称为类对象。所有的类对象都是Class类的实例。
也就是说,创建新类对象的class语句,其动作是将类对象赋值给一个与类同名的常量。另一方面,生成实例的操作是,访问这个常量,通过该对象调用方法(通常是new)。看看下面的例子就可以知道,实例生成同普通的方法调用没有区别。
正是因为这样,Ruby中没有new这个保留字。
接下来,可以用p将生成的类实例显示出来。
不过,它到底无法像字符串和整数那样表示得那么漂亮,显示的是类名和所属的内部ID。这个ID表示的是指向该对象指针的值。
是的是的,可能你已经完全忘了方法名的记法。 “Object.new”表示通过类对象Object本身调用new方法。因此,“Object#new”和“Object.new”完全是两码事,需要严格区分。
obj = Object.new() # Object.new
obj.new() # Object#new
实际上,Object#new并没有定义,像这个程序的第二行会造成错误。希望你只把它当作一个例子。
方法的定义
即便定义了类,没有定义方法也是没有意义的。让我们试着在类C中定义方法。
定义方法用def语句。这个例子中定义了myupcase方法。有一个名为str的参数。同变量一样,参数和返回值都不需要写类型。而且可以有多个参数。
试着用一下定义的方法。缺省情况下,方法可以在外部调用。
当然,习惯之后便无需一个个的赋值了。下面的写法也是一样的。
self
在方法执行过程中,通常会保留自己(方法调用的实例)是谁的信息,这个信息可以通过self得到。类似于C++或Java中的this。我们来确认一下。
如你所见,两个表达式返回的是同样的对象。可以确认,对c调用方法,其self就是c。
那么,通过自身调用方法该怎么做才好呢?首先要考虑通过self进行调用。
不过,调用“自己的”方法还要特意指定,太麻烦。因此,对self的调用可以省略调用方法的目标对象(接收者,receiver)。
实例变量
还有一种说法,对象是数据 + 代码,所以,仅仅定义方法还是没什么用。有必要以对象为单位来记住数据,也就是实例变量,在C++中称为成员变量。
根据Ruby的变量命名规则,第一个字母决定类型。实例变量是“@”。
实例变量不同于前面介绍的变量,即便不赋值(甚至不定义)也一样可以访问。这种情况下会变成怎样呢……接着前面的代码继续尝试。
没有set就get,结果显示nil。nil表示一个“没有”的对象。存在对象却“没有”,很不可思议,没办法,它就是这样。
nil也可以作为一个字面量使用。
initialize
正如我们看到的,即便是刚刚定义的类也可以调用new方法创建实例。的确如此,不过,有时需要对一个类进行特定的初始化。这时要修改的不是new方法,而是一个名为initialize的方法。它会在new的过程中调用。
本来在面向对象系统中,方法属于对象。但那完全是理想世界的事。在普通的程序中,会有大量拥有相同方法集合的程序,坦率的说,以对象为单位去记忆其所拥有的方法并不是件容易的事。通常是用类或多方法(multi method)这样的结构来减少方法的重复定义。
在Ruby中,将对象与方法连在一起的机制采用了传统的”类”的概念。也就是说,所有对象都属于某个类,由类决定可以调用的方法。这时,就称对象是“某某类的实例(instance)”。
比如,字符串“str”是String类的一个实例。并且,String类定义了upcase、downcase、strip,以及其它一些方法,所有字符串对象都可以响应这些方法。
- # 因为全都属于字符串类,所以定义了同样的方法
- "content".upcase()
- "This is a pen.".upcase()
- "chapter II".upcase()
- "content".length()
- "This is a pen.".length()
- "chapter II".length()
# 因为全都属于字符串类,所以定义了同样的方法 "content".upcase() "This is a pen.".upcase() "chapter II".upcase() "content".length() "This is a pen.".length() "chapter II".length()
不过,如果调用的方法没有定义怎么办?静态语言的话,会造成编译错误,而Ruby则会成为运行时异常。实际试一下。这种长度的程序还是用-e运行比较方便。
- % ruby -e '"str".bad_method()'
- -e:1: undefined method `bad_method' for "str":String (NoMethodError)
% ruby -e '"str".bad_method()' -e:1: undefined method `bad_method' for "str":String (NoMethodError)
如果找不到方法,就会抛出一个名为NoMethodError的错误。
最后,为那个一遍遍说的令人厌烦的“String的upcase方法”准备了一个专用的记法。 “String#upcase”表示“定义在String类中的upcase方法”。
顺便说一下,写成“String.upcase”在Ruby世界里有完全不同的意思。至于是什么意思?下节分解。
类的定义
迄今为止,都是已经定义的类。当然,还可以定义自己的类。类可以用class语句定义。
class C end
这里定义了一个新的类C。定义后,可以如下这样用。
class C end c = C.new() # 生成C的实例,赋值给变量c
请注意,生成实例用的不是new C。敏锐的读者可能注意到了,C.new()的记法像是一个方法调用。在Ruby中,生成对象的表达式只是一个方法调用。
首先,Ruby的类名与常量名是等价的。那么,与类名同名的常量是什么呢?实际上,就是这个类。在Ruby中,所有能操作的东西都是对象。类自然也是对象。这种对象称为类对象。所有的类对象都是Class类的实例。
也就是说,创建新类对象的class语句,其动作是将类对象赋值给一个与类同名的常量。另一方面,生成实例的操作是,访问这个常量,通过该对象调用方法(通常是new)。看看下面的例子就可以知道,实例生成同普通的方法调用没有区别。
S = "content" class C end S.upcase() # 得到常量S表示的对象,调用upcase方法 C.new() # 得到常量C表示的对象,调用new方法
正是因为这样,Ruby中没有new这个保留字。
接下来,可以用p将生成的类实例显示出来。
class C end c = C.new() p(c) # #<C:0x2acbd7e4>
不过,它到底无法像字符串和整数那样表示得那么漂亮,显示的是类名和所属的内部ID。这个ID表示的是指向该对象指针的值。
是的是的,可能你已经完全忘了方法名的记法。 “Object.new”表示通过类对象Object本身调用new方法。因此,“Object#new”和“Object.new”完全是两码事,需要严格区分。
obj = Object.new() # Object.new
obj.new() # Object#new
实际上,Object#new并没有定义,像这个程序的第二行会造成错误。希望你只把它当作一个例子。
方法的定义
即便定义了类,没有定义方法也是没有意义的。让我们试着在类C中定义方法。
class C def myupcase( str ) return str.upcase() end end
定义方法用def语句。这个例子中定义了myupcase方法。有一个名为str的参数。同变量一样,参数和返回值都不需要写类型。而且可以有多个参数。
试着用一下定义的方法。缺省情况下,方法可以在外部调用。
c = C.new() result = c.myupcase("content") p(result) # 显示"CONTENT"
当然,习惯之后便无需一个个的赋值了。下面的写法也是一样的。
p(C.new().myupcase("content")) # 同样显示"CONTENT"
self
在方法执行过程中,通常会保留自己(方法调用的实例)是谁的信息,这个信息可以通过self得到。类似于C++或Java中的this。我们来确认一下。
- class C
- def get_self()
- return self
- end
- end
- c = C.new()
- p(c) # #<C:0x40274e44>
- p(c.get_self()) # #<C:0x40274e44>
class C def get_self() return self end end c = C.new() p(c) # #<C:0x40274e44> p(c.get_self()) # #<C:0x40274e44>
如你所见,两个表达式返回的是同样的对象。可以确认,对c调用方法,其self就是c。
那么,通过自身调用方法该怎么做才好呢?首先要考虑通过self进行调用。
- class C
- def my_p( obj )
- self.real_my_p(obj) # 通过自身调用方法
- end
- def real_my_p( obj )
- p(obj)
- end
- end
- C.new().my_p(1) # 显示1
class C def my_p( obj ) self.real_my_p(obj) # 通过自身调用方法 end def real_my_p( obj ) p(obj) end end C.new().my_p(1) # 显示1
不过,调用“自己的”方法还要特意指定,太麻烦。因此,对self的调用可以省略调用方法的目标对象(接收者,receiver)。
- class C
- def my_p( obj )
- real_my_p(obj) # 可以不指定调用的接收者
- end
- def real_my_p( obj )
- p(obj)
- end
- end
- C.new().my_p(1) # 显示1
class C def my_p( obj ) real_my_p(obj) # 可以不指定调用的接收者 end def real_my_p( obj ) p(obj) end end C.new().my_p(1) # 显示1
实例变量
还有一种说法,对象是数据 + 代码,所以,仅仅定义方法还是没什么用。有必要以对象为单位来记住数据,也就是实例变量,在C++中称为成员变量。
根据Ruby的变量命名规则,第一个字母决定类型。实例变量是“@”。
- class C
- def set_i(value)
- @i = value
- end
- def get_i()
- return @i
- end
- end
- c = C.new()
- c.set_i("ok")
- p(c.get_i()) # 显示"ok"
class C def set_i(value) @i = value end def get_i() return @i end end c = C.new() c.set_i("ok") p(c.get_i()) # 显示"ok"
实例变量不同于前面介绍的变量,即便不赋值(甚至不定义)也一样可以访问。这种情况下会变成怎样呢……接着前面的代码继续尝试。
c = C.new() p(c.get_i()) # 显示nil
没有set就get,结果显示nil。nil表示一个“没有”的对象。存在对象却“没有”,很不可思议,没办法,它就是这样。
nil也可以作为一个字面量使用。
p(nil) # 显示nil
initialize
正如我们看到的,即便是刚刚定义的类也可以调用new方法创建实例。的确如此,不过,有时需要对一个类进行特定的初始化。这时要修改的不是new方法,而是一个名为initialize的方法。它会在new的过程中调用。
- class C
- def initialize()
- @i = "ok"
- end
- def get_i()
- return @i
- end
- end
- c = C.new()
- p(c.get_i()) # 显示"ok"
class C def initialize() @i = "ok" end def get_i() return @i end end c = C.new() p(c.get_i())
TAG:
标题搜索
日历
|
|||||||||
日 | 一 | 二 | 三 | 四 | 五 | 六 | |||
1 | 2 | ||||||||
3 | 4 | 5 | 6 | 7 | 8 | 9 | |||
10 | 11 | 12 | 13 | 14 | 15 | 16 | |||
17 | 18 | 19 | 20 | 21 | 22 | 23 | |||
24 | 25 | 26 | 27 | 28 | 29 | 30 | |||
31 |
我的存档
数据统计
- 访问量: 709711
- 日志数: 415
- 图片数: 1
- 文件数: 3
- 建立时间: 2008-12-07
- 更新时间: 2015-07-14