手机
当前位置:查字典教程网 >脚本专栏 >ruby专题 >Ruby 魔法 学习笔记之一
Ruby 魔法 学习笔记之一
摘要:一、向对象显示的发送消息我们可以向对象直接发送消息:Ruby代码复制代码代码如下:classHelloWorlddefsay(name)pr...

一、向对象显示的发送消息

我们可以向对象直接发送消息:

Ruby代码

复制代码 代码如下:

class HelloWorld

def say(name)

print "Hello, ", name

end

end

hw = HelloWorld.new

hw.send(:say,"world")

我们通常使用hw.say("world"),但send可以对private的方法起作用。

不光如此send可以使程序更加动态,下面我们看看一个例子:

我们定义了一个类Person,我们希望一个包含Person对象的数组能够按

照Person的任意成员数据来排序:

Ruby代码

复制代码 代码如下:

class Person

attr_reader :name,:age,:height

def initialize(name,age,height)

@name,@age,@height = name,age,height

end

def inspect

"#@name #@age #@height"

end

end

在ruby中任何一个类都可以随时打开的,这样可以写出像2.days_ago这样优美

的code,我们打开Array,并定义一个sort_by方法:

Ruby代码

class Array

def sort_by(sysm)

self.sort{|x,y| x.send(sym) <=> y.send(sym)}

end

end

我们看看运行结果:

Ruby代码

people = []

people << Person.new("Hansel",35,69)

people << Person.new("Gretel",32,64)

people << Person.new("Ted",36,68)

people << Person.new("Alice", 33, 63)

p1 = people.sort_by(:name)

p2 = people.sort_by(:age)

p3 = people.sort_by(:height)

p p1 # [Alice 33 63, Gretel 32 64, Hansel 35 69, Ted 36 68]

p p2 # [Gretel 32 64, Alice 33 63, Hansel 35 69, Ted 36 68]

p p3 # [Alice 33 63, Gretel 32 64, Ted 36 68, Hansel 35 69]

这个结果是如何得到的呢?

其实除了send外还有一个地方应该注意attr_reader,attr_reader相当于定义了name,

age,heigh三个方法,而Array里的sort方法只需要提供一个比较方法:

x.send(sym) <=> y.send(sym) 通过send得到person的属性值,然后在使用<=>比较

二、定制一个object

<< object

ruby不仅可以打开一个类,而且可以打开一个对象,给这个对象添加或定制功能,而不影响

其他对象:

Ruby代码

a = "hello"

b = "goodbye"

def b.upcase

gsub(/(.)(.)/)($1.upcase + $2)

end

puts a.upcase #HELLO

puts b.upcase #GoOdBye

我们发现b.upcase方法被定制成我们自己的了

如果想给一个对象添加或定制多个功能,我们不想多个def b.method1 def b.method2这么做

我们可以有更模块化的方式:

Ruby代码

b = "goodbye"

class << b

def upcase # create single method

gsub(/(.)(.)/) { $1.upcase + $2 }

end

def upcase!

gsub!(/(.)(.)/) { $1.upcase + $2 }

end

end

puts b.upcase # GoOdBye

puts b # goodbye

b.upcase!

puts b # GoOdBye

这个class被叫做singleton class,因为这个class是针对b这个对象的。

和设计模式singleton object类似,只会发生一次的东东我们叫singleton.

<< self 给你定义的class添加行为

Ruby代码

class TheClass

class << self

def hello

puts "hello!"

end

end

end

TheClass.hello #hello!

<<self修改了你定义class的class,这是个很有用的技术,他可以定义class级别

的helper方法,然后在这个class的其他的定义中使用。下面一个列子定义了访问

函数,我们希望访问的时候把成员数据都转化成string,我们可以通过这个技术来

定义一个Class-Level的方法accessor_string:

Ruby代码

class MyClass

class << self

def accessor_string(*names)

names.each do |name|

class_eval <<-EOF

def #{name}

@#{name}.to_s

end

EOF

end

end

end

def initialize

@a = [ 1, 2, 3 ]

@b = Time.now

end

accessor_string :a, :b

end

o = MyClass.new

puts o.a # 123

puts o.b # Fri Nov 21 09:50:51 +0800 2008

通过extend module给你的对象添加行为,module里面的方法变成了对象里面的

实例方法:

Ruby代码

module Quantifier

def any?

self.each { |x| return true if yield x }

false

end

def all?

self.each { |x| return false if not yield x }

true

end

end

list = [1, 2, 3, 4, 5]

list.extend(Quantifier)

flag1 = list.any? {|x| x > 5 } # false

flag2 = list.any? {|x| x >= 5 } # true

flag3 = list.all? {|x| x <= 10 } # true

flag4 = list.all? {|x| x % 2 == 0 } # false

三、创建一个可参数化的类:

如果我们要创建很多类,这些类只有类成员的初始值不同,我们很容易想起:

Ruby代码

class IntelligentLife # Wrong way to do this!

@@home_planet = nil

def IntelligentLife.home_planet

@@home_planet

end

def IntelligentLife.home_planet=(x)

@@home_planet = x

end

#...

end

class Terran < IntelligentLife

@@home_planet = "Earth"

#...

end

class Martian < IntelligentLife

@@home_planet = "Mars"

#...

end

这种方式是错误的,实际上Ruby中的类成员不仅在这个类中被所有对象共享,

实际上会被整个继承体系共享,所以我们调用Terran.home_planet,会输出

“Mars”,而我们期望的是Earth

一个可行的方法:

我们可以通过class_eval在运行时延迟求值来达到目标:

Ruby代码

class IntelligentLife

def IntelligentLife.home_planet

class_eval("@@home_planet")

end

def IntelligentLife.home_planet=(x)

class_eval("@@home_planet = #{x}")

end

#...

end

class Terran < IntelligentLife

@@home_planet = "Earth"

#...

end

class Martian < IntelligentLife

@@home_planet = "Mars"

#...

end

puts Terran.home_planet # Earth

puts Martian.home_planet # Mars

最好的方法:

我们不使用类变量,而是使用类实例变量:

Ruby代码

class IntelligentLife

class << self

attr_accessor :home_planet

end

#...

end

class Terran < IntelligentLife

self.home_planet = "Earth"

#...

end

class Martian < IntelligentLife

self.home_planet = "Mars"

#...

end

puts Terran.home_planet # Earth

puts Martian.home_planet # Mars

四、Ruby中的Continuations:

Continuations恐怕是Ruby中最难理解的概念了,它可以处理非局部的跳转,

它保存了返回地址和执行的环境,和c中的setjmp和longjump类似,但它保存

了更多的信息:

axgle举的曹操的例子很形象,我们拿过来看看:

来自[http://www.javaeye.com/topic/44271]

曹操(caocao)被誉为“古代轻功最好的人 ”,是因为“说曹操,曹操到”这句名言。

在ruby中,曹操的这种轻功被称为callcc.

Ruby代码

callcc{|caocao|

for say in ["曹操","诸葛亮","周瑜"]

caocao.call if say=="曹操"

puts say #不会输出,因为曹操已经飞出去了

end

}#“曹操”飞到这里来了(跳出了callcc的block,紧跟在这个block后面,继续执行下面的ruby代码)

puts "到"

callcc里的caocao是个"延续"(Continuation)对象.这个对象只有名叫“call"的这样一个方法。

当执行了caocao.call后,caocao就会飞到callcc的块(block)后面,让ruby继续执行其下面的代码。

我上面给出的是一个从块里头”飞“到块外面的例子;下面是Programming Ruby给出的从代码后面”飞“到代码前面的例子:

Ruby代码

arr = [ "Freddie", "Herbie", "Ron", "Max", "Ringo" ]

callcc{|$cc|}#下面的$cc.call如果被执行,就会飞回到这里(callcc的块之后)。

puts(message = arr.shift)

$cc.call unless message =~ /Max/

例子大多来自<<The ruby way>>

【Ruby 魔法 学习笔记之一】相关文章:

Ruby基础知识之数据类型

Ruby On Rails上手笔记(安装使用全过程)

Ruby中钩子方法的运用实例解析

Terry七月Ruby读书笔记(比较详细)第1/4页

rudy 重载方法 详解

Ruby基础语法初探

实例解析Ruby中的数值类型以及常量

Ruby中的字符串编写示例

Ruby中百分号和字面值的使用示例

rudy 方法 分析

精品推荐
分类导航