很多人在学习心得编程语言时都会遇到绊脚石,但是Ruby给我们带来了两个惊喜。
Ruby中的字符串是可变对象,这对于Java程序员来说是个好事。[]和=操作符允许你修改或者在字符串中增加和删除字符。<<操作符允许你向一个字符串附加另一个字符串,而且字符串对象本身还定义了一系列的方法来操作其自身的内容。因为字符串是可变的所以实际上它是一段程序而不再是一个唯一的对象,当然你可以调用freeze方法来防止对字符串的修改。
Ruby 中的条件判断语句和循环语句(比如if,while)通常需要判断一个条件的值是true还是false.但是在Ruby中这不是必须的,如果一个对象的值为nil,那么它会被当作false 对待,其他任何部位nil的值都是ture。这有点像C语言中把0当作false,或者Javascript中把""当作false。
Showing posts with label Ruby介绍. Show all posts
Showing posts with label Ruby介绍. Show all posts
Friday, October 10, 2008
Thursday, October 09, 2008
(8)Ruby中的类和方法
类就是一些列方法的集合,这些方法可以操作类的状态,类中的变量维护了类的状态。由@开头的变量是某个具体类的专有变量,下面的例子演示了如何定义类、方法以及如何使用它们:
下面演示了如何使用这个类:
实际上我们这个类中最关键的部分是迭代方法,实际上如果你只是对迭代方法感兴趣你不必一定要定义一个类,你可以直接在脚本文件中写这个方法,这样它就是一个全局的方法,当然定义太多的全局方法不是个好主意,我们可以把这个方法放到一个模块中module:
通过这种方式定义的方法,我们可以这样调用:
我们使用了另一种方式来定义迭代方法,但是看起来方法名太长了,而且调用它的时候也比较麻烦,有没有简便的办法呢?实际上我们只是想在Range类型中迭代,并且定义一个方法可以自定义步长,Ruby中一个神奇的特性是它的类是“开放”的,甚至是它的核心类,你可以在任何时候向其中新增方法,所以让我们来给Range定义一个新的方法:
Created with colorer-take5 library. Type 'ruby'
#
# 这个类呈现了一系列数字操作,使用了3个参数,from ,to ,by 。变量x遵从如下规则
#
# from <= x <= to
# x = from + n*by, n是一个整数
#
class Sequence
# 这个是enumerable类; 它定义了一个each迭代.
include Enumerable # 在这个模块中导入Enumeable的方法
# initialize是一个特殊方法,它在类被创建的时候自动调用
def initialize(from, to, by)
# 把参数值存储在局部变量中以便后边使用
@from, @to, @by = from, to, by # 注意这里使用了平行赋值和@前缀
end
# 定义Enumerable模块所需的迭代方法
def each
x = @from # 迭代开始,将x值赋值为@from
while x <= @to # 开始循环直到x小于等于@to的值
yield x # 将x之传递给相应迭代的 块
x += @by # 用@by值递增x的值
end
end
# 定义lenght方法返回数组的长度
def length
return 0 if @from > @to # 如果from大于to说明长度为0
Integer((@to-@from)/@by) + 1 # 计算并返回数组长度
end
# 给length定义一个别名方法.
# Ruby中一个方法通常有多个名称
alias size length # 现在size方法与length是相同的。
#重载数组下标访问的操作符
def[](index)
return nil if index < 0 # 如果是负数的下标就返回nil
v = @from + index*@by # 计算返回值
if v <= @to # 判断它是否为数组成员
v # 如果是就返回
else # 否则
nil # 返回nil
end
end
# 重载算数操作符,返回一个新的序列
def *(factor)
Sequence.new(@from*factor, @to*factor, @by*factor)
end
def +(offset)
Sequence.new(@from+offset, @to+offset, @by)
end
end
下面演示了如何使用这个类:
s = Sequence.new(1, 10, 2) # 定义1到10,步长为2
s.each {|x| print x } # 打印 "13579"
print s[s.size-1] # 打印 9
t = (s+1)*2 # 从4到22,步长为4
实际上我们这个类中最关键的部分是迭代方法,实际上如果你只是对迭代方法感兴趣你不必一定要定义一个类,你可以直接在脚本文件中写这个方法,这样它就是一个全局的方法,当然定义太多的全局方法不是个好主意,我们可以把这个方法放到一个模块中module:
module Sequences # 定义一个模块
def self.fromtoby(from, to, by) # 在模块中定义一个方法
x = from
while x <= to
yield x
x += by
end
end
end
通过这种方式定义的方法,我们可以这样调用:
Sequences.fromtoby(1, 10, 2) {|x| print x } # 打印"13579"
我们使用了另一种方式来定义迭代方法,但是看起来方法名太长了,而且调用它的时候也比较麻烦,有没有简便的办法呢?实际上我们只是想在Range类型中迭代,并且定义一个方法可以自定义步长,Ruby中一个神奇的特性是它的类是“开放”的,甚至是它的核心类,你可以在任何时候向其中新增方法,所以让我们来给Range定义一个新的方法:
Created with colorer-take5 library. Type 'ruby'
class Range # 打开一个现存的类,以便给他增加新的方法。
def by(step) # 定义迭代方法的名称为by
x = self.begin # 从Range的一段开始迭代
if exclude_end? # 如果当前的指针没有位于Range的结束点
while x < self.end # 开始循环直到当前的x小于结束点
yield x
x += step
end
else # 如果当前的指针位于Range的结束点
while x <= self.end # 开始循环直到下 <=结束点
yield x
x += step
end
end
end # 结束方法定义
end # 结束对类的修改
# 一个调用的例子
(0..10).by(10) {|x| print x} # 打印 "0246810"
#(0...10).by(2) {|x| print x} # 打印 "02468"
by 方法非常的方便不是吗?但是它不是必须的,因为Range对象实际上有一个step方法可以实现相同的功能,Ruby的核心类型非常丰富,实际上很多你需要的东西Ruby已经为你提供了,在后面的章节中我们会详细介绍Ruby的核心类库。
(7)正则表达式与序列
前面的章节中我们提到了Ruby中的数组和字典对象以及整数和字符串对象,现在我们要介绍另外两种重要的对象,正则表达式(Regexp )和序列(Range)。正则表达式描述了一种文本模式,并且提供了方法来判断一个字符串是否与给定的模式匹配;序列对象是中间用两个点号隔开的值(通常用于整数值)
正则表达式和序列通过==来判断是否相等,除此之外还定义了一个===来测试是模式否匹配或者序列中是否包含某个成员。
Ruby 中的case(类似于Java或C中的switch语句) 语句使用===来匹配每一个判断的值,所以这中方式通常称为条件相等操作符,通常与用条件判断,例如:
/[Rr]uby/ # 匹配 "Ruby" 或者 "ruby"
/\d{5}/ # 匹配5个连续的数字
1..3 # 代表所有的1到3之间的值,相当于1<= x <=3
1...3 # 代表一到3之间的值,但不包括3,相当于 1 <= x < 3
正则表达式和序列通过==来判断是否相等,除此之外还定义了一个===来测试是模式否匹配或者序列中是否包含某个成员。
Ruby 中的case(类似于Java或C中的switch语句) 语句使用===来匹配每一个判断的值,所以这中方式通常称为条件相等操作符,通常与用条件判断,例如:
# 根据出生日期判断使用那个名字
generation = case birthyear
when 1946..1963: "Baby Boomer"
when 1964..1976: "Generation X"
when 1978..2000: "Generation Y"
else nil
end
# 定义一个方法来与用户进行确认
def are_you_sure? # 定义一个方法,用到了问号标记
while true # 开始循环
print "Are you sure? [y/n]: " # 向用户提问
response = gets # 获得答案
case response # 开始条件判断
when /^[yY]/ # 如果条件值的首字母为Y或者y
return true # 返回true
when /^[nN]/, /^$/ # 如果条件值以n,N开头或者为空
return false # 返回false
end
end
end
(6)Ruby中标点符号的前缀与后缀
在本节的第五部分我们提到了用等号结尾的方法。Ruby中的方法同样可以以问号和叹号结尾,问号通常用于谓语方法,这种方法返回一个布尔值。例如Array和Hash类都定义了一个empty?方法,这个方法用于测试数据结构中有没有元素。如果方法以叹号结尾,这意味着我们在使用这个方法的时候要小心,比如大多数核心的Ruby类库方法都提供两个同名的方法,一个以谈好结尾,一个没有,区别在于,如果使用没有叹号结尾的方法,你在调用它的时候会得到当前对象的一个拷贝而不会修改原始对象,而如果使用带有叹号的方法,你在调用它的时候会直接修改当前对象的值。比如Array类对象中定义的sort和sort!方法。
我们再来谈谈前缀符号,在定义变量的时候如果你在变量前面加上$符号,这说明它是一个全局变量,局部变量使用@开头,类变量使用@@开头。开始的时候你可能需要适应一下这种用法,但是以后你就会发现,你要感谢中这语法它让你很清楚的知道你现在使用的变量的范围。这种符号前缀是为了消除Ruby中灵活的语法结构带来的歧义。你可以这样理解符号前缀的好处,我们话很少的待解消除了在方法后加上括号的累赘:)。
我们再来谈谈前缀符号,在定义变量的时候如果你在变量前面加上$符号,这说明它是一个全局变量,局部变量使用@开头,类变量使用@@开头。开始的时候你可能需要适应一下这种用法,但是以后你就会发现,你要感谢中这语法它让你很清楚的知道你现在使用的变量的范围。这种符号前缀是为了消除Ruby中灵活的语法结构带来的歧义。你可以这样理解符号前缀的好处,我们话很少的待解消除了在方法后加上括号的累赘:)。
(5)Ruby中的赋值操作
Ruby中通过等号=将一个值赋给一个变量:
x=1
赋值操作可以与操作符+和- 联合使用:
Ruby 还支持并行赋值,可以给多个变量同时赋值:
Ruby中的方法支持返回多个值,我们可以利用这个特性给多个变量平行的赋值:
在Ruby中方法是可以以等号“=”结尾的,这种情况下Ruby允许通过复制操作来调用一个方法,比如下面我们定义一个类有一个方法名为x= 并且这个方法有一个参数,那么下面这两种调用方式是相同的:
x=1
赋值操作可以与操作符+和- 联合使用:
x += 1 # 递加操作: 注意Ruby没有++ 操作。
y -= 1 # 递减操作:注意Ruby也没有--操作。
Ruby 还支持并行赋值,可以给多个变量同时赋值:
x, y = 1, 2 # 等同于 x = 1; y = 2
a, b = b, a # 交换两个变量的值
x,y,z = [1,2,3] # 将数组的值自动赋值到变量
Ruby中的方法支持返回多个值,我们可以利用这个特性给多个变量平行的赋值:
# 定义一个方法获得笛卡尔集
def polar(x,y)
theta = Math.atan2(y,x) # 计算角度
r = Math.hypot(x,y) # 计算距离
[r, theta] # 返回最终的值
end
# 下面的方法展示了如何通过一个方法的返回值给两个变量赋值
distance, angle = polar(2,2)
在Ruby中方法是可以以等号“=”结尾的,这种情况下Ruby允许通过复制操作来调用一个方法,比如下面我们定义一个类有一个方法名为x= 并且这个方法有一个参数,那么下面这两种调用方式是相同的:
#定义一个名为Test的类
class Test
def x=(v) # 定义一个只有一个参数的方法
print v # 打印这个参数
end
end
t=Test.new #创建这个对象
t.x=(1) #通常的方法调用模式
t.x=2 #通过赋值调用的模式
(4)Ruby 中的方法method
Ruby 中方法用def关键字定义,方法的返回值是方法体的最后一个表达式的值,比如:
当一个方法定义在类或者模块之外,就像上面这个方法,那么它就是一个全局的函数,而不是一个类或者对象的方法。
方法也可以定义为一个独立对象的方法,我们可以通过增加对象前缀的方式实现:
事实上Math模块是Ruby的核心类型,这段代码给它增加了一个新的方法。这是Ruby的一个关键特性,即,类和模块是“开发的”,这意味着你可以在运行时对其进行扩展和修改。
另外要提到的一点是,方法的参数是可以指定默认值的,参数的个数是没有限制的。
def square(x) # 定义一个方法名为square,它有一个参数x
x*x # 返回x的平方
end # 结束方法
当一个方法定义在类或者模块之外,就像上面这个方法,那么它就是一个全局的函数,而不是一个类或者对象的方法。
方法也可以定义为一个独立对象的方法,我们可以通过增加对象前缀的方式实现:
def Math.square(x) # 定义Math模块的类方法
x*x
end
事实上Math模块是Ruby的核心类型,这段代码给它增加了一个新的方法。这是Ruby的一个关键特性,即,类和模块是“开发的”,这意味着你可以在运行时对其进行扩展和修改。
另外要提到的一点是,方法的参数是可以指定默认值的,参数的个数是没有限制的。
(3)Ruby中的表达式和操作符
Ruby 的语法是面向表达式的,和其他语言不同,像if这样的控制语句在Ruby中是表达式,也就是会所它是有返回值的,比如下面的例子:
Ruby 的操作和大多数语言是类似的,比如Java,C++,javaScript等等 :
minimum = if x < y then x else y end但是这并不意味着它们都返回一个实际的值,比如while loop 这样的循环它们实际上返回的是一个空值nil。
Ruby 的操作和大多数语言是类似的,比如Java,C++,javaScript等等 :
大多数Ruby的操作符都实现为方法,这意味着你可以定义或者重载这些操作符,比如+和* 操作对于整数和字符串是不一样的,你可以定义自己的操作实现。
Created with colorer-take5 library. Type 'ruby'
1 + 2 # => 3: 加法运算
1 * 2 # => 2: 乘法运算
1 + 2 == 3 # => true: ==用于测试是否相等
2 ** 1024 # 计算2的1024次方,Ruby的整数是任意长度的
"Ruby" + " rocks!" # => "Ruby rocks!": 字符串相加
"Ruby! " * 3 # => "Ruby! Ruby! Ruby! ": 重复输出3次
"%d %s" % [3, "rubies"] # => "3 Rubies": 类似于Python的printf格式
max = x > y ? x : y # 条件表达式
Wednesday, October 08, 2008
(2)块 与 迭代 (block and Iterator)
在Ruby中调用 整数 的方法非常简单,实际上大多数程序员经常这样做。比如下面的例子:
不只整数有迭代方法,比如数组Array(以及任何“枚举”类的类型)都有一个each迭代方法,这个方法会循环调用块内的方法并且每次循环将数组内的一个元素传递给块内的运行逻辑。
Ruby 中的列表 Map 也有迭代方法each.不过它不同于数组是通过整数下标的方式取值,它是一个键/值对的列表,取值是通过在方括号内放置键的方式。比如下面的例子:
顺便提一下前面用到的#{} 方式,他可以把任何对象转换为字符串,实际上它调用了每一个对象都是先的to_s 方法,我们称这种方法为字符串插入string interpolation。
3.times { print "Ruby! " } # 打印 "Ruby! Ruby! Ruby! "
1.upto(9) {|x| print x } # 打印 "123456789
times 和 upto 是整数对象实现的方法,他们是一种特殊的方法我们称之为迭代,花括号{}中的内容我们称之为块,结合迭代他们实现了对块内部分进行循环的操作,实际上迭代和块是Ruby的一个特性,尽管Ruby语言也提供了原生的while loop 循环,但是跟通用的方法是使用块和迭代。不只整数有迭代方法,比如数组Array(以及任何“枚举”类的类型)都有一个each迭代方法,这个方法会循环调用块内的方法并且每次循环将数组内的一个元素传递给块内的运行逻辑。
a = [3, 2, 1] # 我们定义一个数组
a[3] = a[2] - 1 # 类用方括号下标的方法 查询并给新的数组元素赋值
a.each do |elt| # each 是一个迭代。这个块有一个参数名为 elt
print elt+1 # 打印 "4321"
end # 这里使用 do/end 来定义块而不是用括号{}
另一个例子:a = [1,2,3,4] # 定义一个数组
b = a.map {|x| x*x } # 对块内的内容求平方: b = [1,4,9,16]
c = a.select {|x| x%2==0 } # 选择数组中的偶数: c is [2,4]
a.inject do |sum,x| # 计算数组中所有数的合计值 => 10
sum + x
end
Ruby 中的列表 Map 也有迭代方法each.不过它不同于数组是通过整数下标的方式取值,它是一个键/值对的列表,取值是通过在方括号内放置键的方式。比如下面的例子:
h = { # 我们定义一个新的列表
:one => 1, # 通过=>标记来给键 赋值: key=>value
:two => 2 # 每个键值对用逗号分隔
}
h[:one] # => 1. 通过键访问值,其结果为1
h[:three] = 3 # 在列表中增加一个新的键值对
h.each do |key,value| # 通过键/值对迭代
print "#{value}:#{key}; " # 通过把对象放在#{}内将其转换为字符串
end
Ruby 中的块与迭代的特性非常强大,不只是在循环中使用甚至在一个方法中只调用一次也是非常有用的:File.open("data.txt") do |f| # 打开指定的文件把字节流传给块
line = f.readline # 读取字节流的内容
end # 块结束的时候字节流自动被关闭
t = Thread.new do # 在一个新的线程里运行这个块
File.read("data.txt") # 在程序后台读取文件
end # 文件的内容被读入一个新的线程中。
顺便提一下前面用到的#{} 方式,他可以把任何对象转换为字符串,实际上它调用了每一个对象都是先的to_s 方法,我们称这种方法为字符串插入string interpolation。
(1)一切都是对象
Ruby是一个面向对象的语言,在Ruby中所有的东西都是对象,类、方法、变量、数据类型等等都是,甚至一个数字也是对象,还有true,false 以及nil这些都是对象。注意这里Ruby使用nil代表一个空的对象,他相当于其他语言(java)中的null。
Ruby 中使用#作为注释标记,所有这一行中#后面的内容都是注释内容,他们不参与程序的逻辑运算。=>后面的部分给出了当执行#前的方法后所返回的结果,每一个对象都有class方法。
就像你看到的那样,如果一个方法没有参数的话,那么方法后的括号()是可选的,实际上我们应该避免使用括号。这和其他的语言是有差别的,比如Java中几乎每个方法后都是需要括号的,不管是不是有参数。
1.class # => Fixnum: the number 1 is a Fixnum
0.0.class # => Float: floating-point numbers have class Float
true.class # => TrueClass: true is a the singleton instance of TrueClass
false.class # => FalseClass
nil.class # => NilClass
Ruby 中使用#作为注释标记,所有这一行中#后面的内容都是注释内容,他们不参与程序的逻辑运算。=>后面的部分给出了当执行#前的方法后所返回的结果,每一个对象都有class方法。
就像你看到的那样,如果一个方法没有参数的话,那么方法后的括号()是可选的,实际上我们应该避免使用括号。这和其他的语言是有差别的,比如Java中几乎每个方法后都是需要括号的,不管是不是有参数。
Subscribe to:
Posts (Atom)