Ruby 吸取了 perl 的正则表达式, python 的简单性可读性,smalltalk 的纯面向对象语法和单继承,LISP 的无穷嵌套的语法,Java的线程…
Page 40
面向对象
2008/7/13
1. 消息机制:对象通过消息影响对象,对象即类的实例。谁,将被影响?
2. Object:东西,物体,客体。
3. Class:种类,等级,阶级;
4 动态语言:人们可以决定代码世界中一类事物的属性、方法,当然可以修改代码世界中一类事物的属性、方法,而且可以委托其它的类来修改,甚至删除。这是动态语言超越静态语言之处。由于代码是一直运行着,与其它代码一直交互着,修改、删除应该慎重,避免产生副作用。
5 封装
注意:
类名首字母要大写;
实例变量用@开头;
方法名或者参数名用小写字母或者下划线开头。
6 initialize是初始化方法
p2.motherland="ABC" #以赋值,因为有对应的setter方法。
|
setter: |
getter: |
|
attr_writer:motherland相当于 |
attr_reader:motherland相当于 |
|
def motherland=(value)
return @motherland=value
end |
def motherland
return @motherland
end |
attr_accessor :motherland
相当于 attr_reader:motherland; attr_writer :motherland
7.继承
class 子类<父类
子类继承父类:
重写方法;
添加方法;
增强父类方法(super)。
Ruby语言已经定义了一个类Object,如果你在定义新类的时候,没有指明新类的父类,那么,Ruby解释器认为,新类的父类是 Object 类。类Object 含有new方法、initialize 方法…只要你不重写这些方法,你就自然在使用类Object 的方法。
你写一个类的时候,是在创造一类事物的蓝图;当你new的时候,一个实例就按照蓝图生成了。
Ruby没有析构函数,使用垃圾收集器自动回收实例占用的资源。
8.多态
父类子类的行为差异,不同子类的行为差异,就是多态
Ruby语言,只有重写(override),没有其它语言具有的严格意义上的重载(overload)。Ruby语言有自己的单例方法,还有模块插入(Mix-in)。
插曲
http://liubin.itpub.net/post/325/15623
覆盖 override:
Overriding 也许叫做overwriting更合适,
OVERLOAD覆盖是指在子类(c++中的派生类) 中重新定义父类的函数,其函数名、参数列、返回值类型必须同父类中的相对应被覆盖的函数严格一致,覆盖函数和被覆盖函数只有函数体(花括号中的部分)不同,当派生类对象调用子类中该同名函数时会自动调用子类中的覆盖版本,而不是父类中的被覆盖函数版本。
重载(overload):
在同一个类中,出现多个同名的方法的现象就是Overload重载事发生在同一个类中,不同方法之间的现象。
在c++或者java中,方法一般为
返回类型 方法名(参数1,参数2)
判断2个方法是不是overload,主要指方法名一样,参数不一样,参数不一样指的是参数的个数,相同位置的参数的类型是否一样,而与参数(型参)的名称无关(参数类型/个数/顺序,不同),与返回类型也无关。程序会根据不同的参数列来确定需要调用的函数比如c++或者java中,这都是overload ruby中,不存在这样的overload
多态(polymorphism)
至于多态,我还没有见过一个看一眼就能明白的定义。
有的说是允许将子类类型的指针赋值给父类类型的指针,当然java中没有指针的概念。
多态有时候也被称为动态绑定或者晚绑定或运行时绑定,意思是编译的时候不必关心,运行的时候才决定调用哪个对象的哪个方法。
我觉得多态的用途之一就是在父类提供一个接口(服务),然后调用的时候用的却是子类的具体实现。
page 49
Ruby前前后后
1.动态语言
编程语言将向动态回归;
命令式语言和函数式语言将融合。
Ruby在语法层次实现了冗余。
Ruby语言的冗余性、缺陷性和动态性正是现实世界的真实写照。
page51
2.动态变量类型
|
命令式语言 |
将操作数演化成现在我们熟悉的变量,将操作码演化成方法(或叫函数),对变量执行各种操作。 |
|
面向对象编程又将基本变量和方法封装在一起,成为一个更复杂的变量——对象。但是,在一个类中仍然区分基本变量和方法。 |
|
函数式语言 |
一开始的函数式语言不区分变量和方法,一切都是表(list),表就是能够不断分解成单个元素的数学符号。表可以是变量,可以是方法。 |
|
后来的有些函数式语言,吸取了命令式语言的语法,也区分变量和方法。 |
Ruby: 命令式语言,融合了函数式语言的语法,变量和方法区分得不很明显。
变量名,变量值,变量类型,变量的作用域;“变量名,变量值”,必要的。
动态类型语言:由编译内核(或解释内核)在运行时刻来判断变量类型的语言。
弱类型语言:弱化了类型的概念,变量在运行中能够随时代表不同的事物,而不管事物是什么类型。
Ruby语言有基本类型。
纯粹的函数式语言中没有变量作用域的概念。
Ruby中有变量作用域概念,如变量名前缀字符。
实际应用中,有时会比较复杂,在使用闭包时会显现出来。
Ruby语言中,一切都是对象,变量不是对象,变量只是引用某个对象的时候的一个代号。
Ruby是动态类型语言,不用给任何变量指定数据类型,解释器会在你第一次赋值给变量时,在内部将数据类型记录下来。
Ruby语言中,一个变量被赋予了某个数据类型的值,在程序中你可以随时再赋予这个变量其它数据类型的值。
Ruby 是在运行中检测语法,只要与语法定义不矛盾,就能通过。
Ruby 的动态类型特点是一把双刃剑,
死盯住变量的命名。用一些有意义的名字,不必太长,但是应该少用单字符,除非是循环指针变量。在一个项目组中,程序员是要彼此相互沟通合作的。
3.改变蓝图——类
Ruby是动态语言,你可以改变Ruby程序的结构,功能。
在Ruby程序运行中,方法、属性可以被加入或去除,新的类或对象可以被建立,新的模块可以出现。
除了修改方法,添加方法,还可以除去方法。
Ruby是动态语言:灵活,强大,初学者容易犯错误;
4.编码建议
(1) 命名
|
常量 |
大写,下划线分隔 |
MAX |
|
类名/模块名 |
大写字母开头 |
MyClass |
|
方法名 |
小写,下划线分隔 |
is_prime? |
|
变量/参数名 |
小写字母开头单词组合 |
currentValue |
注意:
类名、模块名、变量名、参数名最好使用“名词”或者“形容词+名词”。
方法名最好使用“动词”或者“动词+名词”。例如:aStudent.talk。
在Ruby里,有时将“!”和“?”附于某些方法名后面。惊叹号“!”暗示这个方法具有破坏性,有可能会改变传入的参数。问号“?”表示这个方法是一个布尔方法,只会返回true或false。
(2) 空格
关键字之后要留空格。
逗号“,”、分号“;”之后要留空格。
“,”、“;”向前紧跟,紧跟处不留空格。
赋值操作符、比较操作符、算术操作符、逻辑操作符,如“=”、“+=”“>=”、“<=”、“+”、“*”、“%”、“&&”、“||”等二元操作符的前后应当加空格。
一元操作符如“!”、“~”等之后不加空格。
象“[]”、“.”、“::”这类操作符前后不加空格。
函数名之后不要留空格,紧跟左圆括号“(”,以与关键字区别。左圆括号“(”向后紧跟,右圆括号“)”向前紧跟,紧跟处不留空格。
(3) 圆括号
Ruby中圆括号常常被省略
优先规则会自动确定哪个参数被哪个方法使用。
建议除了极简单的情况,还是使用圆括号为好。
圆括号还可以把几个语句约束成一个语句集合。
(4) return
在定义方法的时候,在最后一行可以显式地return某个值或几个值,但不是必须的。
Ruby方法的最后一行语句如果是表达式,表达式的值会被自动返回;最后一行语句如果不是表达式,就什么也不返回。
return不仅仅用在方法的最后一行。使用break能够跳出本层循环,如果要从多重循环体中跳出,可以使用return,结束这个方法;return还能够从方法的某个执行点立即退出,而不理会方法的其余代码。
(5) 注释
注释表明了一段代码块的功能、意图或是代码块的解释,应该简洁明了,错误的注释不如没有注释。一般地,注释的位置应与被描述的代码相邻,可以放在代码的上方或右方,不要放在代码的下方。
page 61
面向对象
1. 重载&重写
多态性:重写/覆盖/覆写(override) 重载(overload)
|
重载(overload) |
重写(override) |
|
一个类中,方法名相同、参数列表不同的几个方法,调用时根据不同的参数调用不同的方法。方法重载与返回类型无关。 |
子类有一个方法,方法名、参数列表、返回类型与父类的某个方法完全一致。调用时会调用子类的方法,而屏蔽掉父类的同名方法。需要注意的是,子类覆写的方法,其可访问性一定要强于或等同于,父类被覆写的同名方法。 |
|
重载不仅仅是发生在子类和父类之间,大多数时候,发生在同一个类中。 |
覆写发生在子类和父类之间,当然也可以是子类和父类的父类之间。 |
成员函数被重载的特征: (1)相同的范围(在同一个类中); (2)函数名字相同; (3)参数不同; (4)virtual 关键字可有可无。 覆盖是指派生类函数覆盖基类函数,特征是: (1)不同的范围(分别位于派生类与基类); (2)函数名字相同; (3)参数相同; (4)基类函数必须有virtual关键字。
URL:http://blog.csdn.net/zgbsoap/archive/2005/12/30/566120.aspx
复杂数据类型
1. 数组
(1) 建立数组
¨ array_1=[]
¨ array_2=Array.new
¨ array_3=['3 ','4 ','5 ']
(2) 访问元素
arr=[3,4,5,6,7,8,9]
Ruby以整数作为下标,访问数组元素通过数组下标,数组下标称作数组索引比较好一些。
数组的索引从0开始(arr[0]),一直到数组的长度减去1,如:arr[arr.length-1]或arr[arr.size-1]);
负数表示从数组末尾开始的索引,如:arr[-1]。
用一对数字索引数组,第一个数字表示起始位置,第二数字表示从起始位置开始的元素数目。如:arr[2..4],arr[-3,2]。
arr.first,arr.last分别代表第一个和最后一个元素。
(3) 增删元素
Ruby的数组大小是动态的,你能够随时增加、删除数组元素。
print arr.join(", "),"\n" 意思是:将数组 arr转换成字符串输出,用", "隔开每个
元素,并且换行。
arr=[3,4,5]
print arr #输出:345
print arr.join(", ") #输出:3,4,5
(4) 数组运算
arr1=["abc",1,2,"de"]
arr2=[2,3,4,7,6,5]
|
运算 |
运算表达式 |
输出结果 |
|
加 |
print arr1+arr2 |
abc12de234765 |
|
减 |
print arr2-arr1 |
34765 |
|
乘 |
print arr1*2 |
abc12deabc12de |
|
并 |
print arr1|arr2 |
abc12de34765 |
|
交 |
print arr1&arr2 |
2 |
|
排序 |
print arr2.sort |
234567 |
|
倒置 |
print arr1.reverse |
de21abc |
2. 字符串
(1) 生成一个字符串
字符串是 String 类的对象,一般使用字符串值来创建。
%q用来生成单引号字符串;
%Q用来生成双引号字符串。
%q或者%Q后面跟着的是分隔符,可以是配对的!!;//;<>;();[];{};等等。
字符串文档,从<<和文档结束符的下一行开始,直至一个放置在行首的文档结束符结束。文档结束符自己指定,结束符本身不属于字符串。
注意:文档结束符后面不能跟有空格。
一个数组可以用join方法转换成字符串,join()内的参数也是一个字符串,用来分隔数组的每个元素,例如:arr.join(",")。
(2) 字符串操作
字符串既然是String类的对象,String类的方法你都可以使用在字符串变量上。
(3) 字符串转义
双引号括起来的字符串会有转义,例如:“\n”表示换行。
单引号括起来的字符串不会转义,有一个例外:单引号字符串里的单引号,需要转义。
(4) 字符串内嵌表达式
在双引号字符串中,不仅可以使用转义符,而且可以在#{ }之中放入Ruby表达式,使用字符串时,这些表达式的值被计算并放入字符串。
字符串内嵌表达式,使得你能够更加灵活地组织代码,表现出更强、更多的动态特性。
3. 代码块
可以用大括号{ }将代码组织成块,也可以用 do…end 将代码组织成块。大括号{ }的优先级高于 do…end。代码块,简称块。
yield [ji:ld] 产生,输出……
注意:{}或者do…end之间的是块。

#E8.4-1.rb
def one_block
yield
yield
yield
end
复杂数据类型
1. 数组
(1) 建立数组
¨ array_1=[]
¨ array_2=Array.new
¨ array_3=['3 ','4 ','5 ']
(2) 访问元素
arr=[3,4,5,6,7,8,9]
Ruby以整数作为下标,访问数组元素通过数组下标,数组下标称作数组索引比较好一些。
数组的索引从0开始(arr[0]),一直到数组的长度减去1,如:arr[arr.length-1]或arr[arr.size-1]);
负数表示从数组末尾开始的索引,如:arr[-1]。
用一对数字索引数组,第一个数字表示起始位置,第二数字表示从起始位置开始的元素数目。如:arr[2..4],arr[-3,2]。
arr.first,arr.last分别代表第一个和最后一个元素。
(3) 增删元素
Ruby的数组大小是动态的,你能够随时增加、删除数组元素。
print arr.join(", "),"\n" 意思是:将数组 arr转换成字符串输出,用", "隔开每个
元素,并且换行。
arr=[3,4,5]
print arr #输出:345
print arr.join(", ") #输出:3,4,5
(4) 数组运算
arr1=["abc",1,2,"de"]
arr2=[2,3,4,7,6,5]
|
运算 |
运算表达式 |
输出结果 |
|
加 |
print arr1+arr2 |
abc12de234765 |
|
减 |
print arr2-arr1 |
34765 |
|
乘 |
print arr1*2 |
abc12deabc12de |
|
并 |
print arr1|arr2 |
abc12de34765 |
|
交 |
print arr1&arr2 |
2 |
|
排序 |
print arr2.sort |
234567 |
|
倒置 |
print arr1.reverse |
de21abc |
2. 字符串
(1) 生成一个字符串
字符串是 String 类的对象,一般使用字符串值来创建。
%q用来生成单引号字符串;
%Q用来生成双引号字符串。
%q或者%Q后面跟着的是分隔符,可以是配对的!!;//;<>;();[];{};等等。
字符串文档,从<<和文档结束符的下一行开始,直至一个放置在行首的文档结束符结束。文档结束符自己指定,结束符本身不属于字符串。
注意:文档结束符后面不能跟有空格。
一个数组可以用join方法转换成字符串,join()内的参数也是一个字符串,用来分隔数组的每个元素,例如:arr.join(",")。
(2) 字符串操作
字符串既然是String类的对象,String类的方法你都可以使用在字符串变量上。
(3) 字符串转义
双引号括起来的字符串会有转义,例如:“\n”表示换行。
单引号括起来的字符串不会转义,有一个例外:单引号字符串里的单引号,需要转义。
(4) 字符串内嵌表达式
在双引号字符串中,不仅可以使用转义符,而且可以在#{ }之中放入Ruby表达式,使用字符串时,这些表达式的值被计算并放入字符串。
字符串内嵌表达式,使得你能够更加灵活地组织代码,表现出更强、更多的动态特性。
3. 代码块
可以用大括号{ }将代码组织成块,也可以用 do…end 将代码组织成块。大括号{ }的优先级高于 do…end。代码块,简称块。
yield [ji:ld] 产生,输出……
注意:{}或者do…end之间的是块。

#E8.4-1.rb
def one_block
yield
yield
yield
end
|
one_block { puts "This is a block." } |
把块作为参数,带入到def定义中的yield处即可。
调用一次块要用关键字yield。每一次yield,块就被调用一次。
yield还可以带参数调用块,yield后面是实参,块定义处是形参。
一个块可以接收yield传来的参数,还可以将结果返回给调用它的方法。
如果我们还没有决定在块里写什么代码,或者块里的代码会随着不同的情形而变化,那么就看出代码块的灵活性了。
先写出方法的大致框架,调用方法的时候才告诉方法要作什么。
4. 迭代器
迭代器是一个与代码块有关联的方法。
(1..9).each {|i| print i if i<7}
迭代器 each 是数组类的一个方法;大括号{ }里的代码是代码块。

class Array
def one_by_one
for i in 0...size
yield(self[i] )
end
puts
end
end
|
arr = [1,3,5,7,9]
arr.one_by_one {|k| print k , ", "} # 1, 3, 5, 7, 9,
arr.one_by_one {|h| print h*h, ", "} # 1, 9, 25, 49, 81,
|
5. 闭包
代码块是一段代码,相当于一个匿名方法,被调用它的方法所调用。
如果我们不仅仅想调用代码块,还想把代码块作为参数传递给其它方法,就要使用闭包。
闭包也是一段代码,一个代码块,而且能够共享其它方法的局部变量。
闭包既然是一段代码,也就有自己的状态,属性,作用范围,也就是一个可以通过变量引用的对象,我们称之为过程对象。
一个过程对象用 proc 创建,用 call 方法来调用。
¨ 闭包作为参数传递给其它方法
以下两个程序相同
|
程序1 |
程序2 |
|
def method(pr)
puts pr.call(7)
end
oneProc=proc{|k| k *=3}
method(oneProc) |
oneProc=proc{|k| k *=3}
puts oneProc.call(7) |
|
#过程对象oneProc作为一个参数,传递给method; |
|
¨ 闭包共享其它方法局部变量
|
def method(n)
return proc{|i| n +=i }
end
oneProc=method(3)
puts oneProc.call(9) #12
puts oneProc.call(5) #17 |
方法method返回一个Proc对象,这个对象引用了函数的参数n。即使参数n在闭包被调用时已经不在自己的作用域里,这个闭包还是可以访问参数n,并且和方法method共同拥有变量n。开始时方法method的变量n是3;oneProc.call(9)的时候,oneProc 更新了变量n,把n=12 传回给方法method;oneProc.call(5)的时候,oneProc取出方法 method 的变量 n=12,更新为n=17,传回给方法method的同时,也把n=17作为自己的返回值输出。 |
正则表达式
正则表达式(regular expression)描述了一种字符串匹配的模式,可以用来检查一个串是否含有某种子串;将匹配的子串做替换;或者从某个串中取出符合某个条件的子串;等等。
Ruby 中,可以使用构造器显式地创建一个正则表达式,也可以使用字面值形式 /正则模式/ 来创建一个正则表达式。
|
=~ |
匹配一个正则表达式:用来比较是否符合一个正则表达式,返回模式在字符串中被匹配到的位置,否则返回nil。 |
|
!~ |
不匹配一个正则表达式:用来断言不符合一个正则表达式,返回true,flase。 |
一些字符或字符组合在正则表达式中有特殊的意义。
|
特别字符 |
描述 |
|
( ) |
标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 \( 和 \)。 |
|
[] |
范围描述符 (比如,[a - z] 表示在 a 到 z 范围内的一个字母),要匹配 [,请使用\[。 |
|
{} |
标记限定符表达式。要匹配{,请使用 \{。 |
|
\ |
将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, 'n'匹配字符 'n'。'\n'匹配换行符。序列 '\\'匹配 "\",而 '\('则匹配 "("。 |
|
| |
指明两项之间的一个选择。要匹配 |,请使用 \|。 |
|
. |
匹配除换行符 \n之外的任何单字符。要匹配 .,请使用 \。 |
非打印字符
|
非打印字符 |
描述 |
|
\f |
匹配一个换页符。等价于\x0c。 |
|
\n |
匹配一个换行符。等价于\x0a。 |
|
\r |
匹配一个回车符。等价于\x0d。 |
|
\s |
匹配任何空白字符,包括空格、制表符、换页符等等。等价于[\f\n\r\t\v]。 |
|
\S |
匹配任何非空白字符。等价于[^\f\n\r\t\v]。 |
|
\t |
匹配一个制表符。等价于\x09。 |
|
\w |
匹配包括下划线的任何单词字符。等价于'[A-Za-z0-9_]'字母或数字;相当于[0-9A-Za-z] |
|
\W |
匹配任何非单词字符。等价于'[^A-Za-z0-9_]'非字母,数字 |
|
\d |
匹配一个数字字符。等价于[0-9]。[0-9]数字;相当于[0-9] |
|
\D |
匹配一个非数字字符。等价于[^0-9]。非数字字符 |
|
\b |
退格符(0x08)(仅在范围描述符内部时) |
限定符
限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。
* 和 + 限定符都是贪婪的,因为它们会尽可能多的匹配文字,只有在它们的后面加上一个 ? 就可以实现非贪婪或最小匹配。
|
限定符 |
描述 |
|
* |
前面元素出现0或多次。* 等价于{0,}。
例如,zo*能匹配 "z"以及 "zoo"。
要匹配 * 字符,请使用 \*。 |
|
+ |
前面元素出现1或多次。+ 等价于{1,}。
例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。
要匹配 + 字符,请使用 \+。 |
|
? |
前面元素最多出现1次;相当于 {0,1}。
例如,”do(es)?” 可以匹配 “do” 或 “does” 中的"do" 。
要匹配 ? 字符,请使用 \?。 |
|
{n} |
n 是一个非负整数。匹配确定的 n 次。
例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food"中的两个o。 |
|
{n,} |
n 是一个非负整数。至少匹配n 次。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。
例如,'o{2,}' 不能匹配 "Bob"中的 'o',但能匹配 "foooood"中的所有 o。 |
|
{n,m} |
m 和 n 均为非负整数,其中n <= m。前面元素最少出现n 次,最多出现 m次。
'o{0,1}'等价于 'o?'。请注意在逗号和两个数之间不能有空格。
例如,"o{1,3}"将匹配 "fooooood"中的前三个 o。 |
定位符
用来描述字符串或单词的边界,^和$分别指字符串的开始与结束,\b描述单词的前或后边界,\B表示非单词边界。不能对定位符使用限定符。
|
定位符 |
描述 |
|
^ |
匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。要匹配^字符本身,请使用\^。 |
|
$ |
匹配输入字符串的结尾位置。如果设置了RegExp对象的Multiline属性,则$也匹配'\n'或'\r'。要匹配$字符本身,请使用\$。 |
|
\b |
匹配一个单词边界,也就是指单词和空格间的位置。例如,'er\b'可以匹配"never"中的'er',但不能匹配"verb"中的'er'。 |
|
\B |
匹配非单词边界。'er\B'能匹配"verb"中的'er',但不能匹配"never"中的'er' |
各种操作符的运算优先级
相同优先级的从左到右进行运算,不同优先级的运算先高后低。各种操作符的优先级从高到低如下:
|
优先级 |
操作符 |
描述 |
|
高 |
\ |
转义符 |
|
|
(),[] |
圆括号和方括号 |
|
|
*,+,?,{n},{n,},{n,m} |
限定符 |
|
|
^,$, |
位置和顺序 |
|
低 |
| |
“或”操作 |
元编程
元编程:编写能够生成程序的程序。
听起来很有趣!
Ruby语言强大的动态特征,赋予了我们灵活地进行元编程的能力。
| |