学无所长,一事无成
分类: Python/Ruby
2012-07-27 00:18:51
通过 Kernel#block_given?() 来询问当前方法调用是否包含块
3.2 Ruby的#符号 p71
通过 ensure 语句可以保证 dispose 方法必被调用
3.3 闭包
可以运行的代码块由两部分组成:代码本身和一组绑定(局部变量,实例变量,self 等)
当定义一个块时,它会获取当时环境中的绑定,并且把它传给一个方法时,它会带着这些绑定一起进入该方法:
变量绑定是在块定义时完成的,因此上例中的 x 只会绑定 ‘hello’。方法中的 x 对其是不可见得。
作用域
块局部变量:块内部可以定义局部变量,在块执行结束后会消失。
切换作用域
Ruby 中一旦进入一个新的作用域,原先的绑定就会被替换为一组新的绑定,原先的作用域就超出范围,完全不可见了。
一个对象调用同一对象中的其他方法,实例变量在调用过程中始终存在于作用域中。由此我们知道实例变量为什么要加个 @ 了,它同普通变量不同。
作用域门
程序会在三个地方关闭前一个作用域,同时打开一个新的作用域。
只要程序见到以上三个关键字:class、module、def,就意味着进入了一个新的作用域,之前的作用域将不可见。
如上可见,普通变量是无法穿越作用域门的。要想在不同作用域间共享变量,有两个办法:全局变量($打头的变量) 跟 顶级实例变量(@打头的变量)。
但顶级实例变量在 self 发生改变时,会退出作用域。
class/module 与 def 之间的区别:class/module 中代码会立即执行,def 中的代码只有被调用时才会执行。因此 反复调用同一方法时,我们得到的是不同的作用域,当然该作用域在方法调用结束时消失。
扁平化作用域
class、module、def 是普通变量无法逾越的藩篱,如何让普通变量穿越作用域呢?哈,我们取消这三个关键字,用方法调用取代就可以了。
class MyClass 可以改写成 MyClass = Class.new do … end
module MyModule 可以改写成 MyModule = Module.new do … end
def my_method 可以改写成 define_method :my_method do … end
方法调用不会开启新的作用域,我们依然处于当前作用域中,所有变量自然可以为我所用。这就叫扁平化作用域。
共享作用域
如果想在一组方法间共享一个变量,而且不希望其它方法能够访问。那么我们可以将这一组方法放入一个扁平化作用域中,然后用 class、module或 def加以保护。
3.4 instance_eval() p83
通过 instance_eval 可以将一个对象的的作用域暴露在当前作用域中,同时改变 self ;因此上面代码中我们既可以访问 v,也可以访问 @v。
Ruby 1.9 中引入了 instance_exec :同 instance_eval 功能相似,但它允许对块传入参数。
3.5 可调用对象 p86
有三种方法可以打包代码
Proc 对象
代码块不是对象,因此有必要将其打包,方法如下。
Ruby 还提供了两个内核方法用于把块转换为 Proc:lambda() 和 proc()
&操作符
块更像是方法的一个匿名参数,如果要给它命名,则可以使用 & 操作符
&operation 表示这是一个代码块,operation 表示这是一个 Proc 对象。
proc 与 lambda 的对比
整体而言,lambda 更直观,更像是一个方法,因此很多 Ruby 使用者首选 lambda。
自己写了段小程序,做下测试:
Kernel#proc() 在 Ruby1.9 中,是 Proc.new() 的别名。我们还是只使用 Proc.new() 或者 lambda() 吧,省的搞混。
简洁 lambda
这只是一种实验性功能,还是暂时不要使用了。
重访方法
object.method 可以返回一个 Method 对象,可用 Method#call 对其调用。但此Method对象只在其所在的对象的作用域执行。
而 lambda 是个闭包,因此在其定义时的作用域中执行。
可以使用 Method#unbind() 方法将对象绑定断开,然后使用 bind 绑定到另外一个对象上(前后两个对象必须归属同一个类,否则报错)
这种技术似乎想不出其用途。
Method#to_proc() 可将 Method 对象转换为 Proc 对象。
define_method() 将块转换为方法。
3.6 编写一种领域专属语言