Ruby读书笔记 Terry七月Ruby读书笔记(比较详细)
人气:0Page 3 概述
¨ 解释执行:Python,Ruby,交互性很好; ¨ 编译执行:Pascal, C,速度较快。 n 本地执行,如C,C++; n 虚拟机执行,如Java, C#。
¨ 动态语言,如JavaScript,Ruby; ¨ 静态语言,如C++,Java。 |
语言 ¨ 语法 关键字 ¨ 语义 ¨ 语用
|
松本行弘(Matz) 1993
Ruby [`ru:bi](红宝石)
Ruby 吸取了 perl 的正则表达式, python 的简单性可读性,smalltalk 的纯面向对象语法和单继承,LISP 的无穷嵌套的语法,Java的线程… |
Page 14 上手
XP
Cmd
C:\Documents and Settings\Administrator>ruby -v ruby 1.8.6 (2007-09-24 patchlevel 111) [i386-mswin32] |
第一种方式:ruby命令
C:\Documents and Settings\Administrator>ruby -e 'print "hello world"' hello world |
ruby 运行ruby;
-e 执行后面的ruby脚本程序;
print 打印显示;
hello,world 要输出的内容。
第二种方式:irb交互执行
C:\Documents and Settings\Administrator>irb irb(main):001:0> print "hello world" hello world=> nil irb(main):002:0> exit |
exit: 退出irb交互式环境
第三种方式:创建ruby脚本
C:\Documents and Settings\Administrator>copy con helloworld.rb print "hello world" print "3*7=",3*7 ^Z 已复制 1 个文件。
C:\Documents and Settings\Administrator>helloworld.rb hello world3*7=21 |
Page 17 IDE
¨ Eclipse + RDT(Ruby Development Tools)
¨ FreeRIDE+SciTE
1.在SciTE编辑Ruby代码;
2.保存为 .rb文件;
3.菜单栏,工具->运行或者F5;
Page 18 语法
1. 注释
单行注释 # 多行注释 =begin =end
Ruby的内嵌文档(Rdoc)注释,用ri命令从源文件中产生文档。
Rdoc内嵌在ruby代码之中的,可以转换为html文档。
ri命令,用来查看函数说明、类说明。函数说明、类说明应该放置在=begin和=end之中。注意:“=begin”一定要写在行首,也就是说,这一行前六个字符是“=begin”,之前不允许有空格。
2. 分行
用分号;表示语句结束。
一行多个语句,用分号隔开,最后一个语句,分号可以省略,换行符表示一行结束。
语句太长,可以在行末用符号 \ ,表示续行。
3. 分隔符
名称 |
符号 |
用途 |
分号 |
; |
用来分隔一行中的多个语句 |
圆括号 |
( ) |
提高优先级;定义方法时容纳参数列表 |
空格 |
|
分隔字符;在可省略()的地方,代替() |
逗号 |
, |
隔开多个参数 |
点 |
. |
将对象与它的方法隔开 |
紧连的两个冒号 |
:: |
域作用符,将模块(类)与它的常量隔开 |
4. 关键字
Ruby中的关键字如下:
模块定义: |
module |
异常处理: |
rescue,ensure |
类定义: |
class |
对象引用: |
super,self |
方法定义: |
def,undef |
块的起始: |
begin/end |
检查类型: |
defined? |
嵌入模块: |
BEGIN,END |
逻辑值和空值: |
true,false,nil |
文件相关: |
__FILE__,__LINE__ |
逻辑判断: |
not,and,or |
方法返回: |
return |
条件语句: |
if,then,else,elsif,case,when,unless |
别名: |
alias |
循环语句: |
for,in,while,until,next,break,do,redo,retry,yield |
BEGIN模块相当于C语言中的宏,END模块用来作一些收尾工作。有了
require,include,应该取消BEGIN和END的语法定义。
5. 运算符
6. 标识名:
常量,变量,方法,类和模块;
大小写敏感;
标识名第一个字符表明这个名字的用法。
局部变量、方法参数和方法名称 |
以小写字母开头或者下划线开头; |
类名、模块名和常量 |
用大写字母开头。 |
全局变量 |
以美元符为前缀$; |
实例变量 |
以@开头; |
类变量 |
用@@开头; |
词首字母后面可以是字母、数字和下划线的任意组合;
@后面不可以直接跟数字。
Ruby程序代码用7位ACSII码来表示,通过语言扩展来支持 EUC,SJIS或UTF-8等8位编码系统。Ruby2.0版本将支持16位的Unicode编码。
7. 类库
可以直接使用:
i) 关键字;
ii) require或者include包含的类库中的类、方法;
iii)从父类中继承得到的方法。
puts |
每个参数后自动换行 |
|
不自动换行,换行用”\n” |
printf |
按格式输出,不自动换行 |
gets |
从键盘或文件中读入数据 |
C:\Documents and Settings\Administrator>ruby -e 'puts "hello","world"' hello world |
C:\Documents and Settings\Administrator>ruby -e 'printf "number:%f",3.2' number:3.200000 |
注意:printf的参数之间用逗号隔开。
C:\Documents and Settings\Administrator>ruby -e ' printf "number:%4.3f,string %s ",1.5,"hello world"' number:1.500,string hello world |
8. 数据类型
类型 |
分类 |
说明及示例 |
数字 |
整数型 |
0 八进制, 0x 十六进制,0b 二进制 |
浮点型 | ||
字符串 |
单引号 ‘' |
|
双引号 “” |
| |
数组 |
从0开始,每个元素可以不同类型 |
例 [2.4,"thank you",[a,b,c],78] |
区间 |
1..5 |
1,2,3,4,5 |
1…5 |
1,2,3,4 | |
正则表达式 |
|
|
9. 赋值运算
交换变量a,b的值:
a,b=b,a;
注意括号:
a = (b = 1 + 2) + 3 #a=6 ,b=3
逗号,从左往右运算,分别赋给a,b,c
x = 0 #x=0
a,b,c = x, (x+1), (x+2) #a=0 ,b=1,c=2
10. 条件运算
== |
等于 |
!= |
不等于 |
eql? |
比较两个对象的值、类型是否相等 |
equal? |
比较两个对象在内存中地址是否相同 |
< |
小于 |
> |
大于 |
<= |
小于等于 |
>= |
大于等于 |
=~ |
匹配正则表达式 |
!~ |
断言不匹配正则表达式 |
=== |
右边的对象在左边区间内 puts(0..9)===3.14 #true puts('a'..'f')==='c' # true |
<=> |
比较两个对象大小,大于1,等于0,小于-1. |
Ruby里,nil和false为假,其它都为真。
11. 判断语句
|
if then end |
( ) if | |
|
if elsif else end |
|
unless
|
if not
| |
|
case when when else end |
12. 循环语句
单行while |
(语句1;语句2;语句…)while条件 |
多行while |
while 条件 语句,语句 end |
until直到 |
until 跳出循环条件 语句 until 条件=while not (条件) |
for…in循环 |
for 变量 in 对象 语句1; 语句2 ; 语句… end |
break,next&redo&retry
在循环体内,如果遇到:
break |
跳出当层循环; |
next |
忽略本次循环的剩余部分,开始下一次的循环; |
redo |
重新开始循环,还是从这一次开始; |
retry |
重头开始这个循环体。 |
times , upto , downto , each ,step
语句 |
运行结果 |
3.times { print "Hi!" } |
#Hi!Hi!Hi! |
1.upto(9) {|i| print i if i<7 } |
#123456 |
9.downto(1){|i| print i if i<7 } |
#654321 |
(1..9).each {|i| print i if i<7} |
#123456 |
0.step(11,3) {|i| print i } |
#0369 |
C:\Documents and Settings\Administrator>irb irb(main):001:0> 1.upto(9){|i| print i if i<7} 123456=> 1 irb(main):002:0> 9.downto(1){|i|print i if i<7} 654321=> 9 irb(main):003:0> (1..9).each {|i| print i if i<7} 123456=> 1..9 irb(main):004:0> 0.step(11,3) {|i| print i} 0369=> 0 |
13. 异常
Java
try…catch…finally…throw
Ruby
begin/end…rescue…ensure…raise
retry可以用在rescue中。可以只用rescue或ensure,两者都使用时,rescue必须在ensure前。
14. 线程
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) |
一个类中,方法名相同、参数列表不同的几个方法,调用时根据不同的参数调用不同的方法。方法重载与返回类型无关。 |
子类有一个方法,方法名、参数列表、返回类型与父类的某个方法完全一致。调用时会调用子类的方法,而屏蔽掉父类的同名方法。需要注意的是,子类覆写的方法,其可访问性一定要强于或等同于,父类被覆写的同名方法。 |
重载不仅仅是发生在子类和父类之间,大多数时候,发生在同一个类中。 |
覆写发生在子类和父类之间,当然也可以是子类和父类的父类之间。 |
成员函数被重载的特征:
URL:http://blog.csdn.net/zgbsoap/archive/2005/12/30/566120.aspx |
Ruby语言,只有重写,没有其它语言具有的严格意义上的重载。
分析:重载,参数不同,参数列表对应位置参数类型不同。而ruby支持缺省参数、可变参数,因此,参数不同不会发生重载。所以,ruby没有严格意义上的重载。
父类和子类中的同名方法,子类重写父类。子类对象执行子类中的方法。
在同一个类中写两个同名方法,总是执行写在后面的方法。
ruby支持缺省参数,和C++相同。
在 Ruby中,我们说覆写是指重写,我们说重载也是指重写。
Ruby是动态语言,可以随时改变类的属性、方法,所以覆写和重载的重要性就降低了。
2. 增强父类方法
如果我们只是想增强父类的方法,而不是完全地替代它,就可以用关键字super指明。
在子类定义和父类同名的函数时,加入super关键字。
3. 变量
(1) 稳定性
constant :Unchanging in nature, value, or extent; invariable.稳定的,不变的。
常变量(常量)。常量名用大写字母开头。
变量:作用域,可以改变值。
Ruby中的常量,可以在类和模块中定义,不能在方法中定义。
如果在外部访问类或模块中的常量,要使用域作用符::。
(2) 作用域
全局变量——作用域程序任何位置,全局变量用$开头。
局部变量——作用域程序某个单元,用小写字母开头。
全局变量和实例变量如果没有初始化,其默认值为 nil 。
Ruby中的局部变量,可以是存在于类、方法、模块、循环、过程对象中。
(3) 共享性
实例变量——只能被某个实例对象使用的变量,变量名用@开头;
类变量——能被某个类的所有实例对象共享的变量,变量名用@@开头。
类变量在使用前必须要初始化。
(4) 类方法
类方法——不依赖于任何特定实例对象的方法。
类方法与实例方法的定义方式不同,定义类方法要在方法名前加上类名和点号“.”。
调用一个类方法,与定义类方法一样,要在方法名前加上类名和点号“.”。
类方法提供了一个途径,在类的外部访问类变量,无需通过类的实例方法。
对象是类的实例,说明类是对于对象的一个抽象,那么,如果把类继续抽象,把类看做实例,那它会是谁的实例? ——元类! 元数据,描述数据的数据。 |
疑问:类方法可否操作实例变量,而不仅仅是类变量?
4. 单例方法
Ruby如何反映相同的类,不同实例,之间的不同的行为特征?
(1)单例方法:在Ruby里,可以给具体的实例对象添加实例方法,这个方法只属于这个实例对象,我们把这样的方法称之为单例方法,单例方法也叫作单件方法。
(2)定义单例方法:
首先要生成一个实例对象,
其次,要在方法名前加上对象名和一个点号“.”。
(3)实例方法,属于类的每个实例对象。单例方法只出现在单个实例对象中。用单例方法可以极大地丰富多态性在Ruby中的表现力。
Ruby对于类和对象的扩展,就像数学中的多维,原来只是类和对象两个层次,这里,看成任意的层次,只不过,类和对象是其中的两个不同的层次。所以,有了类的类——类的进一步抽象,有了单例方法,在对象这个层面上设计方法。
5. 访问控制
在Ruby里,要读取或是改变对象的属性,唯一的途径是调用对象的方法。
控制了对方法的访问,也就控制了对对象属性的访问。
控制对方法的访问,有三种方式:
访问控制 |
意义 |
public |
可以被任何实例对象调用,不存在访问控制; |
protected |
可以被定义它的类和其子类访问, 可以在类中或子类中指定给实例对象; |
private |
可以被定义它的类和其子类访问, 不能被实例对象调用。 |
方法默认都是公有的(initialize方法除外,它永远是私有的)。
public方法 |
可以被定义它的类和其子类访问, 可以被类和子类的实例对象调用; |
protected方法 |
可以被定义它的类和其子类访问, 不能被类和子类的实例对象直接调用, 但是可以在类和子类中指定给实例对象; |
private方法 |
可以被定义它的类和其子类访问, 私有方法不能指定对象。 | 它的类和其子类访问,
Ruby语言的访问控制是动态的,是在程序运行时刻确立的。
你可以根据自己的需要,在程序的不同位置,改变某个方法的访问控制级别,让你的程序更加富于变化。
page 79
模块化程序设计
1. 模块
在程序中,相关的、不相关的代码的组合,叫作模块。一般情况下,我们总是把功能相关的代码放在一个模块里。
把功能相关的程序代码放在一个模块里,体现了模块的第一个作用:可以被其它程序代码重复使用。
Ruby标准包里的 Math 模块提供了许多方法
模块名.方法名(参数) |
Math 模块还提供了两个常量,圆周率π 和自然对数底 e,
模块名::常量名 |
定义模块用 module...end 。模块与类非常相似,但是:
A) 模块不可以有实例对象;
B) 模块不可以有子类。
2. 命名空间
模块的第二个作用:提供了一个命名空间(namespace),防止命名冲突。
include 模块名
定义一个模块方法,在方法名前加上模块名和一个点号“.”。
调用一个模块方法,与定义模块方法一样,要在方法名前加上模块名和一个点号“.”。模块方法提供了一个途径,在模块的外部访问模块内部方法,无须 include模块。定义模块常量不需要如此。
3. 糅和(Mix-in)与多重继承
柔和/混合插入/Mix-in
Ruby是单继承,通过Mix-in模块,实现多重继承的优点。
模块的第三个作用:实现了类似多重继承的功能。
通过“<父类名”,一个子类可以得到父类的属性和方法;
通过“include模块名”,一个子类可以得到某个模块的常量和方法。
类不能被include。
include方法为一个类的所有对象包含某个模块;
extend方法为一个类的某个对象包含某个模块(联系单例方法)。
4. require和load
require方法包含另一个文件,另一个文件名需要是一个字符串。
load方法与require方法相对应,也用来包含另一个文件。
require包含文件,只加载一次,遇到同一文件时自动忽略;不同路径下的同名文件会多次加载。
load包含文件,加载多次,即使是相同路径下同一文件。
总结一下:
require,load |
包含文件 |
include,extend |
包含模块 |
require |
加载文件一次 |
加载文件时可以不加后缀名 |
一般情况下用于加载库文件 |
load |
加载文件多次 |
加载文件时必须加后缀名 |
用于加载配置文件 |
利用load多次加载文件的特性,可以用来实现程序的无缝升级和系统的热部署。程序功能改变了,你只需要重新load一次,其它代码与它再次交互的时候,这个程序实际上已经不是原来的程序了。
page 88
复杂数据类型
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语言强大的动态特征,赋予了我们灵活地进行元编程的能力。
Ruby语言的访问控制是动态的,是在程序运行时刻确立的。
你可以根据自己的需要,在程序的不同位置,改变某个方法的访问控制级别,让你的程序更加富于变化。
page 79
模块化程序设计
1. 模块
在程序中,相关的、不相关的代码的组合,叫作模块。一般情况下,我们总是把功能相关的代码放在一个模块里。
把功能相关的程序代码放在一个模块里,体现了模块的第一个作用:可以被其它程序代码重复使用。
Ruby标准包里的 Math 模块提供了许多方法
模块名.方法名(参数) |
Math 模块还提供了两个常量,圆周率π 和自然对数底 e,
模块名::常量名 |
定义模块用 module...end 。模块与类非常相似,但是:
A) 模块不可以有实例对象;
B) 模块不可以有子类。
2. 命名空间
模块的第二个作用:提供了一个命名空间(namespace),防止命名冲突。
include 模块名
定义一个模块方法,在方法名前加上模块名和一个点号“.”。
调用一个模块方法,与定义模块方法一样,要在方法名前加上模块名和一个点号“.”。模块方法提供了一个途径,在模块的外部访问模块内部方法,无须 include模块。定义模块常量不需要如此。
3. 糅和(Mix-in)与多重继承
柔和/混合插入/Mix-in
Ruby是单继承,通过Mix-in模块,实现多重继承的优点。
模块的第三个作用:实现了类似多重继承的功能。
通过“<父类名”,一个子类可以得到父类的属性和方法;
通过“include模块名”,一个子类可以得到某个模块的常量和方法。
类不能被include。
include方法为一个类的所有对象包含某个模块;
extend方法为一个类的某个对象包含某个模块(联系单例方法)。
4. require和load
require方法包含另一个文件,另一个文件名需要是一个字符串。
load方法与require方法相对应,也用来包含另一个文件。
require包含文件,只加载一次,遇到同一文件时自动忽略;不同路径下的同名文件会多次加载。
load包含文件,加载多次,即使是相同路径下同一文件。
总结一下:
require,load |
包含文件 |
include,extend |
包含模块 |
require |
加载文件一次 |
加载文件时可以不加后缀名 |
一般情况下用于加载库文件 |
load |
加载文件多次 |
加载文件时必须加后缀名 |
用于加载配置文件 |
利用load多次加载文件的特性,可以用来实现程序的无缝升级和系统的热部署。程序功能改变了,你只需要重新load一次,其它代码与它再次交互的时候,这个程序实际上已经不是原来的程序了。
page 88
复杂数据类型
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语言强大的动态特征,赋予了我们灵活地进行元编程的能力。
加载全部内容