使用块在父范围

时间:2016-05-09 20:53:58

标签: ruby metaprogramming

gem binding_of_caller有一个如何在父范围中设置变量的示例:

(这只是从他们的自述文件粘贴)

def a
  var = 10
  b
  puts var
end

def b
  c
end

def c
  binding.of_caller(2).eval('var = :hello')
end

a()

# OUTPUT
# => hello

这很有用,但它受到在字符串中进行所有变量初始化的需要的限制。

我稍微考了一下,意识到我可以使用YAML来序列化/反序列化对象。

例如,举例如下:

def c
  value = YAML.dump [ { a: "b" } ]
  binding.of_caller(2).eval("var = YAML.load('#{value}')")
end

a()
# => {a: "b"}

这很酷,但如果我完全避免序列化并且只是编写一个正确的do; end;块,它会更好:

# doesnt work
def c
  binding.of_caller(2).eval do
    # ideally this would set the variable named "var" in the scope of method "a"
    var = [ { a: "b" } ]
  end
end

如何完成上一个示例的功能?如果有其他方式,我不需要使用binding_of_caller

1 个答案:

答案 0 :(得分:4)

这是我能做的最好的,我怀疑(尽管我真的很想被证明是错的),最好的是你找不到自己的C扩展和la binding_of_caller:

eval("ObjectSpace._id2ref(%d).call(binding)" % block.object_id)

当然,魔法在这里:

Binding#eval

我们获取Proc的对象ID,然后在我们的binding中使用ObjectSpace#_id2ref从内存中的任何位置检索它并调用它,传入本地def a var = 10 b puts var end def b c end def c binding.of_caller(2).eval_block do |bnd| bnd.local_variable_set(:var, [ { a: "b" } ]) end end a # => {:a=>"b"}

这是在行动:

var = [ { a: "b" } ]

正如您所看到的,我们必须执行bnd.local_variable_set(:var, [ { a: "b" } ])而不是bnd。没有办法在Ruby中更改块的绑定,因此我们必须传入绑定(class Program { static int SumaDivisors(int nombre) { int suma; suma = 0; for (int contador = 1; contador < nombre; contador++) { if (nombre % contador == 0) { suma = suma + contador; } } return suma; } static void Main(string[] args) { Console.WriteLine("Busca els nombres amics entre 0 i 10000"); int numero, suma1, suma2; for (numero = 1; numero < 10000; numero++) { suma1 = SumaDivisors(numero); suma2 = SumaDivisors(suma1); if (suma1 != numero && suma2 == numero) { Console.WriteLine("Els nombre {0} i {1} són nombres amics", suma1, suma2); } } Console.ReadKey(); } } )并调用Binding#local_variable_set