Ruby 块
您已经知道 Ruby 如何定义方法以及您如何调用方法。类似地,Ruby 有一个块的概念。
- 块由大量的代码组成。
- 您需要给块取个名称。
- 块中的代码总是包含在大括号 {} 内。
- 块总是从与其具有相同名称的函数调用。这意味着如果您的块名称为 test,那么您要使用函数 test 来调用这个块。
- 您可以使用
yield
语句来调用块。
语法
block_name{
statement1
statement2
..........
}
在这里,您将学到如何使用一个简单的yield
语句来调用块。您也将学到如何使用带有参数的yield
语句来调用块。在实例中,您将看到这两种类型的 yield
语句。
yield 语句
让我们看一个 yield 语句的实例:
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
def test
puts "在 test 方法内"
yield
puts "你又回到了 test 方法内"
yield
end
test {puts "你在块内"}
以上实例运行结果为:
在 test 方法内
你在块内
你又回到了 test 方法内
你在块内
您也可以传递带有参数的 yield 语句。下面是一个实例:
实例
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
def test
yield 5
puts "在 test 方法内"
yield 100
end
test {|i| puts "你在块 #{i} 内"}
以上实例运行结果为:
你在块 5 内
在 test 方法内
你在块 100 内
在这里,yield
语句后跟着参数。您甚至可以传递多个参数。
在块中,您可以在两个竖线之间放置一个变量来接受参数。
因此,在上面的代码中,yield 5 语句向 test 块传递值 5 作为参数。
现在,看下面的语句:
test {|i| puts "你在块 #{i} 内"}
在这里,值 5 会在变量 i 中收到。现在,观察下面的 puts 语句:
puts "你在块 #{i} 内"
这个 puts 语句的输出是:
你在块5 内
如果您想要传递多个参数,那么 yield 语句如下所示:
yield a, b
此时,块如下所示:
test {|a, b| statement}
参数使用逗号分隔。
块和方法
您已经看到块和方法之间是如何相互关联的。您通常使用 yield
语句从与其具有相同名称的方法调用块。因此,代码如下所示:
#!/usr/bin/ruby
def test
yield
end
test{ puts "Hello world"}
本实例是实现块的最简单的方式。您使用yield
语句调用 test
块。
但是如果方法的最后一个参数前带有 &,那么您可以向该方法传递一个块,且这个块可被赋给最后一个参数。如果 * 和 & 同时出现在参数列表中,& 应放在后面。
实例
#!/usr/bin/ruby
def test(&block)
block.call
end
test { puts "Hello World!"}
尝试一下 »
以上实例运行结果为:
Hello World!
BEGIN 和 END 块
每个 Ruby
源文件可以声明当文件被加载时要运行的代码块(BEGIN 块),以及程序完成执行后要运行的代码块(END 块)。
实例
#!/usr/bin/ruby
BEGIN {
# BEGIN 代码块
puts "BEGIN 代码块"
}
END {
# END 代码块
puts "END 代码块"
}
# MAIN 代码块
puts "MAIN 代码块"
一个程序可以包含多个 BEGIN 和 END 块。
BEGIN 块按照它们出现的顺序执行。
END 块按照它们出现的相反顺序执行。
当执行时,上面的程序输出以下结果:
BEGIN 代码块
MAIN 代码块
END 代码块
模块常量命名与类常量命名类似,以大写字母开头。
方法定义看起来也相似:模块方法定义与类方法定义类似。
通过类方法,您可以在类方法名称前面放置模块名称和一个点号来调用模块方法,您可以使用模块名称和两个冒号来引用一个常量。
#!/usr/bin/ruby
# 定义在 trig.rb 文件中的模块
module Trig
PI = 3.141592654
def Trig.sin(x)
# ..
end
def Trig.cos(x)
# ..
end
end
我们可以定义多个函数名称相同但是功能不同的模块:
实例
#!/usr/bin/ruby
# 定义在 moral.rb 文件中的模块
module Moral
VERY_BAD = 0
BAD = 1
def Moral.sin(badness)
# ...
end
end
就像类方法,当您在模块中定义一个方法时,您可以指定在模块名称后跟着一个点号,点号后跟着方法名。
Ruby require 语句
require
语句类似于 C
和 C++
中的 include
语句以及 Java
中的 import
语句。如果一个第三方的程序想要使用任何已定义的模块,则可以简单地使用 Ruby require 语句来加载模块文件:
语法
require filename
在这里,文件扩展名 .rb 不是必需的。
实例
$LOAD_PATH << '.'
require 'trig.rb'
require 'moral'
y = Trig.sin(Trig::PI/4)
wrongdoing = Moral.sin(Moral::VERY_BAD)
在这里,我们使用$LOAD_PATH << '.'
让 Ruby 知道必须在当前目录中搜索被引用的文件。
如果您不想使用$LOAD_PATH
,那么您可以使用 require_relative
来从一个相对目录引用文件。
注意:在这里,文件包含相同的函数名称。
所以,这会在引用调用程序时导致代码模糊,但是模块避免了这种代码模糊,而且我们可以使用模块的名称调用适当的函数。
Ruby include 语句
您可以在类中嵌入模块。为了在类中嵌入模块,您可以在类中使用 include 语句:
语法
include modulename
如果模块是定义在一个单独的文件中,那么在嵌入模块之前就需要使用 require 语句引用该文件。
实例
假设下面的模块写在 support.rb 文件中。
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
Ruby 中的 Mixins
在阅读本节之前,您需要初步了解面向对象的概念。
当一个类可以从多个父类继承类的特性时,该类显示为多重继承
。
Ruby 不直接支持多重继承,但是 Ruby 的模块(Module)有另一个神奇的功能。
它几乎消除了多重继承的需要,提供了一种名为 mixin
的装置。
Ruby
没有真正实现多重继承机制,而是采用成为mixin
技术作为替代品。
将模块include到类定义中,模块中的方法就mix进了类中。
让我们看看下面的示例代码,深入了解 mixin
:
实例
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 字符串(String)
Ruby 中的 String
对象用于存储或操作一个或多个字节的序列。
Ruby 字符串分为单引号字符串(’)和双引号字符串("),区别在于双引号字符串能够支持更多的转义字符。
单引号字符串
最简单的字符串是单引号字符串,即在单引号内存放字符串:
'这是一个 Ruby 程序的字符串'
如果您需要在单引号字符串内使用单引号字符,那么需要在单引号字符串使用反斜杠(\
),这样 Ruby 解释器就不会认为这个单引号字符是字符串的终止符号:
'Won\'t you read O\'Reilly\'s book?'
反斜杠也能转义另一个反斜杠,这样第二个反斜杠本身不会解释为转义字符。
以下是 Ruby 中字符串相关的特性。
双引号字符串
在双引号字符串中我们可以使用 #{}
井号和大括号来计算表达式的值:
字符串中嵌入变量:
实例
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
name1 = "Joe"
name2 = "Mary"
puts "你好 #{name1}, #{name2} 在哪?"
以上实例输出运行输出结果为:
你好 Joe, Mary 在哪?
字符串中进行数学运算:
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
x, y, z = 12, 36, 72
puts "x 的值为 #{ x }"
puts "x + y 的值为 #{ x + y }"
puts "x + y + z 的平均值为 #{ (x + y + z)/3 }"
尝试一下 »
以上实例输出运行输出结果为:
x 的值为 12
x + y 的值为 48
x + y + z 的平均值为 40
Ruby 中还支持一种采用 %q
和%Q
来引导的字符串变量
%q
使用的是单引号引用规则,而 %Q
是双引号引用规则
后面再接一个(! [ { 等等的开始界定符和与 } ] )
等等的末尾界定符。
跟在 q
或 Q
后面的字符是分界符.
分界符可以是任意一个非字母数字的单字节字符.
如:[,{,(,<,!
等,字符串会一直读取到发现相匹配的结束符为止.
实例
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
desc1 = %Q{Ruby 的字符串可以使用 '' 和 ""。}
desc2 = %q|Ruby 的字符串可以使用 '' 和 ""。|
puts desc1
puts desc2
尝试一下 »
以上实例输出运行输出结果为:
Ruby 的字符串可以使用 '' 和 ""。
Ruby 的字符串可以使用 '' 和 ""。
转义字符
下标列出了可使用反斜杠符号转义的转义字符或非打印字符。
注意:
在一个双引号括起的字符串内,转义字符会被解析。
在一个单引号括起的字符串内,转义字符不会被解析,原样输出。
反斜杠符号 | 十六进制字符 | 描述 |
---|---|---|
\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'
下面是 $KCODE
可能的值。
编码 | 描述 |
---|---|
a | ASCII (与 none 相同)。这是默认的。 |
e | EUC。 |
n | None (与 ASCII 相同)。 |
u | UTF-8。 |
字符串内建方法
我们需要有一个 String
对象的实例来调用 String
方法。下面是创建 String
对象实例的方式:
new [String.new(str="")]
这将返回一个包含 str 副本的新的字符串对象。
现在,使用 str 对象,我们可以调用任意可用的实例方法。例如:
实例
#!/usr/bin/ruby
myStr = String.new("THIS IS TEST")
foo = myStr.downcase
puts "#{foo}"
这将产生以下结果:
this is test
下面是公共的字符串方法(假设 str 是一个 String 对象):
尝试下面的实例,解压各种数据。
"abc \0\0abc \0\0".unpack('A6Z6') #=> ["abc", "abc "]
"abc \0\0".unpack('a3a3') #=> ["abc", " \000\000"]
"abc \0abc \0".unpack('Z*Z*') #=> ["abc ", "abc "]
"aa".unpack('b8B8') #=> ["10000110", "01100001"]
"aaa".unpack('h2H2c') #=> ["16", "61", 97]
"\xfe\xff\xfe\xff".unpack('sS') #=> [-2, 65534]
"now=20is".unpack('M*') #=> ["now is"]
"whole".unpack('xax2aX2aX1aX2a') #=> ["h", "e", "l", "l", "o"]
Ruby 数组(Array)
Ruby 数组是任何对象的有序整数索引集合。数组中的每个元素都与一个索引相关,并可通过索引进行获取。
数组的索引从 0
开始,这与 C
或 Java
中一样。一个负数的索相对于数组的末尾计数的。
也就是说,索引为 -1 表示数组的最后一个元素,-2 表示数组中的倒数第二个元素,依此类推。
Ruby
数组可存储诸如 String
、 Integer
、 Fixnum
、 Hash
、 Symbol
等对象,甚至可以是其他 Array
对象。
Ruby 数组不需要指定大小,当向数组添加元素时,Ruby
数组会自动增长。
创建数组
有多种方式创建或初始化数组。一种方式是通过 new 类方法:
names = Array.new
您可以在创建数组的同时设置数组的大小:
names = Array.new(20)
数组 names 的大小或长度为 20 个元素。您可以使用 size 或 length 方法返回数组的大小:
实例
#!/usr/bin/ruby
names = Array.new(20)
puts names.size # 返回 20
puts names.length # 返回 20
以上实例运行输出结果为:
20
20
您可以给数组中的每个元素赋值,如下所示:
#!/usr/bin/ruby
names = Array.new(4, "mac")
puts "#{names}"
以上实例运行输出结果为:
["mac", "mac", "mac", "mac"]
您也可以使用带有 new
的块,每个元素使用块中的计算结果来填充:
实例
#!/usr/bin/ruby
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]
在 Ruby 核心模块中可以有一个只接收单个参数的 Array 方法,该方法使用一个范围作为参数来创建一个数字数组:
#!/usr/bin/ruby
digits = Array(0..9)
puts "#{digits}"
以上实例运行输出结果为:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
数组内建方法
我们需要有一个 Array 对象的实例来调用 Array 方法。下面是创建 Array 对象实例的方式:
Array.[](...) [or] Array[...] [or] [...]
这将返回一个使用给定对象进行填充的新数组。
现在,使用创建的对象,我们可以调用任意可用的方法。例如:
#!/usr/bin/ruby
digits = Array(0..9)
num = digits.at(6)
puts "#{num}"
以上实例运行输出结果为:
6
尝试下面的实例,压缩各种数据。
实例
a = [ "a", "b", "c" ]
n = [ 65, 66, 67 ]
puts a.pack("A3A3A3") #=> "a b c "
puts a.pack("a3a3a3") #=> "a\000\000b\000\000c\000\000"
puts n.pack("ccc") #=> "ABC"
以上实例运行输出结果为:
a b c
abc
ABC
Ruby 哈希(Hash)
哈希(Hash
)是类似 "key" => "value"
这样的键值对集合。哈希类似于一个数组,只不过它的索引不局限于使用数字。
Hash
的索引(或者叫"键")几乎可以是任何对象。
Hash
虽然和数组类似,但却有一个很重要的区别:Hash 的元素没有特定的顺序。 如果顺序很重要的话就要使用数组了。
创建哈希
与数组一样,有各种不同的方式来创建哈希。
您可以通过 new
类方法创建一个空的哈希:
months = Hash.new
您也可以使用 new
创建带有默认值的哈希,不带默认值的哈希是nil
:
months = Hash.new( "month" )
或
months = Hash.new "month"
当您访问带有默认值的哈希中的任意键时,如果键或值不存在,访问哈希将返回默认值:
#!/usr/bin/ruby
months = Hash.new( "month" )
puts "#{months[0]}"
puts "#{months[72]}"
以上实例运行输出结果为:
month
month
#!/usr/bin/ruby
H = Hash["a" => 100, "b" => 200]
puts "#{H['a']}"
puts "#{H['b']}"
以上实例运行输出结果为:
100
200
您可以使用任何的 Ruby
对象作为键或值,甚至可以使用数组,如下实例所示:
[1,"jan"] => "January"
哈希内置方法
如果需要调用 Hash
方法,需要先实例化一个 Hash
对象。下面是创建 Hash 对象实例的方式:
Hash[[key =>|, value]* ] or
Hash.new [or] Hash.new(obj) [or]
Hash.new { |hash, key| block }
这将返回一个使用给定对象进行填充的新的哈希。
现在,使用创建的对象,我们可以调用任意可用的方法。例如:
#!/usr/bin/ruby
$, = ", "
months = Hash.new( "month" )
months = {"1" => "January", "2" => "February"}
keys = months.keys
puts "#{keys}"
以上实例运行输出结果为:
["1", "2"]
Ruby 日期 & 时间(Date & Time)
Time
类在Ruby
中用于表示日期和时间。它是基于操作系统提供的系统日期和时间之上。该类可能无法表示 1970 年之前或者 2038 年之后的日期。
创建当前的日期和时间
下面是获取当前的日期和时间的简单实例:
#!/usr/bin/ruby -w
# -*- coding: UTF-8 -*-
time1 = Time.new
puts "当前时间 : " + time1.inspect
# Time.now 功能相同
time2 = Time.now
puts "当前时间 : " + time2.inspect
尝试一下 »
以上实例运行输出结果为:
当前时间 : 2015-09-17 15:23:14 +0800
当前时间 : 2015-09-17 15:23:14 +0800
获取 Date & Time 组件
我们可以使用 Time 对象来获取各种日期和时间的组件。请看下面的实例:
#!/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":时区名称
以上实例运行输出结果为:
当前时间 : 2015-09-17 15:24:44 +0800
2015
9
17
4
260
15
24
44
921519
CST
Time.utc、Time.gm 和 Time.local 函数
这些函数可用于格式化标准格式的日期,如下所示:
# 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)
下面的实例在数组中获取所有的组件:
[sec,min,hour,day,month,year,wday,yday,isdst,zone]
尝试下面的实例:
#!/usr/bin/ruby -w
time = Time.new
values = time.to_a
p values
以上实例运行输出结果为:
[39, 25, 15, 17, 9, 2015, 4, 260, false, "CST"]
该数组可被传到 Time.utc 或 Time.local 函数来获取日期的不同格式,如下所示:
#!/usr/bin/ruby -w
time = Time.new
values = time.to_a
puts Time.utc(*values)
以上实例运行输出结果为:
2015-09-17 15:26:09 UTC
下面是获取时间的方式,从纪元以来的秒数(平台相关):
# 返回从纪元以来的秒数
time = Time.now.to_i
# 把秒数转换为 Time 对象
Time.at(time)
# 返回从纪元以来的秒数,包含微妙
time = Time.now.to_f
时区和夏令时
您可以使用Time
对象来获取与时区和夏令时有关的所有信息,如下所示:
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 对象
格式化时间和日期
有多种方式格式化日期和时间。下面的实例演示了其中一部分:
#!/usr/bin/ruby -w
time = Time.new
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