Ruby初探
官方网站:https://www.ruby-lang.org/zh_cn/
标准库API文档:http://ruby-doc.org/stdlib-2.3.0/
特性
安装
Ruby 命令行选项
编码
语法
空白与行尾
标识符与保留字
Here Document
BEGIN与END 语句
注释
运算符
算数运算符
比较运算符
赋值运算符
并行赋值
位运算符
范围运算符
defined? 运算符
点运算符 “.” 和双冒号运算符 “::”
运算符的优先级
数据类型
数值(Number)
字符串(String)
数组(Array)
哈希(Hash)
范围(Range)
迭代器(iterator)
each 迭代器
collect 迭代器
条件判断与循环
变量
方法
类方法
alias 语句与undef 语句
块
BEGIN 和 END 块
模块(Module)
require 语句
include 语句
Mixins
面向对象
类定义
访问器(accessor) & 设置器(setter)方法
访问控制
继承与重载
冻结对象
allocate 创建对象
类信息
日期与时间
时间格式化指令
异常处理
retry 语句
raise 语句
Catch 和 Throw
类 Exception
参考
简介
Ruby,一种为简单快捷的面向对象编程(面向对象程序设计)而创的脚本语言,在20世纪90年代由日本人松本行弘(Yukihiro Matsumoto)开发,遵守GPL协议和Ruby License。它的灵感与特性来自于 Perl、Smalltalk、Eiffel、Ada以及 Lisp 语言。Smalltalk 是一个真正的面向对象语言。Ruby,与 Smalltalk 一样,是一个完美的面向对象语言。使用 Ruby 的语法比使用 Smalltalk 的语法要容易得多。由 Ruby 语言本身还发展出了JRuby(Java平台)、IronRuby(.NET平台)等其他平台的 Ruby 语言替代品。Ruby的作者于1993年2月24日开始编写Ruby,直至1995年12月才正式公开发布于fj(新闻组)。因为Perl发音与6月诞生石pearl(珍珠)相同,因此Ruby以7月诞生石ruby(红宝石)命名。在 Ruby 社区,松本也被称为马茨(Matz)。
Ruby 经常位于全球编程语言成长和流行度指数的前十名(比如TIOBE)。造成 Ruby 如此快速成长的原因很大程度上是因为使用 Ruby 编写的 Web 框架 Ruby on Rails 非常受欢迎。
Ruby哲学是:“每一件事情总是有多种做法”(Having more than one way to do the same thing)。
所以程序员的创造力就完全被激发出来了。当你看到某个问题的一种前所未见又十分优雅的解决方案时,你会有一种混杂着强烈喜悦的惊讶的——写(优美的)Ruby能够激发人的灵感。
Ruby的元编程能力还有宽松的语法使得使用Ruby可以很轻松的开发DSL(Domain Specific Language)
Ruby吸收了很多语言(Lisp、Smalltalk等等)的特性,对于程序员来说了解一下Ruby对于开阔思路是很有帮助的,它能帮助你更好的看清你之前一直使用的语言的限制之处在哪。
特性
- Ruby 是开源的,在 Web 上免费提供,但需要一个许可证。
- Ruby 是一种通用的、解释的编程语言。
- Ruby 是一种真正的面向对象编程语言。
- Ruby 是一种类似于 Python 和 Perl 的服务器端脚本语言。
- Ruby 可以用来编写通用网关接口(CGI)脚本。
- Ruby 可以被嵌入到超文本标记语言(HTML)。
- Ruby 语法简单,这使得新的开发人员能够快速轻松地学习 Ruby。
- Ruby 与 C++ 和 Perl 等许多编程语言有着类似的语法。
- Ruby 可扩展性强,用 Ruby 编写的大程序易于维护。
- Ruby 可用于开发的 Internet 和 Intranet 应用程序。
- Ruby 可以安装在 Windows 和 POSIX 环境中。
- Ruby 支持许多 GUI 工具,比如 Tcl/Tk、GTK 和 OpenGL。
- Ruby 可以很容易地连接到 DB2、MySQL、Oracle 和 Sybase。
- Ruby 有丰富的内置函数,可以直接在 Ruby 脚本中使用。
安装
linux环境
sudo apt-get install ruby-full # Debian 或 Ubuntu 系统
sudo yum install ruby # CentOS, Fedora, 或 RHEL 系统
ruby -v
ruby 2.1.5p273 (2014-11-13) [x86_64-linux-gnu]
Ruby 命令行选项
Ruby 一般是从命令行运行,方式如下:
$ ruby [ options ] [.] [ programfile ] [ arguments … ]
解释器可以通过下列选项被调用,来控制解释器的环境和行为。
选项 | 描述 |
---|---|
-a | 与 -n 或 -p 一起使用时,可以打开自动拆分模式(auto split mode)。请查看 -n 和 -p 选项。 |
-c | 只检查语法,不执行程序。 |
-C dir | 在执行前改变目录(等价于 -X)。 |
-d | 启用调试模式(等价于 -debug)。 |
-F pat | 指定 pat 作为默认的分离模式($;)。 |
-e prog | 指定 prog 作为程序在命令行中执行。可以指定多个 -e 选项,用来执行多个程序。 |
-h | 显示命令行选项的一个概览。 |
-i [ ext] | 把文件内容重写为程序输出。原始文件会被加上扩展名 ext 保存下来。如果未指定 ext,原始文件会被删除。 |
-I dir | 添加 dir 作为加载库的目录。 |
-K [ kcode] | 指定多字节字符集编码。e 或 E 对应 EUC(extended Unix code),s 或 S 对应 SJIS(Shift-JIS),u 或 U 对应 UTF-8,a、A、n 或 N 对应 ASCII。 |
-l | 启用自动行尾处理。从输入行取消一个换行符,并向输出行追加一个换行符。 |
-n | 把代码放置在一个输入循环中(就像在 while gets; … end 中一样)。 |
-0[ octal] | 设置默认的记录分隔符($/)为八进制。如果未指定 octal 则默认为 \0。 |
-p | 把代码放置在一个输入循环中。在每次迭代后输出变量 $_ 的值。 |
-r lib | 使用 require 来加载 lib 作为执行前的库。 |
-s | 解读程序名称和文件名参数之间的匹配模式 -xxx 的任何参数作为开关,并定义相应的变量。 |
-T [level] | 设置安全级别,执行不纯度测试(如果未指定 level,则默认值为 1)。 |
-v | 显示版本,并启用冗余模式。 |
-w | 启用冗余模式。如果未指定程序文件,则从 STDIN 读取。 |
-x [dir] | 删除 #!ruby 行之前的文本。如果指定了 dir,则把目录改变为 dir。 |
-X dir | 在执行前改变目录(等价于 -C)。 |
-y | 启用解析器调试模式。 |
–copyright | 显示版权声明。 |
–debug | 启用调试模式(等价于 -d)。 |
–help | 显示命令行选项的一个概览(等价于 -h)。 |
–version | 显示版本。 |
–verbose | 启用冗余模式(等价于 -v)。设置 $VERBOSE 为 true。 |
–yydebug | 启用解析器调试模式(等价于 -y)。 |
编码
如果出现乱码或者字符异常,尝试声明编码即可,同时必须设置编辑器保存文件的编码为utf-8。
#!/usr/bin/ruby -w
# -*- coding: UTF-8 -*-
puts "你好,世界!";
语法
让我们编写一个简单的 Ruby 程序。所有的 Ruby 文件扩展名都是 .rb。所以,把下面的源代码放在 test.rb 文件中。
#!/usr/bin/ruby -w
puts "Hello, Ruby!";
$ ruby test.rb
Hello, Ruby!
空白与行尾
在 Ruby 代码中的空白字符,如空格和制表符一般会被忽略,除非当它们出现在字符串中时才不会被忽略。然而,有时候它们用于解释模棱两可的语句。当启用 -w 选项时,这种解释会产生警告。
a + b 被解释为 a+b (这是一个局部变量)
a +b 被解释为 a(+b) (这是一个方法调用)
Ruby 把分号和换行符解释为语句的结尾。但是,如果 Ruby 在行尾遇到运算符,比如 +、- 或反斜杠,它们表示一个语句的延续。
标识符与保留字
标识符是变量、常量和方法的名称。Ruby 标识符是大小写敏感的。这意味着 Ram 和 RAM 在 Ruby 中是两个不同的标识符。
Ruby 标识符的名称可以包含字母、数字和下划线字符( _ )。
下表列出了 Ruby 中的保留字。这些保留字不能作为常量或变量的名称。但是,它们可以作为方法名。
BEGIN do next then
END else nil true
alias elsif not undef
and end or unless
begin ensure redo until
break false rescue when
case for retry while
class if return while
def in self FILE
defined? module super LINE
Here Document
“Here Document” 是指建立多行字符串。在 << 之后,您可以指定一个字符串或标识符来终止字符串,且当前行之后直到终止符为止的所有行是字符串的值。
如果终止符用引号括起,引号的类型决定了面向行的字符串类型。请注意<< 和终止符之间必须没有空格。
#!/usr/bin/ruby -w
# -*- coding : utf-8 -*-
print <<EOF
这是第一种方式创建here document 。
多行字符串。
EOF
print <<"EOF"; # 与上面相同
这是第二种方式创建here document 。
多行字符串。
EOF
print <<`EOC` # 执行命令
echo hi there
echo lo there
EOC
print <<"foo", <<"bar" # 您可以把它们进行堆叠
I said foo.
foo
I said bar.
bar
BEGIN与END 语句
#!/usr/bin/ruby
puts "This is main Ruby Program"
END {
puts "Terminating Ruby Program"
}
BEGIN {
puts "Initializing Ruby Program"
}
Initializing Ruby Program
This is main Ruby Program
Terminating Ruby Program
注释
注释会对 Ruby 解释器隐藏一行,或者一行的一部分,或者若干行。您可以在行首使用字符( # )
或者,注释可以跟着语句或表达式的同一行的后面,
块注释会对解释器隐藏 =begin/=end 之间的行。
# 我是注释,请忽略我。
name = "Madisetti" # 这也是注释
# 这是注释。
# 这也是注释。
# 这也是注释。
# 这还是注释。
=begin
这是注释。
这也是注释。
这也是注释。
这还是注释。
=end
运算符
Ruby 支持一套丰富的运算符。大多数运算符实际上是方法调用。例如,a + b 被解释为 a.+(b),其中指向变量 a 的 + 方法被调用,b 作为方法调用的参数。
对于每个运算符(+ - * / % ** & | ^ << >> && ||),都有一个相对应的缩写赋值运算符(+= -= 等等)。
逻辑运算符,三元运算符略过,语言通用。
算数运算符
常规语言通用略过
其中 代表指数 - 执行指数计算, 假设变量 a 的值为 10,变量 b 的值为 20, ab 将得到 10 的 20 次方
比较运算符
常规语言通用略过,其中
运算符 | 描述 | 实例 |
---|---|---|
<=> | 联合比较运算符。如果第一个操作数等于第二个操作数则返回 0,如果第一个操作数大于第二个操作数则返回 1,如果第一个操作数小于第二个操作数则返回 -1。 | (a <=> b) 返回 -1。 |
=== | 用于测试 case 语句的 when 子句内的相等。 | (1…10) === 5 返回 true。 |
.eql? | 如果接收器和参数具有相同的类型和相等的值,则返回 true。 | 1 == 1.0 返回 true,但是 1.eql?(1.0) 返回 false。 |
equal? | 如果接收器和参数具有相同的对象 id,则返回 true。 | 如果 aObj 是 bObj 的副本,那么 aObj == bObj 返回 true,a.equal?bObj 返回 false,但是 a.equal?aObj 返回 true。 |
赋值运算符
常规语言通用略过,其中
= 指数且赋值运算符,执行指数计算,并赋值给左操作数 c = a 相当于 c = c ** a
并行赋值
a, b, c = 10, 20, 30
#并行赋值在交换两个变量的值时也很有用
a, b = b, c
位运算符
位运算符作用于位,并逐位执行操作。
假设如果 a = 60,且 b = 13,现在以二进制格式,它们如下所示:
a = 0011 1100
b = 0000 1101
a&b = 0000 1100
a|b = 0011 1101
a^b = 0011 0001
~a = 1100 0011
运算符 | 描述 | 实例 |
---|---|---|
& | 如果同时存在于两个操作数中,二进制 AND 运算符复制一位到结果中。 | (a & b) 将得到 12,即为 0000 1100 |
| | 如果存在于任一操作数中,二进制 OR 运算符复制一位到结果中。 | (a | b) 将得到 61,即为 0011 1101 |
^ | 如果存在于其中一个操作数中但不同时存在于两个操作数中,二进制异或运算符复制一位到结果中。 | (a ^ b) 将得到 49,即为 0011 0001 |
~ | 二进制补码运算符是一元运算符,具有”翻转”位效果。 | (~a ) 将得到 -61,即为 1100 0011,2 的补码形式,带符号的二进制数。 |
<< | 二进制左移运算符。左操作数的值向左移动右操作数指定的位数。 | a << 2 将得到 240,即为 1111 0000 |
>> | 二进制右移运算符。左操作数的值向右移动右操作数指定的位数。 | a >> 2 将得到 15,即为 0000 1111 |
范围运算符
在 Ruby 中,序列范围用于创建一系列连续的值 - 包含起始值、结束值(视情况而定)和它们之间的值。
在 Ruby 中,这些序列是使用 “..” 和 “…” 范围运算符来创建的。两点形式创建的范围包含起始值和结束值,三点形式创建的范围只包含起始值不包含结束值。
1..10 创建从 1 到 10 的范围
1…10 创建从 1 到 9 的范围
defined? 运算符
defined? 是一个特殊的运算符,以方法调用的形式来判断传递的表达式是否已定义。它返回表达式的描述字符串,如果表达式未定义则返回 nil。
点运算符 “.” 和双冒号运算符 “::”
您可以通过在方法名称前加上模块名称和一条下划线来调用模块方法。您可以使用模块名称和两个冒号来引用一个常量。
:: 是一元运算符,允许在类或模块内定义常量、实例方法和类方法,可以从类或模块外的任何地方进行访问。
请记住:在 Ruby 中,类和方法也可以被当作常量。
您只需要在表达式的常量名前加上 :: 前缀,即可返回适当的类或模块对象。
如果未使用前缀表达式,则默认使用主 Object 类。
CONST = ' out there'
class Inside_one
CONST = proc {' in there'}
def where_is_my_CONST
::CONST + ' inside one'
end
end
class Inside_two
CONST = ' inside two'
def where_is_my_CONST
CONST
end
end
puts Inside_one.new.where_is_my_CONST
puts Inside_two.new.where_is_my_CONST
puts Object::CONST + Inside_two::CONST
puts Inside_two::CONST + CONST
puts Inside_one::CONST
puts Inside_one::CONST.call + Inside_two::CONST
运算符的优先级
下表按照运算符的优先级从高到低列出了所有的运算符。
方法 | 运算符 | 描述 |
---|---|---|
是 | :: | 常量解析运算符 |
是 | [ ] [ ]= | 元素引用、元素集合 |
是 | ** | 指数 |
是 | ! ~ + - | 非、补、一元加、一元减(最后两个的方法名为 +@ 和 -@) |
是 |
|
乘法、除法、求模 |
是 |
|
加法和减法 |
是 | >> << | 位右移、位左移 |
是 | & | 位与 |
是 | ^ | | 位异或、位或 |
是 | <= < > >= | 比较运算符 |
是 | <=> == === != =~ !~ | 相等和模式匹配运算符(!= 和 !~ 不能被定义为方法) |
&& | 逻辑与 | |
|| | 逻辑或 | |
.. … | 范围(包含、不包含) | |
? : | 三元 if-then-else | |
= %= { /= -= += | = &= >>= <<= *= &&= | |
defined? | 检查指定符号是否已定义 | |
not | 逻辑否定 | |
or and | 逻辑组成 |
数据类型
Ruby支持的数据类型包括基本的Number、String、Ranges、Symbols,以及true、false和nil这几个特殊值,同时还有两种重要的数据结构——Array和Hash。
数值(Number)
整型(Integer)
整型分两种,如果在31位以内(四字节),那为Fixnum实例。如果超过,即为Bignum实例。
整数范围从 -230 到 230-1 或 -262 到 262-1。在这个范围内的整数是类 Fixnum 的对象,在这个范围外的整数存储在类 Bignum 的对象中。
您可以在整数前使用一个可选的前导符号,一个可选的基础指标(0 对应 octal,0x 对应 hex,0b 对应 binary),后跟一串数字。下划线字符在数字字符串中被忽略。
您可以获取一个 ASCII 字符或一个用问号标记的转义序列的整数值。
123 # Fixnum 十进制
1_234 # Fixnum 带有下划线的十进制
-500 # 负的 Fixnum
0377 # 八进制
0xff # 十六进制
0b1011 # 二进制
"a".ord # "a" 的字符编码
?\n # 换行符(0x0a)的编码
12345678901234567890 # Bignum
浮点型
Ruby 支持浮点数。它们是带有小数的数字。浮点数是类 Float 的对象,且可以是下列中任意一个。
123.4 # 浮点值
1.0e6 # 科学记数法
4E20 # 不是必需的
4e+20 # 指数前的符号
字符串(String)
Ruby 中的 String 对象用于存储或操作一个或多个字节的序列。
Ruby 字符串分为单引号字符串(’)和双引号字符串(”),区别在于双引号字符串能够支持更多的转义字符。
#!/usr/bin/ruby -w
puts 'escape using "\\"';
puts 'That\'s right';
escape using "\"
That's right
可以使用序列 #{ expr } 替换任意 Ruby 表达式的值为一个字符串。在这里,expr 可以是任意的 Ruby 表达式。
puts "Multiplication Value : #{24*60*60}";
Multiplication Value : 86400
name="Ruby"
puts name
puts "#{name+",ok"}"
Ruby
Ruby,ok
转义字符
反斜杠符号 | 十六进制字符 | 描述 |
---|---|---|
\a | 0x07 | 报警符 |
\b | 0x08 | 退格键 |
\cx | Control-x | |
\C-x | Control-x | |
\e | 0x1b | 转义符 |
\f | 0x0c | 换页符 |
\M-\C-x | Meta-Control-x | |
\n | 0x0a | 换行符 |
\nnn | 八进制表示法,其中 n 的范围为 0.7 | |
\r | 0x0d | 回车符 |
\s | 0x20 | 空格符 |
\t | 0x09 | 制表符 |
\v | 0x0b | 垂直制表符 |
\x | 字符 x | |
\xnn | 十六进制表示法,其中 n 的范围为 0.9、 a.f 或 A.F |
字符编码
Ruby 的默认字符集是 ASCII,字符可用单个字节表示。如果您使用 UTF-8 或其他现代的字符集,字符可能是用一个到四个字节表示。
您可以在程序开头使用 $KCODE 改变字符集
$KCODE = ‘u’
a ASCII (与 none 相同)。这是默认的。
e EUC。
n None (与 ASCII 相同)。
u UTF-8。
数组(Array)
Ruby 数组是任何对象的有序整数索引集合。数组中的每个元素都与一个索引相关,并可通过索引进行获取。
数组的索引从 0 开始,这与 C 或 Java 中一样。一个负数的索相对于数组的末尾计数的,也就是说,索引为 -1 表示数组的最后一个元素,-2 表示数组中的倒数第二个元素,依此类推。
Ruby 数组可存储诸如 String、 Integer、 Fixnum、 Hash、 Symbol 等对象,甚至可以是其他 Array 对象。
Ruby 数组不需要指定大小,当向数组添加元素时,Ruby 数组会自动增长。
names = Array.new
names = Array.new(20) #创建数组的同时设置数组的大小
puts names.size # 返回 20
puts names.length # 返回 20
names = Array.new(4, "mac")
puts "#{names}"
#["mac", "mac", "mac", "mac"]
nums = Array.new(10) { |e| e = e * 2 }
puts "#{nums}"
#[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
nums = Array[1, 2, 3, 4,5]
nums = Array.[](1, 2, 3, 4,5)
digits = Array(0..9)
puts "#{digits}"
#[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
哈希(Hash)
哈希(Hash)是类似 “key” => “value” 这样的键值对集合。哈希类似于一个数组,只不过它的索引不局限于使用数字。
Hash 的索引(或者叫”键”)几乎可以是任何对象。
Hash 虽然和数组类似,但却有一个很重要的区别:Hash 的元素没有特定的顺序。 如果顺序很重要的话就要使用数组了。
months = Hash.new
# 创建带有默认值的哈希
months = Hash.new( "month" )
months = Hash.new "month"
#当您访问带有默认值的哈希中的任意键时,如果键或值不存在,访问哈希将返回默认值
puts "#{months[0]}"
puts "#{months[72]}"
#month
#month
H = Hash["a" => 100, "b" => 200]
puts "#{H['a']}"
puts "#{H['b']}"
#100
#200
范围(Range)
Ruby 支持范围,并允许我们以不同的方式使用范围:
- 作为序列的范围
- 作为条件的范围
- 作为间隔的范围
(1..5) #==> 1, 2, 3, 4, 5
(1...5) #==> 1, 2, 3, 4
('a'..'d') #==> 'a', 'b', 'c', 'd'
#可以使用 to_a 方法把范围转换为列表。
range1 = (1..10).to_a #[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
#可以通过多种方式检查它们的内容
digits = 0..9
puts digits.include?(5)
ret = digits.min
puts "最小值为 #{ret}"
ret = digits.max
puts "最大值为 #{ret}"
ret = digits.reject {|i| i < 5 }
puts "不符合条件的有 #{ret}"
digits.each do |digit|
puts "在循环中 #{digit}"
end
#其中每个集合的第一行包含单词 start,最后一行包含单词 end.
while gets
print if /start/../end/
end
score = 70
result = case score
when 0..40
"糟糕的分数"
when 41..60
"快要及格"
when 61..70
"及格分数"
when 71..100
"良好分数"
else
"错误的分数"
end
puts result
#检查指定值是否在指定的范围内。需要使用 === 相等运算符来完成计算
if ((1..10) === 5)
puts "5 在 (1..10)"
end
if (('a'..'j') === 'c')
puts "c 在 ('a'..'j')"
end
if (('a'..'j') === 'z')
puts "z 在 ('a'..'j')"
end
迭代器(iterator)
迭代器是集合支持的方法。存储一组数据成员的对象称为集合。在 Ruby 中,数组(Array)和哈希(Hash)可以称之为集合。
迭代器返回集合的所有元素,一个接着一个。
each 迭代器
#each 迭代器总是与一个块关联。它向块返回数组的每个值,一个接着一个。值被存储在变量 i 中
ary = [1,2,3,4,5]
ary.each do |i|
puts i
end
#当您想要对每个值进行一些操作以便获得新的数组时,您通常使用 collect 方法。例如,下面的代码会生成一个数组,其值是 a 中每个值的 10 倍。
a = [1,2,3,4,5]
b = a.collect{|x| 10*x}
puts b
collect 迭代器
collect 迭代器返回集合的所有元素。
a = [1,2,3,4,5]
b = Array.new
b = a.collect{ |x|x }
puts b
条件判断与循环
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
#if 表达式用于条件执行。值 false 和 nil 为假,其他值都为真。请注意,Ruby 使用 elsif,不是使用 else if 和 elif。
#如果 conditional 为真,则执行 code。如果 conditional 不为真,则执行 else 子句中指定的 code。
#通常我们省略保留字 then 。若想在一行内写出完整的 if 式,则必须以 then 隔开条件式和程式区块。
x=1
if x > 2
puts "x 大于 2"
elsif x <= 2 and x!=0
puts "x 是 1"
else
puts "无法得知 x 的值"
end
#if修饰词组表示当 if 右边之条件成立时才执行 if 左边的式子。即如果 conditional 为真,则执行 code。
$debug=1
print "debug\n" if $debug
#unless式和 if式作用相反,即如果 conditional 为假,则执行 code。如果 conditional 为真,则执行 else 子句中指定的 code。
$var = 1
print "1 -- 这一行输出\n" if $var
print "2 -- 这一行不输出\n" unless $var
$var = false
print "3 -- 这一行输出\n" unless $var
#unless式和 if式作用相反,即如果 conditional 为假,则执行 code。如果 conditional 为真,则执行 else 子句中指定的 code。
x=1
unless x>2
puts "x 小于 2"
else
puts "x 大于 2"
end
#case先对一个 expression 进行匹配判断,然后根据匹配结果进行分支选择。
#它使用 ===运算符比较 when 指定的 expression,若一致的话就执行 when 部分的内容。
#通常我们省略保留字 then 。若想在一行内写出完整的 when 式,则必须以 then 隔开条件式和程式区块。
$age = 5
case $age
when 0 .. 2
puts "婴儿"
when 3 .. 6
puts "小孩"
when 7 .. 12
puts "child"
when 13 .. 18
puts "少年"
else
puts "其他年龄段的"
end
#语法中 do 或 : 可以省略不写。但若要在一行内写出 while 式,则必须以 do 或 : 隔开条件式或程式区块。
while $i < $num do
puts("在循环语句中 i = #$i" )
$i +=1
end
#当 conditional 为真时,执行 code。
#如果 while 修饰符跟在一个没有 rescue 或 ensure 子句的 begin 语句后面,code 会在 conditional 判断之前执行一次。
$i = 0
$num = 5
begin
puts("在循环语句中 i = #$i" )
$i +=1
end while $i < $num
#当 conditional 为 false 时,执行 code。
#语法中 do 可以省略不写。但若要在一行内写出 until 式,则必须以 do 隔开条件式或程式区块。
$i = 0
$num = 5
until $i > $num do
puts("在循环语句中 i = #$i" )
$i +=1;
end
#如果 until 修饰符跟在一个没有 rescue 或 ensure 子句的 begin 语句后面,code 会在 conditional 判断之前执行一次。
$i = 0
$num = 5
begin
puts("在循环语句中 i = #$i" )
$i +=1;
end until $i > $num
#先计算表达式得到一个对象,然后针对 expression 中的每个元素分别执行一次 code。
for i in 0..5
puts "局部变量的值为 #{i}"
end
#注意条件内的循环终止关键字~
for i in 0..5
if i > 2 then
break #终止最内部的循环。如果在块内调用,则终止相关块的方法(方法返回 nil)。
next #跳到循环的下一个迭代。如果在块内调用,则终止块的执行(yield 表达式返回 nil)。
redo #重新开始最内部循环的该次迭代,不检查循环条件。如果在块内调用,则重新开始 yield 或 call。
retry if i > 2 #如果 retry 出现在迭代内、块内或者 for 表达式的主体内,则重新开始迭代调用。迭代的参数会重新评估。
end
puts "局部变量的值为 #{i}"
end
变量
变量是持有可被任何程序使用的任何数据的存储位置。
Ruby 支持五种类型的变量。
- 一般小写字母、下划线开头:变量(Variable)。
- $开头:全局变量(Global variable)。
- @开头:实例变量(Instance variable)。
- @@开头:类变量(Class variable)类变量被共享在整个继承链中,引用一个未初始化的类变量会产生错误。类变量在定义它的类或模块的子类或子模块中可共享使用。
- 大写字母开头:常量(Constant)。常量不能定义在方法内。引用一个未初始化的常量会产生错误。对已经初始化的常量赋值会产生警告。
方法
Ruby 方法与其他编程语言中的函数类似。Ruby 方法用于捆绑一个或多个重复的语句到一个单元中。
方法名应以小写字母开头。如果您以大写字母作为方法名的开头,Ruby 可能会把它当作常量,从而导致不正确地解析调用。
方法应在调用之前定义,否则 Ruby 会产生未定义的方法调用异常。
Ruby 中的每个方法默认都会返回一个值。这个返回的值是最后一个语句的值。
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
def test
i = 100
j = 200
k = 300
return i, j, k
end
var = test
puts var
#Ruby 允许您声明参数数量可变的方法。
def sample (*test)
puts "参数个数为 #{test.length}"
for i in 0...test.length
puts "参数值为 #{test[i]}"
end
end
sample "Zara", "6", "F"
sample "Mac", "36", "M", "MCA"
类方法
Ruby 提供了一种不用实例化即可访问方法的方式。让我们看看如何声明并访问类方法
class Accounts
def reading_charge
end
def Accounts.return_date
end
end
#如需访问该方法,您不需要创建类 Accounts 的对象。
Accounts.return_date
alias 语句与undef 语句
alias 用于为方法或全局变量起别名。别名不能在方法主体内定义。即使方法被重写,方法的别名也保持方法的当前定义。
为编号的全局变量(2,…)起别名是被禁止的。重写内置的全局变量可能会导致严重的问题。
undef 用于取消方法定义。undef 不能出现在方法主体内。
通过使用 undef 和 alias,类的接口可以从父类独立修改,但请注意,在自身内部方法调用时,它可能会破坏程序。
块
Ruby 有一个块的概念。
- 块由大量的代码组成。
- 您需要给块取个名称。
- 块中的代码总是包含在大括号 {} 内。
- 块总是从与其具有相同名称的函数调用。这意味着如果您的块名称为 test,那么您要使用函数 test 来调用这个块。
- 您可以使用 yield 语句来调用块。
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
def test
puts "在 test 方法内"
yield
puts "你又回到了 test 方法内"
yield
end
test {puts "你在块内"}
在 test 方法内
你在块内
你又回到了 test 方法内
你在块内
def test
yield 5
puts "在 test 方法内"
yield 100,2
end
test {|i,j| puts "你在块 #{i} 内"}
你在块 5 内
在 test 方法内
你在块 100 内
#如果方法的最后一个参数前带有 &,那么您可以向该方法传递一个块,且这个块可被赋给最后一个参数。如果 * 和 & 同时出现在参数列表中,& 应放在后面。
def test(&block)
block.call
end
test { puts "Hello World!"}
BEGIN 和 END 块
#!/usr/bin/ruby
BEGIN {
# BEGIN 代码块
puts "BEGIN 代码块"
}
END {
# END 代码块
puts "END 代码块"
}
# MAIN 代码块
puts "MAIN 代码块"
BEGIN 代码块
MAIN 代码块
END 代码块
模块(Module)
模块(Module)是一种把方法、类和常量组合在一起的方式。模块(Module)为您提供了两大好处。
- 模块提供了一个命名空间和避免名字冲突。
- 模块实现了 mixin 装置。
模块(Module)定义了一个命名空间,相当于一个沙盒,在里边您的方法和常量不会与其他地方的方法常量冲突。
模块类似与类,但有一下不同: - 模块不能实例化
- 模块没有子类
- 模块只能被另一个模块定义
通过类方法,您可以在类方法名称前面放置模块名称和一个点号来调用模块方法,您可以使用模块名称和两个冒号来引用一个常量。
#!/usr/bin/ruby
# 定义在 trig.rb 文件中的模块
module Trig
PI = 3.141592654
def Trig.sin(x)
# ..
end
def Trig.cos(x)
# ..
end
end
require 语句
require 语句类似于 C 和 C++ 中的 include 语句以及 Java 中的 import 语句。如果一个第三方的程序想要使用任何已定义的模块,则可以简单地使用 Ruby require 语句来加载模块文件:
#我们使用 $LOAD_PATH << '.' 让 Ruby 知道必须在当前目录中搜索被引用的文件。如果您不想使用 $LOAD_PATH,那么您可以使用 require_relative 来从一个相对目录引用文件。
$LOAD_PATH << '.'
require 'trig.rb'
require 'moral' #在这里,文件扩展名 .rb 不是必需的。
y = Trig.sin(Trig::PI/4)
wrongdoing = Moral.sin(Moral::VERY_BAD)
include 语句
您可以在类中嵌入模块。为了在类中嵌入模块,您可以在类中使用 include 语句
如果模块是定义在一个单独的文件中,那么在嵌入模块之前就需要使用 require 语句引用该文件。
module Week
FIRST_DAY = "Sunday"
def Week.weeks_in_month
puts "You have four weeks in a month"
end
def Week.weeks_in_year
puts "You have 52 weeks in a year"
end
end
#!/usr/bin/ruby
$LOAD_PATH << '.'
require "support"
class Decade
include Week
no_of_yrs=10
def no_of_months
puts Week::FIRST_DAY
number=10*12
puts number
end
end
d1=Decade.new
puts Week::FIRST_DAY
Week.weeks_in_month
Week.weeks_in_year
d1.no_of_months
#Sunday
#You have four weeks in a month
#You have 52 weeks in a year
#Sunday
#120
Mixins
Ruby 不直接支持多重继承,但是 Ruby 的模块(Module)有另一个神奇的功能。它几乎消除了多重继承的需要,提供了一种名为 mixin 的装置。
Ruby 没有真正实现多重继承机制,而是采用成为mixin技术作为替代品。将模块include到类定义中,模块中的方法就mix进了类中。
module A
def a1
end
def a2
end
end
module B
def b1
end
def b2
end
end
class Sample
include A
include B
def s1
end
end
samp=Sample.new
samp.a1
samp.a2
samp.b1
samp.b2
samp.s1
- 模块 A 由方法 a1 和 a2 组成。
- 模块 B 由方法 b1 和 b2 组成。
- 类 Sample 包含了模块 A 和 B。
- 类 Sample 可以访问所有四个方法,即 a1、a2、b1 和 b2。
因此,您可以看到类 Sample 继承了两个模块,您可以说类 Sample 使用了多重继承或 mixin 。
面向对象
Ruby 是纯面向对象的语言,Ruby 中的一切都是以对象的形式出现。Ruby 中的每个值都是一个对象,即使是最原始的东西:字符串、数字,甚至连 true 和 false 都是对象。类本身也是一个对象,是 Class 类的一个实例。
类用于指定对象的形式,它结合了数据表示法和方法,把数据整理成一个整齐的包。类中的数据和方法被称为类的成员。
类定义
class Customer
@@no_of_customers=0
#自定义方法来创建 Ruby 对象
def initialize(id, name, addr)
@cust_id=id
@cust_name=name
@cust_addr=addr
end
#成员函数
def hello
puts "Hello Ruby!"
end
end
cust1=Customer.new("1", "John", "Wisdom Apartments, Ludhiya")
cust2=Customer.new("2", "Poul", "New Empire road, Khandala")
cus1.hello
访问器(accessor) & 设置器(setter)方法
#!/usr/bin/ruby -w
# 定义类
class Box
# 构造器方法
def initialize(w,h)
@width, @height = w, h
end
# 访问器方法
def getWidth
@width
end
def getHeight
@height
end
# 设置器方法
def setWidth=(value)
@width = value
end
def setHeight=(value)
@height = value
end
end
# 创建对象
box = Box.new(10, 20)
# 使用设置器方法
box.setWidth = 30
box.setHeight = 50
# 使用访问器方法
x = box.getWidth()
y = box.getHeight()
puts "Width of the box is : #{x}"
puts "Height of the box is : #{y}"
to_s 方法
任何类都有一个 to_s 实例方法来返回对象的字符串表示形式。
访问控制
Ruby 为您提供了三个级别的实例方法保护,分别是 public、private 或 protected。Ruby 不在实例和类变量上应用任何访问控制。
- Public 方法: Public 方法可被任意对象调用。默认情况下,方法都是 public 的,除了 initialize 方法总是 private 的。
- Private 方法: Private 方法不能从类外部访问或查看。只有类方法可以访问私有成员。
- Protected 方法: Protected 方法只能被类及其子类的对象调用。访问也只能在类及其子类内部进行。
# 定义类
class Box
# 构造器方法
def initialize(w,h)
@width, @height = w, h
end
# 实例方法默认是 public 的
def getArea
getWidth() * getHeight
end
# 定义 private 的访问器方法
def getWidth
@width
end
def getHeight
@height
end
# make them private
private :getWidth, :getHeight
# 用于输出面积的实例方法
def printArea
@area = getWidth() * getHeight
puts "Big box area is : #@area"
end
# 让实例方法是 protected 的
protected :printArea
end
# 创建对象
box = Box.new(10, 20)
# 调用实例方法
a = box.getArea()
puts "Area of the box is : #{a}"
# 尝试调用 protected 的实例方法
box.printArea()
继承与重载
Ruby 不支持多继承,但是 Ruby 支持 mixins。mixin 就像是多继承的一个特定实现,在多继承中,只有接口部分是可继承的。
当创建类时,程序员可以直接指定新类继承自某个已有类的成员,这样就不用从头编写新的数据成员和成员函数。这个已有类被称为基类或父类,新类被称为派生类或子类。
Ruby 也提供了子类化的概念,子类化即继承,下面的实例解释了这个概念。扩展一个类的语法非常简单。只要添加一个 < 字符和父类的名称到类语句中即可。
#!/usr/bin/ruby -w
# 定义类
class Box
# 构造器方法
def initialize(w,h)
@width, @height = w, h
end
# 实例方法
def getArea
@width * @height
end
end
# 定义子类
class BigBox < Box
# 添加一个新的实例方法
def printArea
@area = @width * @height
puts "Big box area is : #@area"
end
# 改变已有的 getArea 方法
def getArea
@area = @width * @height
puts "Big box area is : #@area"
end
end
# 创建对象
box = BigBox.new(10, 20)
# 输出面积
box.printArea()
box.getArea()
#运算符重载
class Box
def initialize(w,h) # 初始化 width 和 height
@width,@height = w, h
end
def +(other) # 定义 + 来执行向量加法
Box.new(@width + other.width, @height + other.height)
end
def -@ # 定义一元运算符 - 来对 width 和 height 求反
Box.new(-@width, -@height)
end
def *(scalar) # 执行标量乘法
Box.new(@width*scalar, @height*scalar)
end
end
冻结对象
有时候,我们想要防止对象被改变。在 Object 中,freeze 方法可实现这点,它能有效地把一个对象变成一个常量。任何对象都可以通过调用 Object.freeze 进行冻结。冻结对象不能被修改,也就是说,您不能改变它的实例变量。
您可以使用 Object.frozen? 方法检查一个给定的对象是否已经被冻结。如果对象已被冻结,该方法将返回 true,否则返回一个 false 值。
# 创建对象
box = Box.new(10, 20)
# 让我们冻结该对象
box.freeze
if( box.frozen? )
puts "Box object is frozen object"
else
puts "Box object is normal object"
end
allocate 创建对象
可能有一种情况,您想要在不调用对象构造器 initialize 的情况下创建对象,即,使用 new 方法创建对象,在这种情况下,您可以调用 allocate 来创建一个未初始化的对象
# 使用 allocate 创建两一个对象
box2 = Box.allocate
# 使用 box2 调用实例方法
a = box2.getArea()
#test.rb:14: warning: instance variable @width not initialized
#test.rb:14: warning: instance variable @height not initialized
类信息
Ruby的 self 和 Java 的 this 有相似之处,但又大不相同。Java的方法都是在实例方法中引用,所以this一般都是指向当前对象的。而Ruby的代码逐行执行,所以在不同的上下文(context)self就有了不同的含义。
class Box
# 输出类信息
puts "Class of self = #{self.class}"
puts "Name of self = #{self.name}"
end
日期与时间
Time 类在 Ruby 中用于表示日期和时间。它是基于操作系统提供的系统日期和时间之上。该类可能无法表示 1970 年之前或者 2038 年之后的日期。
#!/usr/bin/ruby -w
# -*- coding: UTF-8 -*-
time = Time.new
# Time 的组件
puts "当前时间 : " + time.inspect
puts time.year # => 日期的年份
puts time.month # => 日期的月份(1 到 12)
puts time.day # => 一个月中的第几天(1 到 31)
puts time.wday # => 一周中的星期几(0 是星期日)
puts time.yday # => 365:一年中的第几天
puts time.hour # => 23:24 小时制
puts time.min # => 59
puts time.sec # => 59
puts time.usec # => 999999:微秒
puts time.zone # => "UTC":时区名称
# July 8, 2008
Time.local(2008, 7, 8)
# July 8, 2008, 09:10am,本地时间
Time.local(2008, 7, 8, 9, 10)
# July 8, 2008, 09:10 UTC
Time.utc(2008, 7, 8, 9, 10)
# July 8, 2008, 09:10:11 GMT (与 UTC 相同)
Time.gm(2008, 7, 8, 9, 10, 11)
time = Time.new
values = time.to_a
puts values
#[39, 25, 15, 17, 9, 2015, 4, 260, false, "CST"]
#[sec,min,hour,day,month,year,wday,yday,isdst,zone]
puts Time.utc(*values)
#2015-09-17 15:26:09 UTC
time = Time.new
# 这里是解释
time.zone # => "UTC":返回时区
time.utc_offset # => 0:UTC 是相对于 UTC 的 0 秒偏移
time.zone # => "PST"(或其他时区)
time.isdst # => false:如果 UTC 没有 DST(夏令时)
time.utc? # => true:如果在 UTC 时区
time.localtime # 转换为本地时区
time.gmtime # 转换回 UTC
time.getlocal # 返回本地区中的一个新的 Time 对象
time.getutc # 返回 UTC 中的一个新的 Time 对象
puts time.to_s
puts time.ctime
puts time.localtime
puts time.strftime("%Y-%m-%d %H:%M:%S")
#2015-09-17 15:26:42 +0800
#Thu Sep 17 15:26:42 2015
#2015-09-17 15:26:42 +0800
#2015-09-17 15:26:42
now = Time.now # 当前时间
puts now
past = now - 10 # 10 秒之前。Time - number => Time
puts past
future = now + 10 # 从现在开始 10 秒之后。Time + number => Time
puts future
diff = future - now # => 10 Time - Time => 秒数
puts diff
#2015-09-17 15:27:08 +0800
#2015-09-17 15:26:58 +0800
#2015-09-17 15:27:18 +0800
#10.0
时间格式化指令
指令 | 描述 |
---|---|
%a | 星期几名称的缩写(比如 Sun)。 |
%A | 星期几名称的全称(比如 Sunday)。 |
%b | 月份名称的缩写(比如 Jan)。 |
%B | 月份名称的全称(比如 January)。 |
%c | 优选的本地日期和时间表示法。 |
%d | 一个月中的第几天(01 到 31)。 |
%H | 一天中的第几小时,24 小时制(00 到 23)。 |
%I | 一天中的第几小时,12 小时制(01 到 12)。 |
%j | 一年中的第几天(001 到 366)。 |
%m | 一年中的第几月(01 到 12)。 |
%M | 小时中的第几分钟(00 到 59)。 |
%p | 子午线指示(AM 或 PM)。 |
%S | 分钟中的第几秒(00 或 60)。 |
%U | 当前年中的周数,从第一个星期日(作为第一周的第一天)开始(00 到 53)。 |
%W | 当前年中的周数,从第一个星期一(作为第一周的第一天)开始(00 到 53)。 |
%w | 一星期中的第几天(Sunday 是 0,0 到 6)。 |
%x | 只有日期没有时间的优先表示法。 |
%X | 只有时间没有日期的优先表示法。 |
%y | 不带世纪的年份表示(00 到 99)。 |
%Y | 带有世纪的年份。 |
%Z | 时区名称。 |
%% | % 字符。 |
异常处理
Ruby 提供了一个完美的处理异常的机制。我们可以在 begin/end 块中附上可能抛出异常的代码,并使用 rescue 子句告诉 Ruby 完美要处理的异常类型。
begin #开始
raise.. #抛出异常
rescue [ExceptionType = StandardException] #捕获指定类型的异常 缺省值是StandardException
$! #表示异常信息
$@ #表示异常出现的代码位置
else #其余异常
..
ensure #不管有没有异常,进入该代码块
end #结束
从 begin 到 rescue 中的一切是受保护的。如果代码块执行期间发生了异常,控制会传到 rescue 和 end 之间的块。
对于 begin 块中的每个 rescue 子句,Ruby 把抛出的异常与每个参数进行轮流比较。如果 rescue 子句中命名的异常与当前抛出的异常类型相同,或者是该异常的父类,则匹配成功。
如果异常不匹配所有指定的错误类型,我们可以在所有的 rescue 子句后使用一个 else 子句。
retry 语句
可以使用 rescue 块捕获异常,然后使用 retry 语句从开头开始执行 begin 块。
begin
# 这段代码抛出的异常将被下面的 rescue 子句捕获
rescue
# 这个块将捕获所有类型的异常
retry # 这将把控制移到 begin 的开头
end
有时候逻辑错误会导致代码无限尝试。所以异常处理时,谨慎使用 retry。
raise 语句
可以使用 raise 语句抛出异常。下面的方法在调用时抛出异常。它的第二个消息将被输出。
raise
或
raise "Error Message"
或
raise ExceptionType, "Error Message"
或
raise ExceptionType, "Error Message" condition
Catch 和 Throw
raise 和 rescue 的异常机制能在发生错误时放弃执行,有时候需要在正常处理时跳出一些深层嵌套的结构。此时 catch 和 throw 就派上用场了。
catch 定义了一个使用给定的名称(可以是 Symbol 或 String)作为标签的块。块会正常执行知道遇到一个 throw。
def promptAndGet(prompt)
print prompt
res = readline.chomp
throw :quitRequested if res == "!"
return res
end
catch :quitRequested do
name = promptAndGet("Name: ")
age = promptAndGet("Age: ")
sex = promptAndGet("Sex: ")
# ..
# 处理信息
end
promptAndGet("Name:")
类 Exception
Ruby 的标准类和模块抛出异常。所有的异常类组成一个层次,包括顶部的 Exception 类在内。下一层是七种不同的类型:
- Interrupt
- NoMemoryError
- SignalException
- ScriptError
- StandardError
- SystemExit
Fatal 是该层中另一种异常,但是 Ruby 解释器只在内部使用它。
ScriptError 和 StandardError 都有一些子类,最重要的事情是创建我们自己的异常类,它们必须是类 Exception 或其子代的子类。
class FileSaveError < StandardError
attr_reader :reason
def initialize(reason)
@reason = reason
end
end
参考
http://www.runoob.com/ruby/ruby-tutorial.html