全部博文(403)
2012年(403)
分类: 嵌入式
2012-03-09 18:49:48
今天又学了一会RUBY的闭包,主要是看《RUBY元编程(metapromgramming ruby)》一书:
第三章闭包结尾的守关BOSS是一道题:编写你的第一种领域专属语言。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 |
event "the sky is falling" do
@sky_height < 300
end
event "It's getting closer" do
@sky_height < @mountains_height
end
setup do
puts "Setting up sky"
@sky_height = 100
end
setup do
puts "Setting up mountains"
@mountains_height = 200
end |
要求编写一个程序:redflag.rb. 对上面这段测试文件运行,得到如下的输出
1
2
3
4
5
6 |
Setting up sky
Setting up mountains
Alert: the sky is falling
Setting up sky
Setting up mountains
Alert: It's getting closer |
原书作者的给出的答案如下:
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 |
def event(name,&block)
@events[name] = block
end
def setup(&block)
@setups.push(block)
end
Dir.glob('*event.rb').each do |file|
@events={}
@setups=[]
load file
#puts file
@events.each do |name,event|
env = Object.new()
@setups.each do |setup|
env.instance_eval &setup
end
puts "Alert: #{name}" if env.instance_eval &event
end
#puts @events.to_s
#puts @setups.to_s
end
#我加的这两行,用来测试@sky_height的作用域
puts @sky_height
puts @mountains_heigh |
前面的都好理解,关键是后来做的这个Clean Room:
env = Object.new()
@setups.each do |setupa|
env.instance_eval &setup
end
puts "Alert: #{name}" if env.instance_eval &event |
这一段,主要是为了让 &setup 这个区块与 &event 区块在同一个对象env的空间内运行,达到来共享两个变量的值:@sky_height , @mountains_height的目的。
我去掉了这个clean room后,改为proc.call的方式做了下面的这个测试:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 |
def event(name,&block)
@events[name] = block
end
def setup(&block)
@setups.push(block)
end
Dir.glob('*event.rb').each do |file|
@events={}
@setups=[]
load file
#puts file
@events.each do |name,event|
env = Object.new()
@setups.each do |setup|
setup.call
end
puts "Alert: #{name}" if event.call
end
#puts @events.to_s
#puts @setups.to_s
end |
#我加的这两行,用来测试@sky_height的作用域
puts @sky_height
puts @mountains_heigh
也能通过。不过这时候发现这两个变量@sky_height @mountains_heigh已经变成一个全局变量--proc层级的变量。在程序的末尾打出了变量的值。
而用作者的洁净室方法,这两个变量只是在env的上下文环境中存在,是这个Object对象的实例变量。在程序的末尾这两个变量是nil。
作者通过这个例子极好地展示了 洁净室 和 扁平作用域 的功能。
这章的最后,作者给出了另外一个更完美的方法,连@events @setups 这两个全局变量也去掉了:
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 |
#---
# Excerpted from "Metaprogramming Ruby",
# published by The Pragmatic Bookshelf.
# Copyrights apply to this code. It may not be used to create training material,
# courses, books, articles, and the like. Contact us if you are in doubt.
# We make no guarantees that this code is fit for any purpose.
# Visit for more book information.
#---
lambda {
setups = []
events = {}
Kernel.send :define_method, :event do |name, &block|
events[name] = block
end
Kernel.send :define_method, :setup do |&block|
setups << block
end
Kernel.send :define_method, :each_event do |&block|
events.each_pair do |name, event|
block.call name, event
end
end
Kernel.send :define_method, :each_setup do |&block|
setups.each do |setup|
block.call setup
end
end
}.call
Dir.glob('*events.rb').each do |file|
load file
each_event do |name, event|
env = Object.new
each_setup do |setup|
env.instance_eval &setup
end
puts "ALERT: #{name}" if env.instance_eval &event
end
end |
附:关于instance_eval的解释:
instance_eval可以在一个实例的上下文中eval一个字符串或者一个block:
instance_eval()方法做下面3件事情:
a,改变self为instance_eval的接收器。
b,改变默认的definee给接收器的eigenclass,如果没有,则创建它。
c, 执行block的内容。
参考: 的书评