1. Here Document: 多行字符串
print <<ONELINE
=begin
注释区
=end
内容区
ONELINE
2. BEGIN 语句
BEGIN {
code
}
声明 code 会在程序运行之前被调用。
3. END 语句
END {
code
}
声明 code 会在程序的结尾被调用。
3. 注释
# 这是注释
=begin
这是
多行
注释
=end
4. #{ expr } 替换任意 Ruby 表达式的值为一个字符串
name="Ruby"
puts "#{name+",ok"}"
输出结果为:
Ruby,ok
5.数组
数组字面量通过[]中以逗号分隔定义,且支持range定义。
(1)数组通过[]索引访问
(2)通过赋值操作插入、删除、替换元素
(3)通过+,-号进行合并和删除元素,且集合做为新集合出现
(4)通过<<号向原数据追加元素
(5)通过*号重复数组元素
(6)通过|和&符号做并集和交集操作(注意顺序)
6.Ruby 类中的变量
* 局部变量:局部变量是在方法中定义的变量。局部变量在方法外是不可用的。在后续的章节中,您将看到有关方法的更多细节。局部变量以小写字母或 _ 开始。
* 实例变量:实例变量可以跨任何特定的实例或对象中的方法使用。这意味着,实例变量可以从对象到对象的改变。实例变量在变量名之前放置符号(@)。
* 类变量(所有实例对象共用):类变量可以跨不同的对象使用。类变量属于类,且是类的一个属性。类变量在变量名之前放置符号(@@)。
* 全局变量:类变量不能跨类使用。如果您想要有一个可以跨类使用的变量,您需要定义全局变量。全局变量总是以美元符号($)开始。
* 常数(Constant): 大写字母开头
7. 自定义方法创建对象
class Customer
@@no_of_customers=0
def initialize(id, name, addr)
@cust_id=id
@cust_name=name
@cust_addr=addr
end
end
cust1=Customer.new("1", "John", "Wisdom Apartments, Ludhiya")
8.Ruby 类中的成员函数
class Sample
def function
statement 1
statement 2
end
end
class Sample
def hello
puts "Hello Ruby!"
end
end
# 使用上面的类来创建对象
object = Sample. new
object.hello
9.Ruby 伪变量
它们是特殊的变量,有着局部变量的外观,但行为却像常量。您不能给这些变量赋任何值。
* self: 当前方法的接收器对象。
* true: 代表 true 的值。
* false: 代表 false 的值。
* nil: 代表 undefined 的值。
* __FILE__: 当前源文件的名称。
* __LINE__: 当前行在源文件中的编号。
10.Ruby 比较运算符
<=> :第一个操作数等于第二个操作数则返回 0,如果第一个操作数大于第二个操作数则返回 1,如果第一个操作数小于第二个操作数则返回 -1
.eql?:类型和值都需要相等(1 == 1.0 返回 true,但是 1.eql?(1.0) 返回 false)
equal?:具有相同的对象 id,则返回 true(如果 aObj 是 bObj 的副本,那么 aObj == bObj 返回 true,aObj.equal?bObj 返回 false,但是 aObj.equal?aObj 返回 true。)
11.Ruby 并行赋值
a, b, c = 10, 20, 30
// 交换两个值
a, b = b, a
12. 逻辑运算符
and :&&
or :||
not :!
13. defined? 运算符
defined? 是一个特殊的运算符,以方法调用的形式来判断传递的表达式是否已定义。它返回表达式的描述字符串,如果表达式未定义则返回 nil。
defined? variable # 如果 variable 已经初始化,则为 True
foo = 42
defined? foo # => "local-variable"
defined? $_ # => "global-variable"
defined? bar # => nil(未定义)
defined? method_call # 如果方法已经定义,则为 True
14. .和::运算符
点运算符:调用类或模块中的方法
::(常量解析运算符) :引用类或模块中的常量
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" 的局部常量
DC56746E-33C6-490A-928E-3669FC2CAA7F.png
15. 判断
- if else 语句
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
x=1
if x > 2
puts "x 大于 2"
elsif x <= 2 and x!=0
puts "x 是 1"
else
puts "无法得知 x 的值"
end
- if 修饰符(表示当 if 右边之条件成立时才执行 if 左边的式子)
$debug=1
print "debug\n" if $debug
- unless语句(unless式和 if式作用相反,即如果 conditional 为假,则执行 code。如果 conditional 为真,则执行 else 子句中指定的 code。)
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
x=1
unless x>2
puts "x 小于 2"
else
puts "x 大于 2"
end
- unless修饰符(表示当 unless 右边之条件不成立时才执行 if 左边的式子)
$var = false
print "3 -- 这一行输出\n" unless $var
- Ruby case 语句
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
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
$age = 5
case $age
when 0 .. 2
puts "婴儿"
when 3 .. 6
puts "小孩"
when 7 .. 12
puts "child"
when 13 .. 18
puts "少年"
else
puts "其他年龄段的"
end
输出:小孩
当case的"表达式"部分被省略时,将计算第一个when条件部分为真的表达式。
case
when foo then puts 'foo is true'
when bar then puts 'bar is true'
when quu then puts 'quu is true'
end
# 显示 "bar is true"
16. 循环
- while 语句(语法中 do 或 : 可以省略不写。但若要在一行内写出 while 式,则必须以 do 或 : 隔开条件式或程式区块)
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
$i = 0
$num = 5
while $i < $num do
puts("在循环语句中 i = #$i" )
$i +=1
end
while $i >= 0
puts("在循环语句中 i = #$i")
$i -=1
end
- while 修饰符(当 conditional 为真时,执行 code)
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
$i = 0
$num = 5
begin
puts("在循环语句中 i = #$i" )
$i +=1
end while $i < $num
def name(count)
puts("name is xiaoming #{count}")
$i -=1
end
name($i) while $i >= 0
结果:
在循环语句中 i = 0
在循环语句中 i = 1
在循环语句中 i = 2
在循环语句中 i = 3
在循环语句中 i = 4
name is xiaoming 5
name is xiaoming 4
name is xiaoming 3
name is xiaoming 2
name is xiaoming 1
name is xiaoming 0
- Ruby until 语句 (当 conditional 为假时,执行 code)
与while 语句相反
- until 修饰符 (当 conditional 为 false 时,执行 code)
与while修饰词相反
- (1)Ruby for in语句
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
for i in 0..5
puts "局部变量的值为 #{i}"
end
- (2) Ruby each do
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
(0..5).each do |i|
puts "局部变量的值为 #{i}"
end
结果:
局部变量的值为 0
局部变量的值为 1
局部变量的值为 2
局部变量的值为 3
局部变量的值为 4
局部变量的值为 5
- 循环内语句:
break语句:跳出循环
next语句: continue 进行下次循环
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
for i in 0..5
if i > 2 then
break
end
puts "局部变量的值为 #{i}"
end
结果:
局部变量的值为 0
局部变量的值为 1
局部变量的值为 2
redo 语句(重新开始最内部循环的该次迭代,不检查循环条件。如果在块内调用,则重新开始 yield 或 call)
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
COUNT = 1
for i in 0..5
if (i < 2 && COUNT < 5) then
puts "局部变量的值为 #{i}"
COUNT += 1
redo
end
end
结果:
局部变量的值为 0
局部变量的值为 0
局部变量的值为 0
局部变量的值为 0
retry 语句(1.9以及之后的版本不支持在循环中使用retry)
- 如果 retry 出现在 begin 表达式的 rescue 子句中,则从 begin 主体的开头重新开始。
begin
do_something # 抛出的异常
rescue
# 处理错误
retry # 重新从 begin 开始
end
17. 方法
- 方法基本构成
# 声明
def method_name
expr..
end
def method_name (var1, var2)
expr..
end
# 默认值
def method_name (var1=value1, var2=value2)
expr..
end
# 调用
method_name
method_name 25, 30
# 返回值 (默认)
def test
i = 100
j = 10
k = 0
end
# 返回值 (多个)
def test
i = 100
j = 200
k = 300
return i, j, k
end
var = test
puts var
结果:
100
200
300
# 可变参数 * 符号
# *在方法定义时,可将多个参数放入一个数组中
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"
# *在方法调用时,可将一个数组拆分成多个参数
data = [1,5,9]
big = max(*data)
# *在方法返回时,可将返回的数组展开給另一个方法调用(带*号的方法调用,别懵)
max(*polar(x,y))
# 哈希作为有名参数(应用场景:当一个方法多个参数,很难记参数顺序,ruby又不支持为参数值指定参数名,哈希对象做为参数可以得到近似的功能)
# 为了更好的支持这个风格,当哈希作为最后一个参数(或者其后只有一个&打头的代码块参数)时候可以省略{}
def sequence(args)
n = args[:n] || 0
m = args[:m] || 1
end
sequence(:m=>3, :n=>5)
# 关键字参数的方法定义
# 指定参数的默认值
def area2(x:0, y:0, z:0)
xy = x * y
yz = y * z
zx = z *x
(xy+yz+zx) * 2
end
# 调用的时候指定参数名
area2(x: 2, y: 3, z: 4)
area2(z: 4, y: 3, x: 2)
# 可以用 散列作为实参
args = {x: 2, y: 3, z: 4}
area2(args)
- 类方法 实例方法 单键方法
# 类方法实际就是一种单键方法
# ruby实现上把Fixnum和symbol的值看作立即值而非对象引用,因此无法定义单键方法,为了维持一致性,numeric对象也不能
# 声明
class Accounts
def reading_charge
puts "实例方法"
end
def Accounts.return_date
puts "类方法"
end
o = "message"
def o.printme
p "单键方法"
end
end
# 调用
Accounts.return_date
Accounts.new.reading_charge
o.printme
# 结果
类方法
实例方法
单键方法
- 方法命名
- 小写开头下划线连接
- 方法名可以!?= 结尾
- ? 结尾一般用在返回一个能回答调用者给出问题的值。eg:empty? black? nonzero?
- ! 结尾的方法要小心调用:代表此方法是可变的方法,会改变对象的内部状态 eg: 数组的sort!
- = 结尾的方法代表赋值方法
- 许多ruby操作符是用定义在类中的方法实现的,所以许多操作符可以用方法重载 eg: + * 甚至[] (仅有的例外一元加和一元减用+@和-@定义)
18. alias 语句 和 undef 语句
- alias 语句用于为方法或全局变量起别名
- alias 语句更重要的是为一个存在的方法增加新的功能
alias 方法名 方法名
alias 全局变量 全局变量
# eg:
alias foo bar
alias $MATCH $&
def hello
p "hello word"
end
alias original_hello hello
def hello
p "change hello"
original_hello
p "add other"
end
- undef 语句用于取消方法定义
- undef 不能出现在方法主体内
# 下面的实例取消名为 bar的方法定义
undef bar
19. 块(block) 和 yield 语句
- 代码块的作用域:块中能访问到方法内的局部变量,块中的局部变量仅存在于块中
- 块由大量的代码组成。
- 您需要给块取个名称。
- 块中的代码总是包含在大括号 {} 或do end内。
- 块总是从与其具有相同名称的函数调用。这意味着如果您的块名称为 test,那么您要* 使用函数 test 来调用这个块。
- 您可以使用 yield 语句来调用块。
- 第一种方式
# 无参:
def nameSay
puts "name is "
yield
end
# 可以用花括号和do end作为代码的分节符
# 惯例:代码块只有一行用{} 多行用do end
# 起始的花括号或do关键字必须和方法调用在同一行
nameSay{ puts "xiaoming" }
结果
name is
xiaoming
nameSay do
puts "xiaoming"
puts "xiaohong"
end
结果
name is
xiaoming
xiaohong
# 参数
def test
yield 5
puts "在 test 方法内"
yield 100
end
test {|i| puts "你在块 #{i} 内"}
结果
你在块 5 内
在 test 方法内
你在块 100 内
- 第二种方式
def test(&block)
block.call 1000
end
test {|i| puts "Hello World!#{i}"}
结果
Hello World!1000
# 代码块默认最后一行最为返回值
# 如果希望代码块有多个返回值,或者结束当前进行下次,请用next 关键字
# 不要用return return关键字会导致包含代码块的那个方法返回
array.collect do |x|
next 0 if x == nil
next x, x*x
end
# next上述使用并不多见,常用以下这种
array.collect do |x|
if x == nil
0
else
[x,x**2]
end
end
- 代码块中声明一个与外部同名的块内局部变量的语法(分号)
# 下面是两个参数和三个块级局部变量的代码块
has.each {|key,value;i,j,k| ...}
- 代码块传递实参
# 迭代器在传递实参的时候把全局变量变成了:one和实例变量变成了1
{:one=>1}.each_pair {|$key,@value| ...}
# 代码块多个实参
def two
yield 1,2,3,4,5,6
end
two{|x| p x} # => in ruby 1.8 :warns and print [1,2,3,4,5,6]
two{|x| p x} # => in ruby 1.9 :no warns and print 1
two{|*x| p x} # => in everyone version :no warns and print [1,2,3,4,5,6]
two{|x,| p x} # => in everyone version :no warns and print 1
# yield语句和方法一样允许不带花括号的哈希作为最后一个实参(当yield最后一个实参是哈希的时候可以省略花括号)
def hashiter; yield :a=>1, :b=>2; end
# ruby1.9+ 代码块的最后一个行参可以(和方法一样)具有&前置
printer = lambda {|&b| puts b.call}
printer.call {"hi"}
代码块和方法行参的区别是:代码块参数不允许有默认值
[1,2,3].each {|x,y=10| p x*y} # => syntaxError
# in ruby 1.9 新语法可以有默认值,改写如下
[1,2,3].each &->(x,y=10){p x*y} # => 102030
- BEGIN 和 END 块
BEGIN {
# BEGIN 代码块
puts "BEGIN 代码块"
}
END {
# END 代码块
puts "END 代码块"
}
# MAIN 代码块
puts "MAIN 代码块"
结果
BEGIN 代码块
MAIN 代码块
END 代码块
20. 模块(Module):函数追加,共享函数~
Mixins (Ruby 不直接支持多重继承,这个牛逼了~)
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
module Trig
PI = 3.141592654
def Trig.sin(x)
# ..
end
def Trig.cos(x)
# ..
end
end
module Moral
VERY_BAD = 0
BAD = 1
def Moral.sin(badness)
# ...
end
end
require 语句:类似于 C 和 C++ 中的 include 语句以及 Java 中的 import 语句
$LOAD_PATH << '.'
require 'trig.rb'
require 'moral'
y = Trig.sin(Trig::PI/4)
wrongdoing = Moral.sin(Moral::VERY_BAD)
注释:
1. $LOAD_PATH << '.' 让 Ruby 知道必须在当前目录中搜索被引用的文件。如果您不想使用 $LOAD_PATH,那么您可以使用 require_relative 来从一个相对目录引用文件
2. 文件包含相同的函数名称。所以,这会在引用调用程序时导致代码模糊,但是模块避免了这种代码模糊,而且我们可以使用模块的名称调用适当的函数
extent 语句 (类中嵌入模块的类方法)
class Student
include B
extend A
end
include语句(类中嵌入模块的实例方法)
# 下面的模块写在 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
# include 使用
#!/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
21.语句和控制
- return: 使外围方法返回至调用者,代码块内无论嵌套多深的代码块,他总使得外围方法返回
- break: 不会使词法意义上的外围方法返回,break只能出现在循环或者代码块中,break 可以带返回值
- next: 等价其他语言的continue
- redo: 重新开始一个循环或者迭代的当前迭代,与next的区别,next将控制权传递给下一循环,redo控制权传递给循环或代码块当前迭代的开头,相当于额外进行了一次本循环
- retry: redo的加强版~,会使得代码块以及迭代器方法推出然后重新开始迭代,ruby 1.9 已经将其移除了,使用要小心无限循环
- throw/catch: catch不需要和throw处在一个方法内,throw可以跳出当前的循环和代码块,且可以对外跳出任意级数使其与catch一同定义的代码块退出
22. 异常
# raise函数抛出异常
raise Exception.new('not_found_article') if art.nil?
raise Exception.new('try_to_modify_others_article?') unless art.user_id == User.current.id
异常处理的三个从句:
- rescue : 处理异常
- else :在不执行rescue情况下会执行else
- ensure :无论有无异常都会执行(类似oc中的@finlly)
def method_name(x)
rescue
...
else
...
ensure
...
end
# rescue 处理异常 (本事不是一个语句 而是一个可以附加到其他语言上的从句)
def risky
begin
10.times do
explode
end
rescue TypeError
p $!
end
"hello"
end
rescue
作为语句修饰符 rescue
y = factorial(x) rescue x*2
# 相当于 上述代码好处不需要begin end 关键字
y = begin
factorial(x)
rescue
x*2
end
23. ruby 线程 纤程 连续体(连续体没研究)
# 最简单的线程使用 Thread
f = "file://name"
Thread.new {File.read(f)}
# 纤程fiber
# resume 调用时将会执行block,直到结束或者遇到Fiber.yield类方法,将控制权交给调用者且保存状态,再次执行resume时将继续上次状态继续运行
# 通过resume和yield方法的实参和返回值可以使得纤程与调用者交换数据
2.3.4 :044 > f = Fiber.new{
2.3.4 :045 > p "Fiber says Hello"
2.3.4 :046?> Fiber.yield
2.3.4 :047?> p "goodbye"
2.3.4 :048?> }
=> #<Fiber:0x007f8e62168648>
2.3.4 :050 > f.resume
"Fiber says Hello"
=> nil
2.3.4 :051 > f.resume
"goodbye"
# 纤程的应用实现生成器:每次调用返回斐波那数列的下一个数
2.3.4 :055 > def feibona(x,y)
2.3.4 :056?> Fiber.new do
2.3.4 :057 > x_c = x
2.3.4 :058?> y_c = y
2.3.4 :059?> loop do
2.3.4 :060 > Fiber.yield y_c
2.3.4 :061?> x_c,y_c = y_c,x_c+y_c
2.3.4 :062?> end
2.3.4 :063?> end
2.3.4 :064?> end
2.3.4 :066 > g = feibona 0,1
=> #<Fiber:0x007f8e6204d560>
2.3.4 :067 > 10.times{p g.resume}
# 封装版本的
class FibonaGenerator
def initialize
@x,@y = 0,1
@fiber = Fiber.new do
loop do
@x,@y = @y,@x+@y
fiber.yield @x
end
end
end
def next
@fiber.resume
end
def rewind
@x,@y = 0,1
end
end
# 使用
g = FibonaGenerator.new
10.times{print g.next}
g.rewind # 重置
# 第三方库 fiber:可以实现多个fiber之间传递控制权
# transfer :fiber之间穿梭
# alive:检测一个fiber是否在运行
# current:返回当前掌握控制权的fiber
require 'fiber'
f=g=nil
f = Fiber.new{|x|
p "f1 is #{x}"
x = g.transfer(x+1)
p "f2 is #{x}"
x = g.transfer(x+1)
p "f3 is #{x}"
x+1
}
g = Fiber.new{|x|
p "g1 is #{x}"
x = f.transfer(x+1)
p "g2 is #{x}"
x = f.transfer(x+1)
}
p f.transfer(1)
==begin
f1:1
g1:2
f2:3
g2:4
f3:5
6
==end
网友评论