`

Ruby的魔法 学习笔记之一

    博客分类:
  • Ruby
阅读更多
Ruby的许多动态特性,让Ruby具有很多魔法,这个魔法足以让你来定制你自己的语言DSL,
Rails就是Ruby在Web的DSL.

一、向对象显示的发送消息
我们可以向对象直接发送消息:
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的任意成员数据来排序:
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方法:
class Array
	def sort_by(sysm)
		self.sort{|x,y| x.send(sym) <=> y.send(sym)}
	end
end

我们看看运行结果:
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不仅可以打开一个类,而且可以打开一个对象,给这个对象添加或定制功能,而不影响
其他对象:
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这么做
我们可以有更模块化的方式:
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添加行为
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:
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里面的方法变成了对象里面的
实例方法:
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

三、创建一个可参数化的类:
如果我们要创建很多类,这些类只有类成员的初始值不同,我们很容易想起:
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在运行时延迟求值来达到目标:
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

最好的方法:
我们不使用类变量,而是使用类实例变量:
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.iteye.com/topic/44271]
曹操(caocao)被誉为“古代轻功最好的人 ”,是因为“说曹操,曹操到”这句名言。
在ruby中,曹操的这种轻功被称为callcc.
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给出的从代码后面”飞“到代码前面的例子:
arr = [ "Freddie", "Herbie", "Ron", "Max", "Ringo" ]  
callcc{|$cc|}#下面的$cc.call如果被执行,就会飞回到这里(callcc的块之后)。  
puts(message = arr.shift)  
$cc.call unless message =~ /Max/ 

例子大多来自<<The ruby way>>
分享到:
评论
2 楼 fuliang 2010-05-19  
orcl_zhang 写道
总结的很不错。就是最后的一个例子看的不是很明白。。
这个曹操到底是啥。。

if say=="曹操" 成立
就直接跳出block之外,执行puts "到" 了。
1 楼 orcl_zhang 2010-05-18  
总结的很不错。就是最后的一个例子看的不是很明白。。
这个曹操到底是啥。。

相关推荐

    Ruby 魔法 学习笔记之一

    一、向对象显示的发送消息 我们可以向对象直接发送消息: Ruby代码 代码如下:class HelloWorld def say(name) print “Hello, “, name end end hw = HelloWorld.new hw.send(:say,”world”) 我们通常使用hw.say(...

    ruby笔记1ruby笔记1ruby笔记1

    ruby笔记1ruby笔记1ruby笔记1ruby笔记1ruby笔记1ruby笔记1

    Ruby学习笔记

    这个暑假回家没事干闲...恩,废话就少提,下面我学习Ruby的各个部分。其中Ruby风格这一部分是开放的,因为我暂时还没有形成自己的风格。我用xmind来记笔记,因此我只是把笔记搬到博客上,看看下面的图。好有爱啊!!!

    学习Ruby的笔记以及Demo源码.zip

    学习Ruby的笔记以及Demo源码.zip学习Ruby的笔记以及Demo源码.zip学习Ruby的笔记以及Demo源码.zip学习Ruby的笔记以及Demo源码.zip学习Ruby的笔记以及Demo源码.zip学习Ruby的笔记以及Demo源码.zip学习Ruby的笔记以及...

    ruby学习笔记

    ruby基础笔记,自学笔记,ruby基础语法非常全面ruby基础笔记,自学笔记,ruby基础语法非常全面

    Ruby学习笔记(学习心得)

    Ruby学习笔记 01 类的属性与方法 02 访问控制 03 Container(Array/Hash) 04 Iterator 05 简单数据类型 06 范围 07 方法 08 条件控制 09 异常处理

    ruby笔记3ruby笔记3ruby笔记3

    ruby笔记3ruby笔记3ruby笔记3ruby笔记3ruby笔记3ruby笔记3

    ruby笔记2ruby笔记2ruby笔记2

    ruby笔记2ruby笔记2ruby笔记2ruby笔记2ruby笔记2

    ruby编程学习笔记及demo

    Ruby是一种纯粹的面向对象编程语言。它由日本的松本行弘(まつもとゆきひろ/Yukihiro Matsumoto)创建于1993年。 您可以在 www.ruby-lang.org 的 Ruby 邮件列表上找到松本行弘(まつもとゆきひろ/Yukihiro ...

    个人ruby学习笔记

    ruby学习笔记....................

    ruby初学笔记ruby初学笔记

    ruby初学笔记ruby初学笔记ruby初学笔记ruby初学笔记ruby初学笔记

    精华ruby学习笔记和代码

    ruby是一种简单快捷的面向对象脚本语言 开发者:松本行弘 开发时间:1995年 设计目的:A PROGRAMMER'S BEST FRIEND【程序员的最佳朋友】 文件扩展名:rb 这个笔记里包含了ruby的整型,字符串,浮点数,布尔等基础...

    Ruby知识点笔记

    Ruby知识点笔记

    Ruby基础学习资料

    Ruby基础学习资料:Ruby介绍,环境配置,Ruby语法。

    Redis全套学习笔记 (带章节目录) 完整版pdf

    本文是一篇关于Redis全套学习笔记的文章,主要介绍了Redis的基础知识、数据结构、持久化、集群、高可用、性能优化等方面的内容。通过本文的学习,读者可以全面掌握Redis的使用和应用,提高自己的技术水平和实践能力...

    Ruby学习:关于Ruby的学习笔记

    我对Ruby的学习笔记 评论 #i'm a sinlg eline comment =begin I'm a multiline comment! =end 资料来源 puts(writeln) 控制台输出/打印的Ruby方式 puts "hello world" 编写问候世界并返回nil 打印(写) 字符...

    学习 Ruby 的笔记以及 Demo.zip

    学习 Ruby 的笔记以及 Demo.zip

    Ruby自学笔记

    Ruby自学笔记,这是我在学习Ruby时的小结笔记,若对你有所帮助请给满星,谢谢!!!

    rubyinstaller-devkit-3.0.2-1-x64 and Ruby 学习笔记.7z

    rubyinstaller-devkit-3.0.2-1-x64 and Ruby 学习笔记.7z 由于国内访问github服务器不稳定,所以上传到CSDN给大家方便下载

Global site tag (gtag.js) - Google Analytics