ruby note

link

w3cschool tutorial


简介
Ruby是一种完美的面向对象编程语言。面向对象编程语言的特性包括:

  • 数据封装
  • 数据抽象
  • 多态性
  • 继承


在 Ruby 中,类总是以关键字 class 开始,后跟类的名称。类名的首字母应该大写。类 Customer 如下所示:

class Customer
end

Ruby 类中的变量

  • 局部变量:局部变量是在方法中定义的变量。局部变量在方法外是不可用的。在后续的章节中,您将看到有关方法的更多细节。局部变量以小写字母或 _ 开始。
  • 实例变量:实例变量可以跨任何特定的实例或对象中的方法使用。这意味着,实例变量可以从对象到对象的改变。实例变量在变量名之前放置符号(@)。
  • 类变量:类变量可以跨不同的对象使用。类变量属于类,且是类的一个属性。类变量在变量名之前放置符号(@@)。
  • 全局变量:类变量不能跨类使用。如果您想要有一个可以跨类使用的变量,您需要定义全局变量。全局变量总是以美元符号($)开始。(给全局变量赋值会改变全局状态,所以不建议使用全局变量)

Ruby 点运算符 “.” 和双冒号运算符 “::”
您可以通过在方法名称前加上模块名称和一条下划线来调用模块方法。您可以使用模块名称和两个冒号来引用一个常量。

:: 是一元运算符,允许在类或模块内定义常量、实例方法和类方法,可以从类或模块外的任何地方进行访问。

请记住:在 Ruby 中,类和方法也可以被当作常量。

您只需要在表达式的常量名前加上 :: 前缀,即可返回适当的类或模块对象。

如果未使用前缀表达式,则默认使用主 Object 类。

下面是两个实例:

1
2
3
4
5
6
7
8
MR_COUNT = 0 # 定义在主 Object 类上的常量
module Foo
MR_COUNT = 0
::MR_COUNT = 1 # 设置全局计数为 1
MR_COUNT = 2 # 设置局部计数为 2
end
puts MR_COUNT # 这是全局常量
puts Foo::MR_COUNT # 这是 "Foo" 的局部常量

修饰词
if 为真,执行后面

1
2
3
4
5
6
7
8
x=1
if x > 2
puts "x is greater than 2"
elsif x <= 2 and x!=0
puts "x is 1"
else
puts "I can't guess the number"
end

unless 为假,执行后面

1
2
3
4
5
6
x=1
unless x>2
puts "x is less than 2"
else
puts "x is greater than 2"
end

case

1
2
3
4
5
6
7
8
case expr0
when expr1, expr2
stmt1
when expr3, expr4
stmt2
else
stmt3
end

while 为真,继续执行

1
2
3
4
5
6
i = 0
while $i < 5 do
puts("Inside the loop i = #i" )
i +=1
end

修饰符在后的用法
当 conditional 为真时,执行 code。

1
2
3
4
5
6
7
code while condition
或者
begin
code
end while conditional

until 为假,继续执行

= 1
1
2
3
4
5
until x > 4
puts x
x += 1
end

for

1
2
3
for i in 0..5
puts "Value of local variable is #{i}"
end

for…in 循环几乎是完全等价于:

1
(expression).each do |variable[, variable...]| code end

上例也可以写成

1
2
3
(0..5).each do |i|
puts "Value of local variable is #{i}"
end

break,终止最内部的循环。如果在块内调用,则终止相关块的方法(方法返回 nil)。

1
2
3
4
5
6
for i in 0..5
if i > 2 then
break
end
puts "Value of local variable is #{i}"
end

next,跳到最内部循环的下一个迭代。如果在块内调用,则终止块的执行(yield 或调用返回 nil)。

1
2
3
4
5
6
for i in 0..5
if i < 2 then
next
end
puts "Value of local variable is #{i}"
end

redo,重新开始最内部循环的该次迭代,不检查循环条件。如果在块内调用,则重新开始 yield 或 call。

1
2
3
4
5
6
7
for i in 0..5
if i < 2 then
puts "Value of local variable is #{i}"
redo
end
end
#这将产生以下结果,并会进入一个无限循环

retry,如果 retry 出现在 begin 表达式的 rescue 子句中,则从 begin 主体的开头重新开始。

1
2
3
4
5
6
begin
do_something # 抛出的异常
rescue
# 处理错误
retry # 重新从 begin 开始
end

如果 retry 出现在迭代内、块内或者 for 表达式的主体内,则重新开始迭代调用。迭代的参数会重新评估。

1
2
3
4
5
for i in 1..5
retry if i > 2
puts "Value of local variable is #{i}"
end
#这将产生以下结果,并会进入一个无限循环

方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def test #不带参数的方法
puts "test"
end
test #output : test
def test1(var1, var2)
puts var1,var2
end
test1(1,2) #or test1 1,2 空格可以去掉
def test(a1="Ruby", a2="Perl") #默认值
puts "The programming language is #{a1}"
puts "The programming language is #{a2}"
end
test "C", "C++"
test

返回值

1
2
3
4
5
def test
i = 100
j = 10
k = 0
end #返回最后一个声明的变量 k
1
2
3
4
5
6
7
8
9
10
11
12
13
return
OR
return 12
OR
return 1,2,3
OR
return x,y #自带tuple like python

可变数量的参数 like python

1
2
3
4
5
6
7
8
9
10
11
12
def sample (*test)
puts "The number of parameters is #{test.length}"
for i in 0...test.length
puts "The parameters are #{test[i]}"
end
end
sample "Zara", "6", "F"
sample "Mac", "36", "M", "MCA"
list = ["Zara", "6", "F"] #也可以使用 array 加* 前缀
sample(*list)

类方法

1
2
3
4
5
6
class Accounts
def reading_charge
end
def Accounts.return_date #Accounts 可以用self 替换
end
end

定义方法

1
Accounts.return_date


  • 块由大量的代码组成。
  • 您需要给块取个名称。
  • 块中的代码总是包含在大括号 {} 内。
  • 块总是从与其具有相同名称的函数调用。这意味着如果您的块名称为 test,那么您要使用函数 test 来调用这个块。
    您可以使用 yield 语句来调用块。
1
2
3
4
5
6
def test
yield 5
puts "You are in the method test"
yield 100
end
test {|i| puts "You are in the block #{i}"}

如果您想要传递多个参数,那么 yield 语句如下所示:

1
2
yield a, b
test {|a, b| statement}

复杂用法

1
2
3
4
5
def test(var = 2)
puts var
yield(var)
end
test("bumaociyuan"){|name| puts "Hello world #{name}"}

但是如果方法的最后一个参数前带有 &,那么您可以向该方法传递一个块,且这个块可被赋给最后一个参数。如果 * 和 & 同时出现在参数列表中,& 应放在后面。

1
2
3
4
def test(&block)
block.call
end
test { puts "Hello World!"}

Module
模块常量命名与类常量命名类似,以大写字母开头。方法定义看起来也相似:模块方法定义与类方法定义类似。

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

1
2
3
4
5
6
7
8
9
10
11
\# 定义在 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 语句来加载模块文件:

1
2
3
4
5
6
7
$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 来从一个相对目录引用文件。

注意:在这里,文件包含相同的函数名称。所以,这会在引用调用程序时导致代码模糊,但是模块避免了这种代码模糊,而且我们可以使用模块的名称调用适当的函数。

support.rb

1
2
3
4
5
6
7
8
9
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$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

Mixins 多重继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
module A
def a1
puts "a1"
end
def a2
puts "a2"
end
end
module B
def b1
puts "b1"
end
def b2
puts "b2"
end
end
class Sample
include A
include B
def s1
puts "s1"
end
end
samp=Sample.new
samp.a1
samp.a2
samp.b1
samp.b2
samp.s1
\#output
a1
a2
b1
b2
s1

String
link

1
2
3
4
5
6
7
8
9
x, y, z = 12, 36, 72
puts "The value of x is #{ x }."
puts "The sum of x and y is #{ x + y }."
puts "The average was #{ (x + y + z)/3 }."
%{Ruby is fun.} 相当于 "Ruby is fun."
%Q{ Ruby is fun. } 相当于 " Ruby is fun. "
%q[Ruby is fun.] 相当于以单引号字符串
%x!ls! 相当于反勾号命令输出 `ls`


Array

link

数组的索引从 0 开始,这与 C 或 Java 中一样。一个负数的索引时相对于数组的末尾计数的,也就是说,索引为 -1 表示数组的最后一个元素,-2 表示数组中的倒数第二个元素,依此类推。

Ruby 数组可存储诸如 String、 Integer、 Fixnum、 Hash、 Symbol 等对象,甚至可以是其他 Array 对象。Ruby 数组不像其他语言中的数组那么刚性。当向数组添加元素时,Ruby 数组会自动增长。

创建数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
names = Array.new
names = Array.new(20)
names = Array.new(20)
puts names.size # 返回 20
puts names.length # 返回 20
names = Array.new(4, "mac")
puts "#{names}" #output macmacmacmac
nums = Array.new(10) { |e| e = e * 2 }
puts "#{nums}" #output 024681012141618
nums = Array.[](1, 2, 3, 4,5)
nums = Array[1, 2, 3, 4,5]
digits = Array(0..9)
puts "#{digits}" #output 0123456789

哈希(Hash)

link

哈希(Hash)是类似 “employee” => “salary” 这样的键值对的集合。哈希的索引是通过任何对象类型的任意键来完成的,而不是一个整数索引,其他与数组相似。

通过键或值遍历哈希的顺序看起来是随意的,且通常不是按照插入顺序。如果您尝试通过一个不存在的键访问哈希,则方法会返回 nil。


日期 & 时间(Date & Time)

创建当前的日期和时间

1
2
3
4
5
6
7
time1 = Time.new
puts "Current Time : " + time1.inspect
\# Time.now 是一个同义词
time2 = Time.now
puts "Current Time : " + time2.inspect
1
2
3
4
5
6
7
8
9
10
11
12
13
14
time = Time.new
\# Time 的组件
puts "Current Time : " + 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":时区名称

范围(Range)
范围(Range)无处不在:January 到 December、 0 到 9、等等。Ruby 支持范围,并允许我们以不同的方式使用范围:

  • 作为序列的范围
  • 作为条件的范围
  • 作为间隔的范围
1
2
3
(1..5) #==> 1, 2, 3, 4, 5
(1...5) #==> 1, 2, 3, 4
('a'..'d') #==> 'a', 'b', 'c', 'd'

序列 1..100 是一个 Range 对象,包含了两个 Fixnum 对象的引用。如果需要,您可以使用 to_a 方法把范围转换为列表。尝试下面的实例:

1
2
3
4
5
6
$, =", " # Array 值分隔符
range1 = (1..10).to_a
range2 = ('bar'..'bat').to_a
puts "#{range1}"
puts "#{range2}"

each 迭代器

1
2
3
collection.each do |variable|
code
end
1
2
3
a = [1,2,3,4,5]
b = a.collect{|x| 10*x}
puts b

collect 方法不是数组间进行复制的正确方式。这里有另一个称为 clone 的方法,用于复制一个数组到另一个数组


文件的输入与输出

link

Ruby 提供了一整套 I/O 相关的方法,在内核(Kernel)模块中实现。所有的 I/O 方法派生自 IO 类。

类 IO 提供了所有基础的方法,比如 read、 write、 gets、 puts、 readline、 getc 和 printf。

本章节将讲解所有 Ruby 中可用的基础的 I/O 函数。如需了解更多的函数,请查看 Ruby 的 IO 类。

puts 语句

1
2
3
4
val1 = "This is variable one"
val2 = "This is variable two"
puts val1
puts val2

gets 语句

1
2
3
puts "Enter a value :"
val = gets
puts val

putc 语句

1
2
str="Hello Ruby!"
putc str

print 语句

1
2
print "Hello World"
print "Good Morning" #output Hello WorldGood Morning


打开和关闭文件

File.new 方法

您可以使用 File.new 方法创建一个 File 对象用于读取、写入或者读写,读写权限取决于 mode 字符串。最后,您可以使用 File.close 方法来关闭该文件。

1
2
3
4
aFile = File.new("filename", "mode")
# ... 处理文件
#filename 默认路径是当前路径,必须带extension 如 "support.rb"
aFile.close

mode:

模式 描述
r 只读模式。文件指针被放置在文件的开头。这是默认模式。
r+ 读写模式。文件指针被放置在文件的开头。
w 只写模式。如果文件存在,则重写文件。如果文件不存在,则创建一个新文件用于写入。
w+ 读写模式。如果文件存在,则重写已存在的文件。如果文件不存在,则创建一个新文件用于读写。
a 只写模式。如果文件存在,则文件指针被放置在文件的末尾。也就是说,文件是追加模式。如果文件不存在,则创建一个新文件用于写入。
a+ 读写模式。如果文件存在,则文件指针被放置在文件的末尾。也就是说,文件是追加模式。如果文件不存在,则创建一个新文件用于读写。

读取和写入文件

sysread 方法

1
2
3
4
5
6
7
aFile = File.new("input.txt", "r")
if aFile
content = aFile.sysread(20)
puts content
else
puts "Unable to open file!"
end

该语句将输入文件的头 20 个字符。文件指针将被放置在文件中第 21 个字符的位置。

syswrite 方法

1
2
3
4
5
6
aFile = File.new("input.txt", "r+")
if aFile
aFile.syswrite("ABCDEF")
else
puts "Unable to open file!"
end

该语句将写入 “ABCDEF” 到文件中。

each_byte 方法

1
2
3
4
5
6
7
aFile = File.new("input.txt", "r+")
if aFile
aFile.syswrite("ABCDEF")
aFile.each_byte {|ch| putc ch; putc ?. }
else
puts "Unable to open file!"
end

IO.readlines 方法
类 File 是类 IO 的一个子类。类 IO 也有一些用于操作文件的方法。

IO.readlines 是 IO 类中的一个方法。该方法逐行返回文件的内容。下面的代码显示了方法 IO.readlines 的使用:

1
2
3
arr = IO.readlines("input.txt")
puts arr[0]
puts arr[1]

IO.foreach 方法

1
IO.foreach("input.txt"){|block| puts block}

重命名和删除文件

1
2
3
4
5
\# 重命名文件 test1.txt 为 test2.txt
File.rename( "test1.txt", "test2.txt" )
\# 删除文件 test2.txt
File.delete("text2.txt")

文件模式与所有权

1
2
file = File.new( "test.txt", "w" )
file.chmod( 0755 )
掩码 描述
0700 rwx 掩码,针对所有者
0400 r ,针对所有者
0200 w ,针对所有者
0100 x ,针对所有者
0070 rwx 掩码,针对所属组
0040 r ,针对所属组
0020 w ,针对所属组
0010 x ,针对所属组
0007 rwx 掩码,针对其他人
0004 r ,针对其他人
0002 w ,针对其他人
0001 x ,针对其他人
4000 执行时设置用户 ID
2000 执行时设置所属组 ID
1000 保存交换文本,甚至在使用后也会保存

文件查询

1
2
#下面的命令在打开文件前检查文件是否已存在:
File.open("file.rb") if File::exists?( "file.rb" )

您可以通过 Dir.pwd 查看当前目录:

1
puts Dir.pwd # 返回当前目录,类似 /usr/bin

您可以使用 Dir.entries 获取指定目录内的文件和目录列表:

1
puts Dir.entries("/usr/bin").join(' ')

Dir.entries 返回一个数组,包含指定目录内的所有项。Dir.foreach 提供了相同的功能:

1
2
3
Dir.foreach("/usr/bin") do |entry|
puts entry
end

创建目录

1
2
Dir.mkdir("mynewdir")
Dir.mkdir( "mynewdir", 755 ) #掩码 755 设置所有者(owner)、所属组(group)、每个人(world [anyone])的权限为 rwxr-xr-x,其中 r = read 读取,w = write 写入,x = execute 执行。

删除目录

1
Dir.delete("testdir")

File 类和方法

link


Dir 类和方法

link


异常

link


面向对象

Ruby 是纯面向对象的语言,Ruby 中的一切都是以对象的形式出现。Ruby 中的每个值都是一个对象,即使是最原始的东西:字符串、数字,甚至连 true 和 false 都是对象。类本身也是一个对象,是 Class 类的一个实例。本章将向您讲解所有与 Ruby 面向对象相关的主要功能。

类用于指定对象的形式,它结合了数据表示法和方法,把数据整理成一个整齐的包。类中的数据和方法被称为类的成员。


类定义

按照惯例,名称必须以大写字母开头,如果包含多个单词,每个单词首字母大写,但此间没有分隔符(例如:CamelCase)。

1
2
3
4
5
6
class Box
code
end
box1 = Box.new
box2 = Box.new

initialize 方法

1
2
3
4
5
class Box
def initialize(w,h)
@width, @height = w, h
end
end

实例变量

1
2
3
4
5
6
class Box
def initialize(w,h)
# 给实例变量赋值
@width, @height = w, h
end
end

访问器 & 设置器 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 定义类
class Box
# 构造器方法
def initialize(w,h)
@width, @height = w, h
end
# 访问器方法
def printWidth
@width
end
def printHeight
@height
end
end
# 创建对象
box = Box.new(10, 20)
# 使用访问器方法
x = box.printWidth()
y = box.printHeight()
puts "Width of the box is : #{x}"
puts "Height of the box is : #{y}"

类方法 & 类变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Box
# 初始化类变量
@@count = 0
def initialize(w,h)
# 给实例变量赋值
@width, @height = w, h
@@count += 1
end
def self.printCount() #self 可以用Box 替换
puts "Box count is : #@@count"
end
end
# 创建两个对象
box1 = Box.new(10, 20)
box2 = Box.new(30, 100)
# 调用类方法来输出盒子计数
Box.printCount()

访问控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 定义类
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()

运算符重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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
def width
@width
end
def height
@height
end
end
box1 = Box.new(10,20);
puts (box1 * 3).height

冻结对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 定义类
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.freeze
if( box.frozen? )
puts "Box object is frozen object"
else
puts "Box object is normal object"
end
# 现在尝试使用设置器方法
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}"

类常量
您可以在类的内部定义一个常量,通过把一个直接的数值或字符串值赋给一个变量来定义的,常量的定义不需要使用 @ 或 @@。按照惯例,常量的名称使用大写

一旦常量被定义,您就不能改变它的值,您可以在类的内部直接访问常量,就像是访问变量一样,但是如果您想要在类的外部访问常量,那么您必须使用 classname::constant,如下面实例所示。

1
2
3
4
5
class Box
NAME = "box name"
end
puts Box::NAME

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Box
attr_accessor :width , :height
def initialize(w,h)
@width, @height = w,h
end
def getArea
@width * @height
end
end
box1 = Box.new(10,20)
puts box1.getArea()
box2 = Box.allocate #报错,好像没什么用
box2.getArea()

正则表达式

语法

正则表达式从字面上看是一种介于斜杠之间或介于跟在 %r 后的任意分隔符之间的模式,如下所示:

1
2
3
/pattern/
/pattern/im # 可以指定选项
%r!/usr/local! # 一般的分隔的正则表达式