Ruby 提供了其他现代语言中很常见的条件结构。
在这里,我们将解释所有的条件语句和 Ruby 中可用的修饰符。
if conditional [then]
code...
[elsif conditional [then]
code...]...
[else
code...]
end
if a == 4 then a = 7 end
code if condition
如果 conditional 为真,则执行 code。牛X
#!/usr/bin/ruby
$debug=1
print "debug\n" if $debug
这将产生以下结果:
debug
unless conditional [then]
code
[else
code ]
end
unless式和 if式作用相反,即如果 conditional 为假,则执行 code。
如果 conditional 为真,则执行 else 子句中指定的 code。牛X
#!/usr/bin/ruby
x=1
unless x>2
puts "x 小于 2"
else
puts "x 大于 2"
end
这将产生以下结果:
x 小于 2
code unless conditional
如果 conditional 为假,则执行 code。牛X
case expression
[when expression [, expression ...] [then]
code ]...
[else
code ]
end
case先对一个 expression 进行匹配判断,然后根据匹配结果进行分支选择。
它使用 ===运算符比较 when 指定的 expression,若一致的话就执行 when 部分的内容。
通常我们省略保留字 then 。
若想在一行内写出完整的 when 式,则必须以 then 隔开条件式和程式区块。如下所示:
when a == 4 then a = 7 end
因此:
case expr0
when expr1, expr2
stmt1
when expr3, expr4
stmt2
else
stmt3
end
基本上类似于:
_tmp = expr0
if expr1 === _tmp || expr2 === _tmp
stmt1
elsif expr3 === _tmp || expr4 === _tmp
stmt2
else
stmt3
end
Ruby 中的循环用于执行相同的代码块若干次。本章节将详细介绍 Ruby 支持的所有循环语句。
while conditional [do]
code
end
或者
while conditional [:]
code
end
当 conditional 为真时,执行 code。
语法中 do 或 : 可以省略不写。
但若要在一行内写出 while 式,则必须以 do 或 : 隔开条件式或程式区块。
code while condition
或者
begin
code
end while conditional
当 conditional 为真时,执行 code。
如果 while 修饰符跟在一个没有 rescue 或 ensure 子句的 begin 语句后面,code 会在 conditional 判断之前执行一次。???Excuse Me???
until conditional [do]
code
end
当 conditional 为假时,执行 code。
语法中 do 可以省略不写。但若要在一行内写出 until 式,则必须以 do 隔开条件式或程式区块。
code until conditional
或者
begin
code
end until conditional
当 conditional 为假时,执行 code。
如果 until 修饰符跟在一个没有 rescue 或 ensure 子句的 begin 语句后面,code 会在 conditional 判断之前执行一次。???Excuse Me???
for variable [, variable ...] in expression [do]
code
end
针对 expression 中的每个元素分别执行一次 code。
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
for i in 0..5
puts "局部变量的值为 #{i}"
end
在这里,我们已经定义了范围 0..5。语句 for i in 0..5 允许 i 的值从 0 到 5(包含 5)。
以上实例输出结果为:
局部变量的值为 0
局部变量的值为 1
局部变量的值为 2
局部变量的值为 3
局部变量的值为 4
局部变量的值为 5
for...in 循环几乎是完全等价于:
(expression).each do |variable[, variable...]| code end
但是,for 循环不会为局部变量创建一个新的作用域。???Excuse Me???
语法中 do 可以省略不写。
但若要在一行内写出 for 式,则必须以 do 隔开条件式或程式区块。
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
(0..5).each do |element|
puts "局部变量的值为 #{element}"
end
以上实例输出结果为:
局部变量的值为 0
局部变量的值为 1
局部变量的值为 2
局部变量的值为 3
局部变量的值为 4
局部变量的值为 5
break
终止最内部的循环。
如果在块内调用,则终止相关块的方法(方法返回 nil)
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
for i in 0..5
if i > 2 then
break
end
puts "局部变量的值为 #{i}"
end
以上实例输出结果为:
局部变量的值为 0
局部变量的值为 1
局部变量的值为 2
next
跳到最内部循环的下一个迭代。
如果在块内调用,则终止块的执行(yield 或调用返回 nil)。???Excuse Me???
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
for i in 0..5
if i < 2 then
next
end
puts "局部变量的值为 #{i}"
end
以上实例输出结果为:
局部变量的值为 2
局部变量的值为 3
局部变量的值为 4
局部变量的值为 5
redo
重新开始最内部循环的该次迭代,不检查循环条件。
如果在块内调用,则重新开始 yield 或 call。
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
for i in 0..5
if i < 2 then
puts "局部变量的值为 #{i}"
redo
end
end
这将产生以下结果,并会进入一个无限循环:
局部变量的值为 0
局部变量的值为 0
............................
retry
如果 retry 出现在 begin 表达式的 rescue 子句中,
则从 begin 主体的开头重新开始。???Excuse Me???
begin
do_something # 抛出的异常
rescue
# 处理错误
retry # 重新从 begin 开始
end
如果 retry 出现在迭代内、块内或者 for 表达式的主体内,则重新开始迭代调用。
迭代的参数会重新评估。(重置计数器???)
for i in 1..5
retry if some_condition # 重新从 i == 1 开始
end
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
for i in 1..5
retry if i > 2
puts "局部变量的值为 #{i}"
end
理论上,这个将会产生以下的结果,并会进入一个无限循环,但是:
局部变量的值为 1
局部变量的值为 2
局部变量的值为 1
局部变量的值为 2
局部变量的值为 1
局部变量的值为 2
............................
为啥报错???
Ruby 方法与其他编程语言中的函数类似。
Ruby 方法用于捆绑一个或多个重复的语句到一个单元中。
方法名应以小写字母开头。
如果您以大写字母作为方法名的开头,Ruby 可能会把它当作常量,从而导致不正确地解析调用。
方法应在调用之前定义,否则 Ruby 会产生未定义的方法调用异常。
def method_name [( [arg [= default]]...[, * arg [, &expr ]])]
expr..
end
所以,您可以定义一个简单的方法,如下所示:
def method_name
expr..
end
您可以定义一个接受参数的方法,如下所示:
def method_name (var1, var2)
expr..
end
您可以为参数设置默认值,如果方法调用时未传递必需的参数则使用默认值:
def method_name (var1=value1, var2=value2)
expr..
end
当您要调用无参数的方法时,只需要使用方法名即可,如下所示:
method_name
但是,当您调用带参数的方法时,您在写方法名时还要带上参数,例如:牛X
method_name 25, 30
使用带参数方法最大的缺点是:调用方法时需要记住参数个数。哈哈~
例如,如果您向一个接受三个参数的方法只传递了两个参数,Ruby 会显示错误。
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
def test(a1="Ruby", a2="Perl")
puts "编程语言为 #{a1}"
puts "编程语言为 #{a2}"
end
test "C", "C++"
test
这将产生以下结果:
编程语言为 C
编程语言为 C++
编程语言为 Ruby
编程语言为 Perl
Ruby 中的每个方法默认都会返回一个值。
这个返回的值是最后一个语句的值。例如:
def test
i = 100
j = 10
k = 0
end
在调用这个方法时,将返回最后一个声明的变量 k。
Ruby 中的 return 语句用于从 Ruby 方法中返回一个或多个值。
return [expr[`,' expr...]]
如果给出超过两个的表达式,包含这些值的数组将是返回值。
如果未给出表达式,nil 将是返回值。
return
或
return 12
或
return 1,2,3
看看下面的实例:
假设您声明了一个带有两个参数的方法,当您调用该方法时,您同时还需要记得传递两个参数。
但是,Ruby 允许您声明参数数量可变的方法。
让我们看看下面的实例:
在下面的这段代码中,您已经声明了一个方法 showGirl,接受一个参数 paramArr。
但是,这个参数是一个变量参数。
这意味着参数可以带有不同数量的变量。
所以上面的代码将产生下面的结果:
当方法定义在类定义外部时,方法默认标记为 private。
另一方面,定义在类定义中的方法默认标记为 public。
方法默认的可见性 和 private 标记可通过模块(Module)的 public 或 private 改变。
当你想要访问类的方法时,您首先需要实例化类。
然后,使用对象,您可以访问类的任何成员。
Ruby 提供了一种不用实例化类即可访问方法的方式。
让我们看看如何声明并访问类方法:
class Girl
def instance_method
end
def Girl.class_method
end
end
我们已经知道类方法 class_method 是如何声明的。???Excuse Me???
它是通过在类名后跟着一个点号,点号后跟着方法名来声明的。
您可以直接访问类方法,如下所示:
Girl.class_method
如需访问该方法,您不需要创建类 Girl 的对象。
代码如下:
#!/usr/bin/ruby -w
# -*- coding: UTF-8 -*-
#coding=utf-8
class Girl
#使用类名,声明类方法
def Girl.showCup(cup)
puts "girl's cup is #{cup}"
end
#使用self,声明类方法
def self.showAge(age)
puts "girl's age is #{age}"
end
#第3种,使用<<特殊符号,凡是在它的范围内,以定义实例方法的形式定义的方法,都是类方法
class << self
def showName(name)
puts "girl's name is #{name}"
end
end
#第4种,instance_eval定义单例方法(Eigenclass Method或者Singleton Method)
#反而class_eval定义实例方法(Instance Method)
#???Excuse Me???
Girl.instance_eval do
def showBirthday(birthday)
puts "girl's birthday is #{birthday}"
end
end
end
Girl.showCup("F")
Girl.showAge 13
Girl.showName "いりえ·さあや"
Girl.showBirthday "1993年11月15日"
运行效果如下:
这个语句用于为方法 或 全局变量起别名。
别名不能在方法主体内定义。
即使方法被重写,方法的别名也保持方法的当前定义。
为编了号的全局变量($1, $2,...)起别名 是被禁止的。
重写内置的全局变量可能会导致严重的问题。
alias 方法别名 方法名
alias 全局变量别名 全局变量
alias aliasMethod originMethod
alias $aliasGlobalVariable $&
在这里,我们已经为 originalMethod 定义了别名为 aliasMethod,
为 $& 定义了别名为 $aliasGlobalVariable
这个语句用于取消方法定义。
undef 不能出现在方法主体内。
通过使用 undef 和 alias,类的接口可以从父类独立修改 ???Excuse Me???
但请注意,在自身内部方法调用时,它可能会破坏程序。
undef 方法名
下面的实例取消名为 showGirlCup的方法定义:
undef showGirlCup
您已经知道 Ruby 如何定义方法以及您如何调用方法。
类似地,Ruby 有一个块的概念。
block_name{
statement1
statement2
..........
}
在这里,您将学到如何使用一个简单的 yield 语句来调用块。
您也将学到如何使用带有参数的 yield 语句来调用块。
在实例中,您将看到这两种类型的 yield 语句。
让我们看一个 yield 语句的实例:
您也可以传递带有参数的 yield 语句。下面是一个实例:
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
def test
yield 67
puts "在 test 方法内"
yield 520
end
test {|i| puts "#{i}在块内"}
这将产生以下结果:
67在块内
在 test 方法内
520在块内
在这里,yield 语句后跟着参数。
您甚至可以传递多个参数。
在块中,您可以在两个竖线之间放置一个变量来接受参数,如|a|。
因此,在上面的代码中,yield 67 语句向 test 块传递值 67 作为参数。
现在,看下面的语句:
test {|i| puts "#{i}在块内"}
在这里,值 67 会在变量 i 中收到。
现在,观察下面的 puts 语句:
puts "#{i}在块内"
这个 puts 语句的输出是:
67在块内
如果您想要向block块传递多个参数,那么 yield 语句如下所示:
yield a, b
此时,块如下所示:
test {|a, b| statement}
参数使用逗号分隔。
您已经看到块和方法之间是如何相互关联的。
您通常使用 yield 语句 从 与其具有相同名称的 方法里面 调用块。
因此,代码如下所示:
#!/usr/bin/ruby
def test
yield
end
test{ puts "Hello world"}
本实例是实现块的最简单的方式。
您使用 yield 语句调用 test 块。
但是如果方法的最后一个参数前带有 &,那么您可以向该方法传递一个块block,且这个块可被赋给最后一个参数。
如果 * 和 & 同时出现在参数列表中,& 应放在后面。
#!/usr/bin/ruby
def test(&block)
block.call
end
test { puts "Hello World!"}
这将产生以下结果:
Hello World!
每个 Ruby 源文件可以声明当文件被加载时要运行的代码块(BEGIN 块),以及程序执行完成后要运行的代码块(END 块)。
#!/usr/bin/ruby
BEGIN {
# BEGIN 代码块
puts "BEGIN 代码块"
}
END {
# END 代码块
puts "END 代码块"
}
# MAIN 代码块
puts "MAIN 代码块"
一个程序可以包含多个 BEGIN 和 END 块。
BEGIN 块按照它们出现的顺序执行。
END 块按照它们出现的相反顺序执行。
当执行时,上面的程序产生产生以下结果:
BEGIN 代码块
MAIN 代码块
END 代码块
Ruby同JAVA一样只支持单继承,每个类只能有一个父类,
为了提高程序的灵活性ruby引入了 模块 ,为实现多重继承提供了可能。
模块可以说是类的一个补充,但是模块于类有两个不同:
1.模块不能拥有实例
2.模块不能被继承
模块名必须是大写字母开头
模块(Module)是一种把方法、类和常量组合在一起的方式。
模块(Module)为您提供了两大好处。
模块(Module)定义了一个命名空间,相当于一个沙盒,在里边您的方法和常量不会与其他地方的方法常量冲突。
模块类似与类,但又有以下不同:
module Identifier
statement1
statement2
...........
end
模块常量 命名与类常量命名类似,以大写字母开头。
方法定义看起来也相似:模块方法定义与类方法定义类似。
通过类方法,您可以在 类方法名称前面 放置模块名称和一个点号 来调用模块方法,
您可以使用模块名称 和 两个冒号来引用一个 常量。
#!/usr/bin/ruby
# 定义在 ruby_42.rb 文件中的模块
module GirlModule
PI = 3.141592654
def GirlModule.showCup(cup)
# ..
end
def GirlModule.showAge(age)
# ..
end
end
我们可以定义多个函数名称相同但是功能不同的模块:
#!/usr/bin/ruby
# 定义在 ruby_43.rb 文件中的模块
module LoliModule
VERY_SMALL = 0
SMALL = 1
def LoliModule.showCup(cup)
# ...
end
end
就像类方法,当您在模块中定义一个方法时,您必须指定在模块名称后跟着一个点号,点号后跟着方法名。
# 如果想校验类是否包含某个模块可以用如下方式:
# 类名.include?(模块名) 返回true or false
puts Girl.include?(GirlModule)
require 语句类似于 C 和 C++ 中的 include 语句以及 Java 中的 import 语句。
如果一个第三方的程序想要使用任何已定义的模块,则可以简单地使用 Ruby require 语句来加载模块文件:
require filename
在这里,文件扩展名 .rb 不是必需的。
在这里,我们可以使用 $LOAD_PATH << '.' 让 Ruby 知道必须在当前目录中搜索被引用的文件。
如果您不想使用 $LOAD_PATH,那么您可以使用 require_relative 来从一个相对目录引用文件。
注意:在这里,文件包含相同的函数名称。
所以,这会在引用调用程序时导致代码模糊,但是模块避免了这种代码模糊,而且我们可以使用模块的名称调用适当的函数。
模块1代码如下:
#!/usr/bin/ruby -w
# -*- coding: UTF-8 -*-
#coding=utf-8
#定义在ruby_44_module01文件中的模块
module T_Module
PI = Math::PI #52163/16604.0 #355/113.0 #22/7
def T_Module.sin(x)
res = Math.sin(x)
puts "T_Module sin(#{x}) is #{res}"
end
def T_Module.cos(x)
res = Math.cos(x)
puts "T_Module cos(#{x}) is #{res}"
end
end
模块2代码如下:
#!/usr/bin/ruby -w
# -*- coding: UTF-8 -*-
#coding=utf-8
#定义在ruby_44_module02文件中的模块
module P_Module
VERY_BAD = 0
BAD = 1
def P_Module.sin(x)
res = Math.sin(x)
puts "P_Module,sin(#{x}) is #{res}"
end
end
主文件代码如下:
#!/usr/bin/ruby -w
# -*- coding: UTF-8 -*-
#coding=utf-8
require '/Users/beyond/sg_ruby/ruby_44_module01.rb'
require '/Users/beyond/sg_ruby/ruby_44_module02.rb'
res = T_Module.sin(T_Module::PI/2)
puts res
res = T_Module.sin(T_Module::PI/6)
puts res
calc = P_Module.sin(P_Module::VERY_BAD)
puts calc
运行效果如下:
您可以在类中嵌入模块。
为了在类中嵌入模块,您可以在类中使用 include 语句:
include modulename
如果模块是定义在一个单独的文件中,那么在嵌入模块之前使用 require 语句引用该文件是必需的。
假设下面的模块写在ruby_45_module.rb 文件中。
module BeyondModule
FIRST_DAY = "日曜日"
def Beyond.showWeeks
puts "月・火・水・木・金・土・日"
end
def Beyond.showYears
puts "鼠,牛,虎,兎,龍(たつ),蛇,馬,羊(ひつじ),猿,鶏,犬,豚(いのしし)"
end
end
现在,您可以在类中引用该模块,如下所示:
当一个类可以从多个父类继承类的特性时,该类显示为多重继承。
Ruby 不直接支持多重继承,
但是 Ruby 的模块(Module)有另一个神奇的功能。
它几乎消除了多重继承的需要,提供了一种名为 mix-in 的装置。
Mix-ins 向您提供了一种完美的为类添加功能的控制方式。
但是,它们真正的强大在于当 mix-in 中的代码开始与使用它的类中的代码交互时。
让我们看看下面的示例代码,深入了解 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
我们可以使用类.ancestor打印所有的继承关系
未完待续,下一章节,つづく