F = {}
def data name = '', &block
F[name] = [] unless F.include? name
F[name] << block
end
def do_data name
i = 2
h = {}
F[name].each do | b |
# 这里出错, 无法实现 i 变量。
h.merge!(b.call)
end
h
end
data :a do
{
:a1 => i,
:a2 => 'a2222',
}
end
p do_data(:a)
第一反应是看见这样的代码,就想揍人。
我改了下:
F = {}
def data name = '', &block
F[name] = [] unless F.include? name
F[name] << block
end
def do_data name
i = 2
h = {}
F[name].each do | b |
# 传递参数到 block
h.merge!(b.call(i))
end
h
end
# call data function to generate data
data :a do |i|
{
:a1 => i,
:a2 => 'a2222',
}
end
p do_data(:a)
你 block 执行的环境里面没有 i 这个变量啊,你的 i 是在一个方法里面定义的,方法的定义(以及类和模块的定义),会形成一个新的 scope,里面的变量你在外面是无法拿到的。而且你指出的拿不到 i 的地方也不对啊,你试下这个
F = {}
def data name = '', &block
F[name] = [] unless F.include? name
F[name] << block
end
i = 2 # 把 i 移到这里
def do_data name
h = {}
F[name].each do | b |
h.merge!(b.call)
end
h
end
data :a do
{
:a1 => i, # 你原来的代码是这里出错, 拿不到 i
:a2 => 'a2222',
}
end
p do_data(:a)
@coolesting 我猜测你要实现的需求是实现变量的作用域动态配置,而不用作为一般的方法参数传入。
在 Ruby 里,局部变量 (local variable) 只能在 method 和 loop 内被访问,实例变量 (instance variable) 可以在一个类的实例的方法之间互访。
所以要么和 @guyanbiao 说的一样,把 i
这个变量拎到外面。
或者用 实例变量 也可以实现,如下:
1.9.3p392 :001 > class HH; def initialize &blk; @i = 5; instance_eval &blk; end; end
=> nil
1.9.3p392 :002 > HH.new { puts (@i + 5) }
10
=> #<HH:0x000001010588e8 @i=5>
1.9.3p392 :003 > @i
=> nil
1.9.3p392 :004 >
关于 Ruby 里的各种变量的用途在 http://www.techotopia.com/index.php/Ruby_Variable_Scope 这里解释的比较清楚了,包括类变量等。
#6 楼 @guyanbiao 我以为 block 在被 call 时,其块中的变量才被赋值,才把 i 放到方法里面去。
才把代码弄成这样,实际上,代码要复杂很多。
def ss
'ss'
end
i = 2
data :a do
{
:a1 => ss,
:a2 => 'a2222',
:a3 => i,
}
end
data :a do
{
:a4 => Time.now,
}
end
#5 楼 @blacktulip 原本代码很复杂,一楼已被简化的了,但还是令大家生产异意。
不知道有人用过,sinatra 么,sinatra 不是有 before, helpers, configures 之类的块么?
现在我想增加一个 data 块,把 data 都存放在一个 D 常量中。按需索取。
data :a do
{
:a1 => i,
:a2 => 'a2222',
}
end
这个 block 里的i
在这段代码执行时就应该有初始值,然后它就被包裹到一个闭包里,这里的i
跟下面这段代码里的i
完全是两码事,它俩是完全独立的。
def do_data name
i = 2
h = {}
F[name].each do | b |
# 这里出错, 无法实现 i 变量。
h.merge!(b.call)
end
h
end
#13 楼 @edgar_wang_cn 问题是,在 block 封装之前,无法知道对方会以什么形式来赋值, 我想在执行 block 时,赋值,类似用 eval 的方法,来实现,有解决方法 吗 ?
反正就是,有很多这样的 block
data :a do
{
:a1 => i,
:a2 => 'a2222',
}
end
data :a do
{
:a1 => Time.now,
:a3 => 'a333',
}
end
data :a do
{
:a4 => get_value_by_method(),
:a5 => '555555',
:a6 => '666666',
}
end
我不知道用户会以什么方式赋值,我只想把数据装起来,要用的时候调用
the solution of question is end with this code h.merge!(instance_eval(&b))