Ruby Hightlights

标识符

标识符 例子
局部变量 first_name
实例变量 @first_name
类变量 @@first_name
全局变量 $FIRST_NAME
常量 FIRST_NAME
关键字 class
方法名 同局部变量

类变量

类变量的作用域并不属于类作用域,而是输入类层级的作用域。它提供了一种可以在类和类的实例之间共享数据的存储机制,但是对其他对象不可见。

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
46
47
48
49
50
51
52
53
54
55
class Car
@@makes = []
@@cars = {}
@@total_count = 0

attr_reader :make

def self.add_make(make)
unless @@makes.include?(make)
@@makes << make
@@cars[make] = 0
end
end


def self.total_count
puts "#{@@total_count}"
end

def initialize(make)
if @@makes.include? make
puts "Create a new make #{make}"
@make = make
@@cars[make] += 1
@@total_count += 1
else
raise "no such make"
end
end

def make_mates
@@cars[self.make]
end

def total_count
puts "#{@@total_count}"
end

end


# my_car = Car.new "xx"
Car.add_make("BMW")
my_car = Car.new "BMW"
Car.total_count
puts my_car.make
my_car.total_count


=begin
Create a new make BMW
1
BMW
1
=end

父类和子类共享类变量

1
2
3
4
5
6
7
8
9
10
11
12
13
class Parent
@@value = 100
end

class Child < Parent
@@value = 200
end

class Parent
puts @@value
end

Parent.new # 200

常量

定义在类中的实例方法可以看到同样定义在类中的常量,但其他程序也可以看到这些常量

私有方法

私有方法不能显式地被接收者调用。因为当没有明确接收对象时,接收对象是self。所以如果接收对象是Test的一个实例时,是可以调用成功的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Test
def a
puts "a"
end

def b
# self.a error
a
end

private :a
end

test = Test.new
test.a # error!
test.b # "a"

保护方法

只能在此对象的实例方法或此类(或子类)的另一个对象中直接调用受保护的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Test
def a
puts "a"
end

def b
a
puts "#{self}"
end

protected :a
end

test = Test.new
test.b

Class and object

查看对象的原生方法

1
2
3
4
p Object.new.methods.sort
=begin
[:!, :!=, :!~, :<=>, :==, :===, :=~, :__id__, :__send__, :class, :clone, :define_singleton_method, :display, :dup, :enum_for, :eql?, :equal?, :extend, :freeze, :frozen?, :hash, :inspect, :instance_eval, :instance_exec, :instance_of?, :instance_variable_defined?, :instance_variable_get, :instance_variable_set, :instance_variables, :is_a?, :itself, :kind_of?, :method, :methods, :nil?, :object_id, :private_methods, :protected_methods, :public_method, :public_methods, :public_send, :remove_instance_variable, :respond_to?, :send, :singleton_class, :singleton_method, :singleton_methods, :taint, :tainted?, :tap, :then, :to_enum, :to_s, :trust, :untaint, :untrust, :untrusted?, :yield_self]
=end

重开类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class C
def m
puts "method m"
end
end

class C
def y
puts "methods y"
end
end

p C.new.methods.sort

# [...... :m, ...... :y, ......]

单例方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
obj = Object.new

def obj.test
p "test"
end

cla = Class.new
def cla.test
p "test"
end

obj.test
cla.test

# "test"
# "test"

对象方法查找规则

类对象怎样查找方法

  • 类是对象
  • 类的实例也是对象
  • 类对象有自己的方法、自己的状态和唯一标识

因此,类的对象方法查找规则是

  1. 从他的类
  2. 从超类和他们类更早的祖先
  3. 从存储在他们自身的单例方法

顶层方法

顶层方法作为Object类的私有方法被保存下来,它在任何地方可用,但是不能现式地指定接收者。

Self

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
puts "In top level, self is:  #{self}"

module A
def n
puts "In module A, self is: #{self}"
end
end

class Test
puts "In Test, self is: #{self}" # 类对象
include A
def self.x
puts "In x, self is: #{self}" # 类对象
end

def m
puts "In m, self is: #{self}" # 实例对象
end

end

test = Test.new
Test.x
test.m
test.n

=begin
In top level, self is: main
In Test, self is: Test
In x, self is: Test
In m, self is: #<Test:0x00007fa01a04cbc8>
In module A, self is: #<Test:0x00007fa01a04cbc8>
=end

Scope

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
a = 0

def t
puts "Top level method t"
end

class C
a = 1
def self.x
a = 2
puts "C.x, a = #{a}"
end

def m
a = 3
puts "C#m; a = #{a}"
end

def n
a = 4
puts "C#n; a = #{a}"
end

puts "class scope: a = #{a}"
end


C.x
c = C.new
c.m
c.n

=begin
class scope: a = 1
C.x, a = 2
C#m; a = 3
C#n; a = 4
=end

控制流

case 子句

每个 ruby 对象都有一个 case 相等性判断方法: ===

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
class Person
attr_accessor :age, :height

def initialize(age,height)
self.age = age
self.height = height
end

def ===(other_obj)
(self.age == other_obj.age) & (self.height == other_obj.height)
end
end


$person1 = Person.new(10,10)
$person2 = Person.new(10,20)


def test(person)

case person
when $person1
puts "person 1"
when $person2
puts "person"
else
puts "not matching"
end
end


person = Person.new(10,10)
test(person) # person 1

比较和 Comparable 模块

如果类需要有完整的比较方法,可以由如下解决方案:

  • 混合名为 Comparable 的模块
  • 定义 <=> 的比较方法做为实例方法,在这个方法内部需要定义”小于”,”等于”,”大于”的含义。

代码块(block) 和 关键字 (yield)

ruby 中的每个方法调用都遵循如下语法:

  1. 接收者对象或者变量
  2. 点(如果有明确的接收者则为必要条件)
  3. 方法名
  4. 参数列表(可选,默认为())
  5. 代码块 (可选,没有默认)
  • 代码块可以使用 {}, 也可以使用 do while 提供;
  • 如果提供的代码块在方法中不能提供,并不会发生错误;
  • 代码块和方法一样,可以接受参数;
  • yeild 的实际总用是转移控制权与作用域到代码块(可以跳回来)。
  • 代码块是一个闭包(closure)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def block_scope
x = 10
1.times { puts x}
end

block_scope # 10


def block_scope
x = 10
1.times do
x = 20
puts x
end
end

block_scope # 20

String

  1. here-doc

    1
    2
    3
    4
    5
    text = <<EOM.to_i
    10
    EOM

    puts text # 10
  2. 索引

1
2
3
4
5
6
7
8
string = "zhangruochi"

puts string[0,2] # zh
puts string[0..2] # zha
puts string[0...2] # zh
puts string[0..-1] # zhangruochi
puts string["zhang"] #zhang
puts string["xxx"] #nil

Enumerable

在ruby中,对象之间共有的特性通常都被放在模块里,集合类型也不例外:Ruby中的集合类型对象通常都包含 Enumerable 模块。

Each

任何枚举类型都包含一个each方法,其作用是将其中的元素逐个地作为参数传递给代码块。

1
2
3
4
5
# 这里的每个方法都是基于each构建
p Enumerable.instance_methods(false).sort
=begin
[:all?, :any?, :chain, :chunk, :chunk_while, :collect, :collect_concat, :count, :cycle, :detect, :drop, :drop_while, :each_cons, :each_entry, :each_slice, :each_with_index, :each_with_object, :entries, :filter, :find, :find_all, :find_index, :first, :flat_map, :grep, :grep_v, :group_by, :include?, :inject, :lazy, :map, :max, :max_by, :member?, :min, :min_by, :minmax, :minmax_by, :none?, :one?, :partition, :reduce, :reject, :reverse_each, :select, :slice_after, :slice_before, :slice_when, :sort, :sort_by, :sum, :take, :take_while, :to_a, :to_h, :uniq, :zip]
=end

Rainbow 混合了Enumerable模块,则自动赋予了整套基于 each 方法构建的方法。

1
2
3
4
5
6
7
8
9
10
11
12
class Rainbow
include Enumerable
def each
yield "Red"
yield "Orange"
yield "Green"
end
end

a = Rainbow.new
p a.map(&:upcase)
# ["RED", "ORANGE", "GREEN"]

符号参数

可以在方法参数的位置使用如:upcase这样的符号并在前面加上&,它的效果与在代码块中调用每个元素同符号名称相等的方法的作用相同。

1
p ["A","B","C"].map(&:downcase)

Enumerator(枚举器)

  1. 迭代器本质上是一个方法,它将一个或多个元素传递到代码块中。
  2. 枚举器本质上一枚举器是一个简单的可枚举对象。它有 each 方法,并使用Enumerable模块定义其所有常用的方法。
  3. 枚举器只需要有一个“迭代每一个”的逻辑,它已经知道后面的事情可以一一展开。
    • Enumerator.new 伴随一个包含稍后会用到的 each 逻辑的代码块
    • 创建一个基于可存在枚举对象的枚举器,这样枚举器的 each 方法就会从那个枚举对象的特定方法中抽取它的元素用于迭代
1
2
3
4
5
6
7
8
9
e = Enumerator.new do |y|
y << 1
y << 2
y << 3
end


p e.to_a
p e.map {|x| x * 2}
Donate article here