块和闭包(block and Closures)

来源:互联网 发布:淘宝商品推广方法 编辑:程序博客网 时间:2024/05/01 11:11

http://www.ruby-cn.org/

可以参见http://martinfowler.com/bliki/Closures.html

        块(block):只是一段代码,相当于一个匿名函数;闭包(Closures):一段代码,能作为参数传递给其它方法。
让我们再回来看看自动点唱机,某些时候,我们需要处理点唱机和用户的界面:很多按钮,供用户选择歌曲和控制播放,我们需要给这些按钮指定相应的时间处理代码,而Ruby中的block就很适合干这样的事情。假设点唱机的设计者通过Ruby扩展为我们提供了一个基本的按钮类。

bStart = Button.new("Start")bPause = Button.new("Pause")# ...

当用户按下按钮之后如何处理呢?Button类提供了一个buttonPressed方法,在按钮按下时能被回调。所以最简单的方法创建Button的子类,然后在每个子类中实现buttonPressed 方法。

class StartButton < Button  def initialize    super("Start")       # invoke Button's initialize  end  def buttonPressed    # do start actions...  endend

bStart = StartButton.new

这样做有两个问题,首先这样将产生大量子类,如果Button变化了,所有子类都需要维护;第二,按钮按下之后需要执行的行为不应该在按钮上表示,这是点唱机的责任。我们可以用块来消除这些问题

class JukeboxButton < Button  def initialize(label, &action)    super(label)    @action = action  end  def buttonPressed    @action.call(self)  endend bStart = JukeboxButton.new("Start") { songList.start }   bPause = JukeboxButton.new("Pause") { songList.pause } 

上面代码的关键点在JukeboxButton#initialize的第二个参数,这个参数前面带有一个&符号,在Ruby中代表这个参数是一个块,这个块将会被转化为一个Proc对象,并关联道相应变量。在本例中我们把它赋给实例变量@action 。当回调方法buttonPressed 执行时,我们用方法Proc#call来调用相应的block。

当创建一个Proc对象之后我们得到的是什么呢?很有趣,我们得到的不仅是一串代码。和这个block定义相关联的上下文环境(范围内的):self值,定义它的方法,变量,常量等。Ruby中的一个有趣的地方就是即使block定义时候的变量已经不在它的使用范围内,这个变量还保留着,仍可以使用

我们来看一个例子,这个例子用了方法proc,这个方法把一个block变为了一个Proc对象。

def nTimes(aThing)
  return proc { |n| aThing * n }
end
p1 = nTimes(23)
p1.call(3) »69
p1.call(4) »92
p2 = nTimes("Hello ")
p2.call(3) »"Hello Hello Hello "

        方法nTimes 返回一个Proc对象,这个对象引用了这个函数的参数:aThing即使这个参数在块被调用时已经不在自己的作用域里了,这个块还是可以访问这个参数。(比如p1.call(4),这个时候,虽然对p1的赋值语句中的参数23已经超出了作用域范围,但是它仍然保存着23×4=92。)