ruby学习笔记
数组
建立数组
要制作新的数组,可以使用逗号分割元素,并以"[]"框住所有数据。
name = ['a', 'b', 'c', 'd']
当数组对象的内容尚未确定时,可以只写"[]",这表示空的数组对象。
name = []
从数组中取得对象
要获取数组特定位置的对象,可以写:数组名[索引]
name[0]
数组的索引是从0开始的。
将对象存进数组
数组名[索引]=想要存放的对象
name[0]='d'
数组的内容
数组里可以存放任何对象。
数组的大小
可以使用size方法:array.size。
p name.size
逐项处理数组内容
数组.each {|变量|
想要反复执行的动作
}
name.each{|n|
print n, "\n"
}
HASH
建立hash
建立hash时使用"{}",使用"=>"符号定义用来取出对象的键与该键取出的值。
font_table = {"normal" => "+0", "small" => "-1", "big" => "+1"}
从hash获取对象
hash名[键]
要将对象存进hash则写成:hash[键]=想要存放的对象
例如:
p font_table["small"]
p font_table["normal"]
p font_table["big"]
font_table["verybig"] = "+2"
p font_table["verybig"]
注意:hash没有固定的顺序,并不能按存方式的顺序取出数据。
逐项处理hash内容
hash.each{|键的变量, 值的变量|
想要反复执行的动作
}
正则表达式
建立正则表达式对象的语法:/样式/
拿正则表达式匹配字符串时,写成:/样式/=~想要匹配的字符串
当匹配成功时会反悔匹配成功的位置。文字的位置是从0开始的。匹配失败时则会反悔nil。
正则表达式右侧的"/"后面如果加上一个"i",则匹配是就不区分大小写了。
例如:
p /Ruby/ =~ "ruby"
p /Ruby/i =~ "ruby"
从命令行输入的数据
要取得命令行所传入的数据,可以使用"ARGV"这个数组对象。
在ARGV数组中,以字符串形式存放着在命令行对指令脚本传入的实参。
利用命令行给指令脚本指定参数的场合,在参数之间需要用空格隔开进行输入。
将字符串转换成数值,必须使用to_i方法。
读取文件
读入文件内的文本数据并显示
read_text.rb
filename = ARGV[0]
file = open(filename)
text = file.read
print text
file.close
从文件逐行读入数据并显示出来
gets_text.rb
filename = ARGV[0]
file = open(filename)
while text = file.gets do
print text
end
file.close
只显示文件里符合特定样式的几行
simple_grep.rb
pattern = Regexp.new(ARGV[0])
filename = ARGV[1]
file = open(filename)
while text = file.gets do
if pattern =~ text
print text
end
end
file.close
变量
一个变量属于哪一种变量则是由变量名称决定的。
1、局部变量:变量名以小写字母或"_"起始的变量。
2、全局变量:变量名以"$"起始的变量。
3、实例变量:变量名以"@"起始的变量。
4、类变量:变量名以"@@"起始的变量。
5、虚拟变量:"true"、"false"、"self"等特定名称的变量。
常数
常数名称必须以大写字母开始。
对象的同一性
所有的对象都被赋予身份标识。对象的身份标识(ID)可以通过object_id方法获得。
"=="和"eql?"判断是否同值。在同样的使用场合下,两者的动作情况是一样的。
在施行"=="时,需要进行必要的变换,eql?可以不需要变换就能进行比较。
在一般的程序中,使用"=="的场合比较普遍。hash内部的键与给定的键进行比较等情况,在需要严格地进行比较的场合下,使用eql?来比较。
条件判断
条件判断语句大致有下面3种:
1、if语句
语法:
if 条件1 then
语句1
elsif 条件2 then
语句2
elsif 条件3 then
语句3
else
语句4
end
then可以省略。
2、unless语句
unless语句则是与if完全相反的语句。
语法:
unless 条件 then
语句1
else
语句2
end
then可以省略。
等价如下if语句:
if 条件
语句2
else
语句1
end
3、case语句
语法:
case 想要比较的对象
when 值1 then
语句1
when 值2 then
语句2
when 值3 then
语句3
else
语句4
end
then可以省略。
if与unless也可以写在想要执行的语句后面。
print "a比b大" if a > b
等价下面语句
if a > b
print "a比b大"
end
"==="与case表达
在case表达式中,判断与when后指定的值是否一致可以使用"==="运算符来表示。
"==="的左边如果是数值或字符串则意义与"=="是一样的。在正则表达式的场合下
相当于"=~",即匹配判定。在类的场合下,判断"==="右边的对象是否是类的实例。
与其说"==="是进行其左右两边"值"的比较,还不如把它看作判断是否等同。
case value
when A if A === value
处理1 处理1
when B elseif B === value
处理2 处理2
else else
处理3 处理3
end end
循环
1、times方法
语法:
想要重复的次数.times do
想要重复执行的动作
end
想要重复的次数.times {
想要重复执行的动作
}
使用times方法时,在语句块中也可以知道现在是循环到第几次了。
想要重复的次数.times { |变量|
想要重复执行的动作
}
例子:
from = 10
to = 20
sum = 0
(to - from + 1).times{ |i|
sum = sum + (i + from)
}
print sum, "\n"
2、for语句
for 变量 in 开始的数值 .. 结束的数值 do
想要重复执行的动作
end
for 变量 in 对象 do
想要重复执行的动作
end
do可以省略。
".."与"..."只是一种用来建立范围对象的记号而已。
for语句与times方法不同,可以自由更改初始值与终止值。
例一:
from = 10
to = 20
sum = 0
for i in from .. to
sum = sum + i
end
print sum, "\n"
例二:for_names.rb
names = ["awk", "Perl", "Python", "Ruby"]
for name in names
print name, "\n"
end
3、while语句
语法:
while 条件 do
想要重复执行的动作
end
do可以省略。
4、until语句
until 条件 do
想要重复执行的动作
end
do可以省略。
5、each变量
对象.each do |变量|
想要重复执行的动作
end
对象.each { |变量|
想要重复执行的动作
}
do可以省略。
6、loop方法
没有结束条件,只是不断进行循环处理的方法。
例子:
loop {
print "Ruby"
}
用来控制循环的命令
命令 用途
break 停止动作,马上跳出循环
next 直接跳到下一次循环
redo 以相同的条件重新进行这一次循环
例子:
break_next_redo.rb
print "break的示例:\n"
i = 0
["Perl", "Python", "Ruby", "Scheme"].each{ |lang|
i += 1
if i == 3
break
end
p [i, lang]
}
print "\nnext的示例:\n"
i = 0
["Perl", "Python", "Ruby", "Scheme"].each{ |lang|
i += 1
if i == 3
next
end
p [i, lang]
}
print "\nredo的示例:\n"
i = 0
["Perl", "Python", "Ruby", "Scheme"].each{ |lang|
i += 1
if i == 3
redo
end
p [i, lang]
}
方法
调用方法
语法:
对象.方法名(实参1, 实参2, ..., 实参n)
方法的分类
a、实例方法:当对象(实例)存在时,把该对象作为接收者的方法,叫做实例方法。
b、类方法:当接收者不是对象而是类时,这个方法称为类方法。
调用类方法时,"."也可以写成"::"
c、函数性的方法:没有接收者的方法,称为函数性的方法。
定义方法
语法:
def 方法名(参数1, 参数2, ...)
想要执行的动作
end
可以为参数指定预设值。预设值是没指定实参时使用的值,写成"参数名 = 值"的形式。
当方法不止一个参数时,预设值必须从右端的参数开始指定。
例子:
hello_with_default.rb
def hello(name="Ruby")
print("Hello, ", name, ".\n")
end
hello()
hello("Newbie")
在方法中使用return语句可以指定返回值:return 值
return语句可以省略,这时方法中最后一个语句的计算值会是返回值。
def area(x, y, z)
xy = x * y
yz = y * z
zx = z * x
(xy + yz + zx) * 2
end
p area(2, 3, 4)
p area(10, 20, 30)
方法的返回值不一定是最后一行,如下:
def max(a, b)
if a > b
a
else
b
end
end
类与模块
类与实例
要建立新的对象,必须使用每个类的new方法。
想知道对象属于哪个类,可以使用class方法。
要判断一个对象是否是某个类的实例,可以使用instance_of?方法。
Ruby语言所有的类都是Object类的子类(Object是唯一没有父类的类)。
is_a?方法可以用来检查实例是否属于某个类,还可以检查有继承关系的父类。
instance_of?方法与is_a?方法都定义在Object类里,所以所有对象都可以使用这些方法。
自己定义类
class语句
语法:
class 类名
类定义
end
类名一定要以大写字母开始。
initialize方法
调用new方法建立对象时,这个initialize方法会被调用;同时,传递给new的所有实参都会传递给initialize方法。
定义访问方法
定义 意义
attr_reader:name 只定义用来读取的方法(定义name方法)
attr_writer:name 只定义用来写入的方法(定义name=方法)
attr_accessor:name 定义用来读取与写入的方法(定义上述两个方法)
用来设定实例变量的方法称为setter,而用来读取实例变量的方法称为getter。这两种方法放在一起,合称为访问方法(accessors)。
类方法
类方法有3种定义的方式:
a、写成"def 类名.方法名~end"
class HelloWorld
def HelloWorld.hello(name)
print name, " said Hello."
end
end
HelloWorld.hello("John")
b、写成"class<< 类名~def 方法名~end end"
class HelloWorld
.
.
end
class << HelloWorld
def hello(name)
print name, " said Hello."
end
end
HelloWorld.hello("John")
c、写成"class 类名~def self.方法名~end end"
class HelloWorld
def self.hello(name)
print name, " said Hello."
end
end
HelloWorld.hello("John")
方法的表示法:类.方法或类::方法
常数
类内的常数可以使用"::"连接类与常数名称,从类外部访问。
class HelloWorld
Version = "1.0"
end
p HelloWorld::Version
类变量
以"@@"开始的变量称为类变量。类变量是该类所有实例共用的变量。
例子:
hello_count.rb
class HelloCount
@@count = 0
def HelloCount.count
@@count
end
def initialize(myname="Ruby")
@name = myname
end
def hello
@@count += 1
print "Hello, world. I am ", @name, ".\n"
end
end
bob = HelloCount.new("Bob")
alice = HelloCount.new("Alice")
ruby = HelloCount.new
p HelloCount.count
bob.hello
alice.hello
ruby.hello
p HelloCount.count
扩充类
还可以在已经定义的类中新增方法。
例子:
ext_string.rb
class String
def count_word
ary = self.split(/\s+/)
return ary.size
end
end
str = "Just Another Ruby Newbie"
p str.count_word
使用继承
语法:
class 类名 < 父类名
类定义
end
限制方法的调用
Ruby提供了3中限制层级:
a、public---将方法公开为外部可以使用的实例方法。
b、private---将方法限制为只有内部可以使用(不允许接在接收者后面调用)
c、protected---将方法限制为只有内部可以使用。
例子:
acc_text.rb
class AccTest
def pub
puts "pub is a public method."
end
public :pub
def priv
puts "priv is a private method."
end
private :priv
end
acc = AccTest.new
acc.pub
acc.priv
或
class AccTest
public
def pub
puts "pub is a public method."
end
private
def priv
puts "priv is a private method."
end
end
acc = AccTest.new
acc.pub
acc.priv
什么是面向对象
面向对象的特征:封装、多态
什么是模块
模块的用法
1、提供命名空间
命名空间(namespace)是为了让方法、常数、类名称不互相冲突而设计的机制。
以模块形式提供的方法必须使用"模块名.方法名"的方式调用。以这种形式调用的方法又称为模块函数。
当模块内部定义的方法及常数的名称没有与现在命名空间冲突时,省略模块名称会比较方便。使用include可以将模块所拥有的方法名称与常数名称读入现在的命名空间里。
2、以Mix-in方式提供功能
将模块混进类里,称为"Mix-in"。在类的定义内使用include,可以将模块里所定义的方法和常数纳入类定义里。
例如:
module MyModule
#想要共同提供的方法等
end
class MyClass1
include MyModule
#MyClass1所特有的方法
end
class MyClass2
include MyModule
#MyClass2所特有的方法
end
自己定义模块
语法:
module 模块名
模块定义
end
常数
与类一样,模块内部定义的常数可以直接连接在模块名称后面引用。
定义方法
与类一样,module内部也可以定义方法。只是方法在定义以后,只能在模块内部调用,不能使用"模块名.方法名"的调用方式。要将方法对模块外部公开,必须使用module_function。module_function的实参是想要公开的方法名称的符号。
错误处理与例外
错误消息格式:
文件名:行号:in '方法名':错误消息(例外类)
from 调用方法的位置
.
.
例外处理的写法
begin
有可能发生例外的处理动作
rescue
例外发生时的处理措施
end
Ruby将例外也视为对象。在rescue后面指定变量名称,可得例外对象。
begin
有可能发生例外的处理动作
rescue => 用来存放例外对象的变量
例外发生时的处理措施
end
例外发生时会自动设置的变量
变量 意义
$! 最后发生的例外(例外对象)
$@ 最后例外所发生的位置相关信息
例外对象的方法
方法名 意义
class 例外类别
message 例外的消息
backtrace 例外的发生位置信息($@等同于$!.backtrace)
善后处理
如果有些动作无论是否发生例外都想要确实完成,则写在ensure语句块里。
begin
有可能发生例外的处理动作
rescue => 用来存放例外对象的变量
例外发生时的处理措施
ensure
无论例外发生与否都坚持要执行的动作
end
重新执行
在resure语句块里可以使用retry语句重新执行begin语句块的动作。
例如:
file = ARGV[0]
begin
io = open(file)
rescue
sleep 10
retry
end
rescue修饰符
格式:运算式1 rescue 运算式2
例子:n = Integer(val) rescue 0
例外处理语法的补充
def foo def foo
begin 方法内容
方法内容 rescue => ex
rescue => ex 例外处理
例外处理 ensure
ensure 后置处理
后置处理 end
end
end
一样的,在类定义里也可以写rescue与ensure语句块。但是在类定义发生例外时,例外发生位置之后的所有方法定义会被跳过,所以一般的程序不会这样用。
class Foo class Foo
begin 类定义内容
类定义内容 rescue => ex
rescue => ex 例外处理
例外处理 ensure
ensure 后置处理
后置处理 end
end
end
指定要捕捉的例外
当不止一种例外可能发生,而想要分开处理这些例外时,可以使用多个rescue语句块来处理。
begin
有可能发生例外的处理动作
rescue Exception1, Exception2 => 变量
Exception1或Exception2例外的处理措施
rescue Exception3 => 变量
Exception3例外的处理措施
rescue => 变量
其他例外的处理措施
end
指定类名称,就可以捕捉预想的例外。
file1 = ARGV[0]
file2 = ARGV[0]
io = nil
begin
io = open(file1)
rescue Errno::ENOENT, Errno::EACCES
io = open(file2)
end
Errno::ENOENT与Errno::EACCES是文件不存在时与没有文件打开权限时会发生的例外。
例外类
rescue语句块所指定的例外种类,是例外的类名称。
在rescue语句块没有指定例外类时,会捕捉StandardError与其子类。
要定义各种例外类时,一般会先继承StandardError类,接着再往下继承各种例外类。
class MyError < StandardError; end
class MyError1 < MyError; end
class MyError2 < MyError; end
class MyError3 < MyError; end
这样定义的话,只要像下面这样写,就可以捕捉到MyError1、MyError2与MyError3。
begin
.
.
rescue MyError
.
.
end
引发例外
想要让例外发生时则使用raise方法。这个方法用在想要在某些条件下主动产生出新的例外或者再次引发之前捕捉到的例外,将例外交给调用来源时。
raise有下面4种形式:
1、raise 消息
引发RuntimeError例外。指定的字符串会被当做新的例外对象的消息。
2、raise 例外类
引发指定的例外。
3、raise 例外类,消息
引发指定的例外,并且指定的字符串会当做新的例外对象的消息。
4、raise
写在rescue语句块外部则引发RuntimeError例外;写在rescue语句块内则可以自动再次引发最后发生的例外($!)。
catch与throw
在catch的实参中指定符号,连同语句块一起调用,那么在这个语句块内若以相同符号作为实参调用throw方法则语句块会在这里中断执行。
例子:
catch(:exit) {
loop {
loop {
:
if val != 0 #在某个条件成立时
throw :exit, val #抛出:exit跳离catch
end
}
}
}
指定throw的第2个实参时,这会称为catch的返回值。
数值类