Ruby初探

官方网站:https://www.ruby-lang.org/zh_cn/
标准库API文档:http://ruby-doc.org/stdlib-2.3.0/

简介

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。

  1. #!/usr/bin/ruby -w
  2. # -*- coding: UTF-8 -*-
  3. puts "你好,世界!";

语法

让我们编写一个简单的 Ruby 程序。所有的 Ruby 文件扩展名都是 .rb。所以,把下面的源代码放在 test.rb 文件中。

  1. #!/usr/bin/ruby -w
  2. 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” 是指建立多行字符串。在 << 之后,您可以指定一个字符串或标识符来终止字符串,且当前行之后直到终止符为止的所有行是字符串的值。
如果终止符用引号括起,引号的类型决定了面向行的字符串类型。请注意<< 和终止符之间必须没有空格。

  1. #!/usr/bin/ruby -w
  2. # -*- coding : utf-8 -*-
  3. print <<EOF
  4. 这是第一种方式创建here document
  5. 多行字符串。
  6. EOF
  7. print <<"EOF"; # 与上面相同
  8. 这是第二种方式创建here document
  9. 多行字符串。
  10. EOF
  11. print <<`EOC` # 执行命令
  12. echo hi there
  13. echo lo there
  14. EOC
  15. print <<"foo", <<"bar" # 您可以把它们进行堆叠
  16. I said foo.
  17. foo
  18. I said bar.
  19. bar

BEGIN与END 语句

  1. #!/usr/bin/ruby
  2. puts "This is main Ruby Program"
  3. END {
  4. puts "Terminating Ruby Program"
  5. }
  6. BEGIN {
  7. puts "Initializing Ruby Program"
  8. }
  9. Initializing Ruby Program
  10. This is main Ruby Program
  11. Terminating Ruby Program

注释

注释会对 Ruby 解释器隐藏一行,或者一行的一部分,或者若干行。您可以在行首使用字符( # )
或者,注释可以跟着语句或表达式的同一行的后面,
块注释会对解释器隐藏 =begin/=end 之间的行。

  1. # 我是注释,请忽略我。
  2. name = "Madisetti" # 这也是注释
  3. # 这是注释。
  4. # 这也是注释。
  5. # 这也是注释。
  6. # 这还是注释。
  7. =begin
  8. 这是注释。
  9. 这也是注释。
  10. 这也是注释。
  11. 这还是注释。
  12. =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

并行赋值

  1. a, b, c = 10, 20, 30
  2. #并行赋值在交换两个变量的值时也很有用
  3. 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 类。

  1. CONST = ' out there'
  2. class Inside_one
  3. CONST = proc {' in there'}
  4. def where_is_my_CONST
  5. ::CONST + ' inside one'
  6. end
  7. end
  8. class Inside_two
  9. CONST = ' inside two'
  10. def where_is_my_CONST
  11. CONST
  12. end
  13. end
  14. puts Inside_one.new.where_is_my_CONST
  15. puts Inside_two.new.where_is_my_CONST
  16. puts Object::CONST + Inside_two::CONST
  17. puts Inside_two::CONST + CONST
  18. puts Inside_one::CONST
  19. 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 字符或一个用问号标记的转义序列的整数值。

  1. 123 # Fixnum 十进制
  2. 1_234 # Fixnum 带有下划线的十进制
  3. -500 # 负的 Fixnum
  4. 0377 # 八进制
  5. 0xff # 十六进制
  6. 0b1011 # 二进制
  7. "a".ord # "a" 的字符编码
  8. ?\n # 换行符(0x0a)的编码
  9. 12345678901234567890 # Bignum

浮点型
Ruby 支持浮点数。它们是带有小数的数字。浮点数是类 Float 的对象,且可以是下列中任意一个。

  1. 123.4 # 浮点值
  2. 1.0e6 # 科学记数法
  3. 4E20 # 不是必需的
  4. 4e+20 # 指数前的符号

字符串(String)

Ruby 中的 String 对象用于存储或操作一个或多个字节的序列。
Ruby 字符串分为单引号字符串(’)和双引号字符串(”),区别在于双引号字符串能够支持更多的转义字符。

  1. #!/usr/bin/ruby -w
  2. puts 'escape using "\\"';
  3. puts 'That\'s right';
  4. escape using "\"
  5. That's right

可以使用序列 #{ expr } 替换任意 Ruby 表达式的值为一个字符串。在这里,expr 可以是任意的 Ruby 表达式。

  1. puts "Multiplication Value : #{24*60*60}";
  2. Multiplication Value : 86400
  3. name="Ruby"
  4. puts name
  5. puts "#{name+",ok"}"
  6. Ruby
  7. 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 数组会自动增长。

  1. names = Array.new
  2. names = Array.new(20) #创建数组的同时设置数组的大小
  3. puts names.size # 返回 20
  4. puts names.length # 返回 20
  5. names = Array.new(4, "mac")
  6. puts "#{names}"
  7. #["mac", "mac", "mac", "mac"]
  8. nums = Array.new(10) { |e| e = e * 2 }
  9. puts "#{nums}"
  10. #[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
  11. nums = Array[1, 2, 3, 4,5]
  12. nums = Array.[](1, 2, 3, 4,5)
  13. digits = Array(0..9)
  14. puts "#{digits}"
  15. #[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

哈希(Hash)

哈希(Hash)是类似 “key” => “value” 这样的键值对集合。哈希类似于一个数组,只不过它的索引不局限于使用数字。
Hash 的索引(或者叫”键”)几乎可以是任何对象。
Hash 虽然和数组类似,但却有一个很重要的区别:Hash 的元素没有特定的顺序。 如果顺序很重要的话就要使用数组了。

  1. months = Hash.new
  2. # 创建带有默认值的哈希
  3. months = Hash.new( "month" )
  4. months = Hash.new "month"
  5. #当您访问带有默认值的哈希中的任意键时,如果键或值不存在,访问哈希将返回默认值
  6. puts "#{months[0]}"
  7. puts "#{months[72]}"
  8. #month
  9. #month
  10. H = Hash["a" => 100, "b" => 200]
  11. puts "#{H['a']}"
  12. puts "#{H['b']}"
  13. #100
  14. #200

范围(Range)

Ruby 支持范围,并允许我们以不同的方式使用范围:

  • 作为序列的范围
  • 作为条件的范围
  • 作为间隔的范围
  1. (1..5) #==> 1, 2, 3, 4, 5
  2. (1...5) #==> 1, 2, 3, 4
  3. ('a'..'d') #==> 'a', 'b', 'c', 'd'
  4. #可以使用 to_a 方法把范围转换为列表。
  5. range1 = (1..10).to_a #[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  6. #可以通过多种方式检查它们的内容
  7. digits = 0..9
  8. puts digits.include?(5)
  9. ret = digits.min
  10. puts "最小值为 #{ret}"
  11. ret = digits.max
  12. puts "最大值为 #{ret}"
  13. ret = digits.reject {|i| i < 5 }
  14. puts "不符合条件的有 #{ret}"
  15. digits.each do |digit|
  16. puts "在循环中 #{digit}"
  17. end
  18. #其中每个集合的第一行包含单词 start,最后一行包含单词 end.
  19. while gets
  20. print if /start/../end/
  21. end
  22. score = 70
  23. result = case score
  24. when 0..40
  25. "糟糕的分数"
  26. when 41..60
  27. "快要及格"
  28. when 61..70
  29. "及格分数"
  30. when 71..100
  31. "良好分数"
  32. else
  33. "错误的分数"
  34. end
  35. puts result
  36. #检查指定值是否在指定的范围内。需要使用 === 相等运算符来完成计算
  37. if ((1..10) === 5)
  38. puts "5 在 (1..10)"
  39. end
  40. if (('a'..'j') === 'c')
  41. puts "c 在 ('a'..'j')"
  42. end
  43. if (('a'..'j') === 'z')
  44. puts "z 在 ('a'..'j')"
  45. end

迭代器(iterator)

迭代器是集合支持的方法。存储一组数据成员的对象称为集合。在 Ruby 中,数组(Array)和哈希(Hash)可以称之为集合。
迭代器返回集合的所有元素,一个接着一个。

each 迭代器

  1. #each 迭代器总是与一个块关联。它向块返回数组的每个值,一个接着一个。值被存储在变量 i 中
  2. ary = [1,2,3,4,5]
  3. ary.each do |i|
  4. puts i
  5. end
  6. #当您想要对每个值进行一些操作以便获得新的数组时,您通常使用 collect 方法。例如,下面的代码会生成一个数组,其值是 a 中每个值的 10 倍。
  7. a = [1,2,3,4,5]
  8. b = a.collect{|x| 10*x}
  9. puts b

collect 迭代器

collect 迭代器返回集合的所有元素。

  1. a = [1,2,3,4,5]
  2. b = Array.new
  3. b = a.collect{ |x|x }
  4. puts b

条件判断与循环

  1. #!/usr/bin/ruby
  2. # -*- coding: UTF-8 -*-
  3. #if 表达式用于条件执行。值 false 和 nil 为假,其他值都为真。请注意,Ruby 使用 elsif,不是使用 else if 和 elif。
  4. #如果 conditional 为真,则执行 code。如果 conditional 不为真,则执行 else 子句中指定的 code。
  5. #通常我们省略保留字 then 。若想在一行内写出完整的 if 式,则必须以 then 隔开条件式和程式区块。
  6. x=1
  7. if x > 2
  8. puts "x 大于 2"
  9. elsif x <= 2 and x!=0
  10. puts "x 是 1"
  11. else
  12. puts "无法得知 x 的值"
  13. end
  14. #if修饰词组表示当 if 右边之条件成立时才执行 if 左边的式子。即如果 conditional 为真,则执行 code。
  15. $debug=1
  16. print "debug\n" if $debug
  17. #unless式和 if式作用相反,即如果 conditional 为假,则执行 code。如果 conditional 为真,则执行 else 子句中指定的 code。
  18. $var = 1
  19. print "1 -- 这一行输出\n" if $var
  20. print "2 -- 这一行不输出\n" unless $var
  21. $var = false
  22. print "3 -- 这一行输出\n" unless $var
  23. #unless式和 if式作用相反,即如果 conditional 为假,则执行 code。如果 conditional 为真,则执行 else 子句中指定的 code。
  24. x=1
  25. unless x>2
  26. puts "x 小于 2"
  27. else
  28. puts "x 大于 2"
  29. end
  30. #case先对一个 expression 进行匹配判断,然后根据匹配结果进行分支选择。
  31. #它使用 ===运算符比较 when 指定的 expression,若一致的话就执行 when 部分的内容。
  32. #通常我们省略保留字 then 。若想在一行内写出完整的 when 式,则必须以 then 隔开条件式和程式区块。
  33. $age = 5
  34. case $age
  35. when 0 .. 2
  36. puts "婴儿"
  37. when 3 .. 6
  38. puts "小孩"
  39. when 7 .. 12
  40. puts "child"
  41. when 13 .. 18
  42. puts "少年"
  43. else
  44. puts "其他年龄段的"
  45. end
  46. #语法中 do 或 : 可以省略不写。但若要在一行内写出 while 式,则必须以 do 或 : 隔开条件式或程式区块。
  47. while $i < $num do
  48. puts("在循环语句中 i = #$i" )
  49. $i +=1
  50. end
  51. #当 conditional 为真时,执行 code。
  52. #如果 while 修饰符跟在一个没有 rescue 或 ensure 子句的 begin 语句后面,code 会在 conditional 判断之前执行一次。
  53. $i = 0
  54. $num = 5
  55. begin
  56. puts("在循环语句中 i = #$i" )
  57. $i +=1
  58. end while $i < $num
  59. #当 conditional 为 false 时,执行 code。
  60. #语法中 do 可以省略不写。但若要在一行内写出 until 式,则必须以 do 隔开条件式或程式区块。
  61. $i = 0
  62. $num = 5
  63. until $i > $num do
  64. puts("在循环语句中 i = #$i" )
  65. $i +=1;
  66. end
  67. #如果 until 修饰符跟在一个没有 rescue 或 ensure 子句的 begin 语句后面,code 会在 conditional 判断之前执行一次。
  68. $i = 0
  69. $num = 5
  70. begin
  71. puts("在循环语句中 i = #$i" )
  72. $i +=1;
  73. end until $i > $num
  74. #先计算表达式得到一个对象,然后针对 expression 中的每个元素分别执行一次 code。
  75. for i in 0..5
  76. puts "局部变量的值为 #{i}"
  77. end
  78. #注意条件内的循环终止关键字~
  79. for i in 0..5
  80. if i > 2 then
  81. break #终止最内部的循环。如果在块内调用,则终止相关块的方法(方法返回 nil)。
  82. next #跳到循环的下一个迭代。如果在块内调用,则终止块的执行(yield 表达式返回 nil)。
  83. redo #重新开始最内部循环的该次迭代,不检查循环条件。如果在块内调用,则重新开始 yield 或 call。
  84. retry if i > 2 #如果 retry 出现在迭代内、块内或者 for 表达式的主体内,则重新开始迭代调用。迭代的参数会重新评估。
  85. end
  86. puts "局部变量的值为 #{i}"
  87. end

变量

变量是持有可被任何程序使用的任何数据的存储位置。
Ruby 支持五种类型的变量。

  • 一般小写字母、下划线开头:变量(Variable)。
  • $开头:全局变量(Global variable)。
  • @开头:实例变量(Instance variable)。
  • @@开头:类变量(Class variable)类变量被共享在整个继承链中,引用一个未初始化的类变量会产生错误。类变量在定义它的类或模块的子类或子模块中可共享使用。
  • 大写字母开头:常量(Constant)。常量不能定义在方法内。引用一个未初始化的常量会产生错误。对已经初始化的常量赋值会产生警告。

方法

Ruby 方法与其他编程语言中的函数类似。Ruby 方法用于捆绑一个或多个重复的语句到一个单元中。
方法名应以小写字母开头。如果您以大写字母作为方法名的开头,Ruby 可能会把它当作常量,从而导致不正确地解析调用。
方法应在调用之前定义,否则 Ruby 会产生未定义的方法调用异常。
Ruby 中的每个方法默认都会返回一个值。这个返回的值是最后一个语句的值。

  1. #!/usr/bin/ruby
  2. # -*- coding: UTF-8 -*-
  3. def test
  4. i = 100
  5. j = 200
  6. k = 300
  7. return i, j, k
  8. end
  9. var = test
  10. puts var
  11. #Ruby 允许您声明参数数量可变的方法。
  12. def sample (*test)
  13. puts "参数个数为 #{test.length}"
  14. for i in 0...test.length
  15. puts "参数值为 #{test[i]}"
  16. end
  17. end
  18. sample "Zara", "6", "F"
  19. sample "Mac", "36", "M", "MCA"

类方法

Ruby 提供了一种不用实例化即可访问方法的方式。让我们看看如何声明并访问类方法

  1. class Accounts
  2. def reading_charge
  3. end
  4. def Accounts.return_date
  5. end
  6. end
  7. #如需访问该方法,您不需要创建类 Accounts 的对象。
  8. Accounts.return_date

alias 语句与undef 语句

alias 用于为方法或全局变量起别名。别名不能在方法主体内定义。即使方法被重写,方法的别名也保持方法的当前定义。
为编号的全局变量(1,2,…)起别名是被禁止的。重写内置的全局变量可能会导致严重的问题。

undef 用于取消方法定义。undef 不能出现在方法主体内。
通过使用 undef 和 alias,类的接口可以从父类独立修改,但请注意,在自身内部方法调用时,它可能会破坏程序。

Ruby 有一个块的概念。

  • 块由大量的代码组成。
  • 您需要给块取个名称。
  • 块中的代码总是包含在大括号 {} 内。
  • 块总是从与其具有相同名称的函数调用。这意味着如果您的块名称为 test,那么您要使用函数 test 来调用这个块。
  • 您可以使用 yield 语句来调用块。
  1. #!/usr/bin/ruby
  2. # -*- coding: UTF-8 -*-
  3. def test
  4. puts "在 test 方法内"
  5. yield
  6. puts "你又回到了 test 方法内"
  7. yield
  8. end
  9. test {puts "你在块内"}
  10. test 方法内
  11. 你在块内
  12. 你又回到了 test 方法内
  13. 你在块内
  14. def test
  15. yield 5
  16. puts "在 test 方法内"
  17. yield 100,2
  18. end
  19. test {|i,j| puts "你在块 #{i} 内"}
  20. 你在块 5
  21. test 方法内
  22. 你在块 100
  23. #如果方法的最后一个参数前带有 &,那么您可以向该方法传递一个块,且这个块可被赋给最后一个参数。如果 * 和 & 同时出现在参数列表中,& 应放在后面。
  24. def test(&block)
  25. block.call
  26. end
  27. test { puts "Hello World!"}

BEGIN 和 END 块

  1. #!/usr/bin/ruby
  2. BEGIN {
  3. # BEGIN 代码块
  4. puts "BEGIN 代码块"
  5. }
  6. END {
  7. # END 代码块
  8. puts "END 代码块"
  9. }
  10. # MAIN 代码块
  11. puts "MAIN 代码块"
  12. BEGIN 代码块
  13. MAIN 代码块
  14. END 代码块

模块(Module)

模块(Module)是一种把方法、类和常量组合在一起的方式。模块(Module)为您提供了两大好处。

  • 模块提供了一个命名空间和避免名字冲突。
  • 模块实现了 mixin 装置。
    模块(Module)定义了一个命名空间,相当于一个沙盒,在里边您的方法和常量不会与其他地方的方法常量冲突。
    模块类似与类,但有一下不同:
  • 模块不能实例化
  • 模块没有子类
  • 模块只能被另一个模块定义

通过类方法,您可以在类方法名称前面放置模块名称和一个点号来调用模块方法,您可以使用模块名称和两个冒号来引用一个常量。

  1. #!/usr/bin/ruby
  2. # 定义在 trig.rb 文件中的模块
  3. module Trig
  4. PI = 3.141592654
  5. def Trig.sin(x)
  6. # ..
  7. end
  8. def Trig.cos(x)
  9. # ..
  10. end
  11. end

require 语句

require 语句类似于 C 和 C++ 中的 include 语句以及 Java 中的 import 语句。如果一个第三方的程序想要使用任何已定义的模块,则可以简单地使用 Ruby require 语句来加载模块文件:

  1. #我们使用 $LOAD_PATH << '.' 让 Ruby 知道必须在当前目录中搜索被引用的文件。如果您不想使用 $LOAD_PATH,那么您可以使用 require_relative 来从一个相对目录引用文件。
  2. $LOAD_PATH << '.'
  3. require 'trig.rb'
  4. require 'moral' #在这里,文件扩展名 .rb 不是必需的。
  5. y = Trig.sin(Trig::PI/4)
  6. wrongdoing = Moral.sin(Moral::VERY_BAD)

include 语句

您可以在类中嵌入模块。为了在类中嵌入模块,您可以在类中使用 include 语句
如果模块是定义在一个单独的文件中,那么在嵌入模块之前就需要使用 require 语句引用该文件。

  1. module Week
  2. FIRST_DAY = "Sunday"
  3. def Week.weeks_in_month
  4. puts "You have four weeks in a month"
  5. end
  6. def Week.weeks_in_year
  7. puts "You have 52 weeks in a year"
  8. end
  9. end
  1. #!/usr/bin/ruby
  2. $LOAD_PATH << '.'
  3. require "support"
  4. class Decade
  5. include Week
  6. no_of_yrs=10
  7. def no_of_months
  8. puts Week::FIRST_DAY
  9. number=10*12
  10. puts number
  11. end
  12. end
  13. d1=Decade.new
  14. puts Week::FIRST_DAY
  15. Week.weeks_in_month
  16. Week.weeks_in_year
  17. d1.no_of_months
  18. #Sunday
  19. #You have four weeks in a month
  20. #You have 52 weeks in a year
  21. #Sunday
  22. #120

Mixins

Ruby 不直接支持多重继承,但是 Ruby 的模块(Module)有另一个神奇的功能。它几乎消除了多重继承的需要,提供了一种名为 mixin 的装置。
Ruby 没有真正实现多重继承机制,而是采用成为mixin技术作为替代品。将模块include到类定义中,模块中的方法就mix进了类中。

  1. module A
  2. def a1
  3. end
  4. def a2
  5. end
  6. end
  7. module B
  8. def b1
  9. end
  10. def b2
  11. end
  12. end
  13. class Sample
  14. include A
  15. include B
  16. def s1
  17. end
  18. end
  19. samp=Sample.new
  20. samp.a1
  21. samp.a2
  22. samp.b1
  23. samp.b2
  24. 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 类的一个实例。
类用于指定对象的形式,它结合了数据表示法和方法,把数据整理成一个整齐的包。类中的数据和方法被称为类的成员。

类定义

  1. class Customer
  2. @@no_of_customers=0
  3. #自定义方法来创建 Ruby 对象
  4. def initialize(id, name, addr)
  5. @cust_id=id
  6. @cust_name=name
  7. @cust_addr=addr
  8. end
  9. #成员函数
  10. def hello
  11. puts "Hello Ruby!"
  12. end
  13. end
  14. cust1=Customer.new("1", "John", "Wisdom Apartments, Ludhiya")
  15. cust2=Customer.new("2", "Poul", "New Empire road, Khandala")
  16. cus1.hello

访问器(accessor) & 设置器(setter)方法

  1. #!/usr/bin/ruby -w
  2. # 定义类
  3. class Box
  4. # 构造器方法
  5. def initialize(w,h)
  6. @width, @height = w, h
  7. end
  8. # 访问器方法
  9. def getWidth
  10. @width
  11. end
  12. def getHeight
  13. @height
  14. end
  15. # 设置器方法
  16. def setWidth=(value)
  17. @width = value
  18. end
  19. def setHeight=(value)
  20. @height = value
  21. end
  22. end
  23. # 创建对象
  24. box = Box.new(10, 20)
  25. # 使用设置器方法
  26. box.setWidth = 30
  27. box.setHeight = 50
  28. # 使用访问器方法
  29. x = box.getWidth()
  30. y = box.getHeight()
  31. puts "Width of the box is : #{x}"
  32. 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 方法只能被类及其子类的对象调用。访问也只能在类及其子类内部进行。
  1. # 定义类
  2. class Box
  3. # 构造器方法
  4. def initialize(w,h)
  5. @width, @height = w, h
  6. end
  7. # 实例方法默认是 public 的
  8. def getArea
  9. getWidth() * getHeight
  10. end
  11. # 定义 private 的访问器方法
  12. def getWidth
  13. @width
  14. end
  15. def getHeight
  16. @height
  17. end
  18. # make them private
  19. private :getWidth, :getHeight
  20. # 用于输出面积的实例方法
  21. def printArea
  22. @area = getWidth() * getHeight
  23. puts "Big box area is : #@area"
  24. end
  25. # 让实例方法是 protected 的
  26. protected :printArea
  27. end
  28. # 创建对象
  29. box = Box.new(10, 20)
  30. # 调用实例方法
  31. a = box.getArea()
  32. puts "Area of the box is : #{a}"
  33. # 尝试调用 protected 的实例方法
  34. box.printArea()

继承与重载

Ruby 不支持多继承,但是 Ruby 支持 mixins。mixin 就像是多继承的一个特定实现,在多继承中,只有接口部分是可继承的。
当创建类时,程序员可以直接指定新类继承自某个已有类的成员,这样就不用从头编写新的数据成员和成员函数。这个已有类被称为基类或父类,新类被称为派生类或子类。
Ruby 也提供了子类化的概念,子类化即继承,下面的实例解释了这个概念。扩展一个类的语法非常简单。只要添加一个 < 字符和父类的名称到类语句中即可。

  1. #!/usr/bin/ruby -w
  2. # 定义类
  3. class Box
  4. # 构造器方法
  5. def initialize(w,h)
  6. @width, @height = w, h
  7. end
  8. # 实例方法
  9. def getArea
  10. @width * @height
  11. end
  12. end
  13. # 定义子类
  14. class BigBox < Box
  15. # 添加一个新的实例方法
  16. def printArea
  17. @area = @width * @height
  18. puts "Big box area is : #@area"
  19. end
  20. # 改变已有的 getArea 方法
  21. def getArea
  22. @area = @width * @height
  23. puts "Big box area is : #@area"
  24. end
  25. end
  26. # 创建对象
  27. box = BigBox.new(10, 20)
  28. # 输出面积
  29. box.printArea()
  30. box.getArea()
  31. #运算符重载
  32. class Box
  33. def initialize(w,h) # 初始化 width 和 height
  34. @width,@height = w, h
  35. end
  36. def +(other) # 定义 + 来执行向量加法
  37. Box.new(@width + other.width, @height + other.height)
  38. end
  39. def -@ # 定义一元运算符 - 来对 width 和 height 求反
  40. Box.new(-@width, -@height)
  41. end
  42. def *(scalar) # 执行标量乘法
  43. Box.new(@width*scalar, @height*scalar)
  44. end
  45. end

冻结对象

有时候,我们想要防止对象被改变。在 Object 中,freeze 方法可实现这点,它能有效地把一个对象变成一个常量。任何对象都可以通过调用 Object.freeze 进行冻结。冻结对象不能被修改,也就是说,您不能改变它的实例变量。
您可以使用 Object.frozen? 方法检查一个给定的对象是否已经被冻结。如果对象已被冻结,该方法将返回 true,否则返回一个 false 值。

  1. # 创建对象
  2. box = Box.new(10, 20)
  3. # 让我们冻结该对象
  4. box.freeze
  5. if( box.frozen? )
  6. puts "Box object is frozen object"
  7. else
  8. puts "Box object is normal object"
  9. end

allocate 创建对象

可能有一种情况,您想要在不调用对象构造器 initialize 的情况下创建对象,即,使用 new 方法创建对象,在这种情况下,您可以调用 allocate 来创建一个未初始化的对象

  1. # 使用 allocate 创建两一个对象
  2. box2 = Box.allocate
  3. # 使用 box2 调用实例方法
  4. a = box2.getArea()
  5. #test.rb:14: warning: instance variable @width not initialized
  6. #test.rb:14: warning: instance variable @height not initialized

类信息

Ruby的 self 和 Java 的 this 有相似之处,但又大不相同。Java的方法都是在实例方法中引用,所以this一般都是指向当前对象的。而Ruby的代码逐行执行,所以在不同的上下文(context)self就有了不同的含义。

  1. class Box
  2. # 输出类信息
  3. puts "Class of self = #{self.class}"
  4. puts "Name of self = #{self.name}"
  5. end

日期与时间

Time 类在 Ruby 中用于表示日期和时间。它是基于操作系统提供的系统日期和时间之上。该类可能无法表示 1970 年之前或者 2038 年之后的日期。

  1. #!/usr/bin/ruby -w
  2. # -*- coding: UTF-8 -*-
  3. time = Time.new
  4. # Time 的组件
  5. puts "当前时间 : " + time.inspect
  6. puts time.year # => 日期的年份
  7. puts time.month # => 日期的月份(1 到 12)
  8. puts time.day # => 一个月中的第几天(1 到 31)
  9. puts time.wday # => 一周中的星期几(0 是星期日)
  10. puts time.yday # => 365:一年中的第几天
  11. puts time.hour # => 23:24 小时制
  12. puts time.min # => 59
  13. puts time.sec # => 59
  14. puts time.usec # => 999999:微秒
  15. puts time.zone # => "UTC":时区名称
  16. # July 8, 2008
  17. Time.local(2008, 7, 8)
  18. # July 8, 2008, 09:10am,本地时间
  19. Time.local(2008, 7, 8, 9, 10)
  20. # July 8, 2008, 09:10 UTC
  21. Time.utc(2008, 7, 8, 9, 10)
  22. # July 8, 2008, 09:10:11 GMT (与 UTC 相同)
  23. Time.gm(2008, 7, 8, 9, 10, 11)
  24. time = Time.new
  25. values = time.to_a
  26. puts values
  27. #[39, 25, 15, 17, 9, 2015, 4, 260, false, "CST"]
  28. #[sec,min,hour,day,month,year,wday,yday,isdst,zone]
  29. puts Time.utc(*values)
  30. #2015-09-17 15:26:09 UTC
  31. time = Time.new
  32. # 这里是解释
  33. time.zone # => "UTC":返回时区
  34. time.utc_offset # => 0:UTC 是相对于 UTC 的 0 秒偏移
  35. time.zone # => "PST"(或其他时区)
  36. time.isdst # => false:如果 UTC 没有 DST(夏令时)
  37. time.utc? # => true:如果在 UTC 时区
  38. time.localtime # 转换为本地时区
  39. time.gmtime # 转换回 UTC
  40. time.getlocal # 返回本地区中的一个新的 Time 对象
  41. time.getutc # 返回 UTC 中的一个新的 Time 对象
  42. puts time.to_s
  43. puts time.ctime
  44. puts time.localtime
  45. puts time.strftime("%Y-%m-%d %H:%M:%S")
  46. #2015-09-17 15:26:42 +0800
  47. #Thu Sep 17 15:26:42 2015
  48. #2015-09-17 15:26:42 +0800
  49. #2015-09-17 15:26:42
  50. now = Time.now # 当前时间
  51. puts now
  52. past = now - 10 # 10 秒之前。Time - number => Time
  53. puts past
  54. future = now + 10 # 从现在开始 10 秒之后。Time + number => Time
  55. puts future
  56. diff = future - now # => 10 Time - Time => 秒数
  57. puts diff
  58. #2015-09-17 15:27:08 +0800
  59. #2015-09-17 15:26:58 +0800
  60. #2015-09-17 15:27:18 +0800
  61. #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 完美要处理的异常类型。

  1. begin #开始
  2. raise.. #抛出异常
  3. rescue [ExceptionType = StandardException] #捕获指定类型的异常 缺省值是StandardException
  4. $! #表示异常信息
  5. $@ #表示异常出现的代码位置
  6. else #其余异常
  7. ..
  8. ensure #不管有没有异常,进入该代码块
  9. end #结束

从 begin 到 rescue 中的一切是受保护的。如果代码块执行期间发生了异常,控制会传到 rescue 和 end 之间的块。
对于 begin 块中的每个 rescue 子句,Ruby 把抛出的异常与每个参数进行轮流比较。如果 rescue 子句中命名的异常与当前抛出的异常类型相同,或者是该异常的父类,则匹配成功。
如果异常不匹配所有指定的错误类型,我们可以在所有的 rescue 子句后使用一个 else 子句。

retry 语句

可以使用 rescue 块捕获异常,然后使用 retry 语句从开头开始执行 begin 块。

  1. begin
  2. # 这段代码抛出的异常将被下面的 rescue 子句捕获
  3. rescue
  4. # 这个块将捕获所有类型的异常
  5. retry # 这将把控制移到 begin 的开头
  6. end

有时候逻辑错误会导致代码无限尝试。所以异常处理时,谨慎使用 retry。

raise 语句

可以使用 raise 语句抛出异常。下面的方法在调用时抛出异常。它的第二个消息将被输出。

  1. raise
  2. raise "Error Message"
  3. raise ExceptionType, "Error Message"
  4. raise ExceptionType, "Error Message" condition

Catch 和 Throw

raise 和 rescue 的异常机制能在发生错误时放弃执行,有时候需要在正常处理时跳出一些深层嵌套的结构。此时 catch 和 throw 就派上用场了。
catch 定义了一个使用给定的名称(可以是 Symbol 或 String)作为标签的块。块会正常执行知道遇到一个 throw。

  1. def promptAndGet(prompt)
  2. print prompt
  3. res = readline.chomp
  4. throw :quitRequested if res == "!"
  5. return res
  6. end
  7. catch :quitRequested do
  8. name = promptAndGet("Name: ")
  9. age = promptAndGet("Age: ")
  10. sex = promptAndGet("Sex: ")
  11. # ..
  12. # 处理信息
  13. end
  14. promptAndGet("Name:")

类 Exception

Ruby 的标准类和模块抛出异常。所有的异常类组成一个层次,包括顶部的 Exception 类在内。下一层是七种不同的类型:

  • Interrupt
  • NoMemoryError
  • SignalException
  • ScriptError
  • StandardError
  • SystemExit

Fatal 是该层中另一种异常,但是 Ruby 解释器只在内部使用它。
ScriptError 和 StandardError 都有一些子类,最重要的事情是创建我们自己的异常类,它们必须是类 Exception 或其子代的子类。

  1. class FileSaveError < StandardError
  2. attr_reader :reason
  3. def initialize(reason)
  4. @reason = reason
  5. end
  6. end

参考

http://www.runoob.com/ruby/ruby-tutorial.html





posted @ 2016-03-16 12:32  leestar54  阅读(340)  评论(0编辑  收藏  举报