获取先前作用域的局部变量
Get local variables of previous scope
我正在制作一个 Ruby REPL 以在应用程序中使用。我做了代码:
a = 1
b = 2
currentScope = []
Kernel.local_variables.each do |var|
currentScope << [var,Kernel.eval(var.to_s)]
end
launchREPL(currentScope)
在REPL里面,我可以执行下面的代码:
@a #=>1
@a+@b #=>3
理想情况下,我不必在启动 REPL 之前编写这四行代码,相反,我想在 launchREPL
函数中 运行 它们。但是,这需要从 launchREPL
函数内部访问先前的作用域。
测试 1
最值得注意的是我尝试过:
launchREPL(Kernel)
当我执行以下操作时:
def launchREPL(scope)
F = 0
puts scope.local_variables # => [:F]
end
显然这个方法是无效的
测试2
launchREPL(Kernel.binding)
def launchREPL(scope)
Kernel.binding.local_variables #= Error: private method 'local_variables' called for #<Binding>
end
有什么方法可以做我想做的事吗?
编辑:P.S。这是目前 launchREPL 中的代码:
def launchREPL(scope=nil,winName="Ruby REPL")
# ICM RB file Begin:
puts "\"Starting REPL...\""
__b = binding #Evaluating in a binding, keeps track of local variables
__s = ""
###############################################################################
# SEND INSTANCE VARIABLES TO REPL
###############################################################################
#
#How to prepare scope
# currentScope = []
# Kernel.local_variables.each do |var|
# currentScope << [var,Kernel.eval(var.to_s)]
# end
# launchREPL(currentScope)
if scope != nil
scope.each do |varDef|
__b.instance_variable_set "@#{varDef[0].to_s}" , varDef[1]
__b.eval("@#{varDef[0].to_s} = __b.instance_variable_get(:@#{varDef[0].to_s})")
end
end
# to get instance variables: __b.instance_variable_get(__b.instance_variables[0])
# or better: __b.instance_variable_get(:@pipe1)
#
###############################################################################
bStartup = true
while bStartup || __s != ""
# If startup required skip evaluation step
if !bStartup
#Evaluate command
begin
__ret = __s + "\n>" + __b.eval(__s).to_s
rescue
__ret = __s + "\n> Error: " + $!.to_s
end
puts __ret
else
#REPL is already running
bStartup = false
end
#Read user input & print previous output
__s = WSApplication.input_box(__ret,winName,"")
__s == nil ? __s = "" : nil
end
end
虽然您想要实现的目标尚不清楚,而且肯定有很多方法可以正确地做到这一点,但是 每个 ruby 方法都可以用 Object#send
方法:
def launchREPL(scope)
scope.send :local_variables #⇒ here you go
end
a = 42
launchREPL(binding).include?(:a)
#⇒ true
旁注:你的“4行”通常是这样写的ruby:
local_variables.map { |var| [var, eval(var.to_s)] }
他们应该这样写(注意Binding#local_variable_get
):
local_variables.map { |var| [var, binding.local_variable_get(var)] }
总结:
def launchREPL(scope)
vars = scope.send(:local_variables).map do |var|
[var, scope.local_variable_get(var)]
end
# some other code
end
a = 42
launchREPL(binding).to_h[:a]
#⇒ 42
这不适合评论,所以我会 post 它作为答案。
def launchREPL(scope = nil, winName = "Ruby REPL")
puts '"Starting REPL..."'
scope.eval('local_variables').each do |var|
instance_variable_set "@#{var}", scope.eval(var.to_s)
end if scope
s = ""
loop do
ret = begin
"#{s}\n> #{eval(s)}"
rescue => e
"#{s}\n> Error: #{e.message}"
end
puts ret
# s = WSApplication.input_box(ret, winName, "")
# break if s.empty?
s = "100 * @a" # remove this line and uncomment 2 above
end
end
a = 42
launchREPL(binding)
你的函数应该这样写(我刚刚让它看起来像 ruby 代码。)上面的工作(目前它根本没有 break
,但你可以看到它正在无限地计算 4200
。)
我正在制作一个 Ruby REPL 以在应用程序中使用。我做了代码:
a = 1
b = 2
currentScope = []
Kernel.local_variables.each do |var|
currentScope << [var,Kernel.eval(var.to_s)]
end
launchREPL(currentScope)
在REPL里面,我可以执行下面的代码:
@a #=>1
@a+@b #=>3
理想情况下,我不必在启动 REPL 之前编写这四行代码,相反,我想在 launchREPL
函数中 运行 它们。但是,这需要从 launchREPL
函数内部访问先前的作用域。
测试 1
最值得注意的是我尝试过:
launchREPL(Kernel)
当我执行以下操作时:
def launchREPL(scope)
F = 0
puts scope.local_variables # => [:F]
end
显然这个方法是无效的
测试2
launchREPL(Kernel.binding)
def launchREPL(scope)
Kernel.binding.local_variables #= Error: private method 'local_variables' called for #<Binding>
end
有什么方法可以做我想做的事吗?
编辑:P.S。这是目前 launchREPL 中的代码:
def launchREPL(scope=nil,winName="Ruby REPL")
# ICM RB file Begin:
puts "\"Starting REPL...\""
__b = binding #Evaluating in a binding, keeps track of local variables
__s = ""
###############################################################################
# SEND INSTANCE VARIABLES TO REPL
###############################################################################
#
#How to prepare scope
# currentScope = []
# Kernel.local_variables.each do |var|
# currentScope << [var,Kernel.eval(var.to_s)]
# end
# launchREPL(currentScope)
if scope != nil
scope.each do |varDef|
__b.instance_variable_set "@#{varDef[0].to_s}" , varDef[1]
__b.eval("@#{varDef[0].to_s} = __b.instance_variable_get(:@#{varDef[0].to_s})")
end
end
# to get instance variables: __b.instance_variable_get(__b.instance_variables[0])
# or better: __b.instance_variable_get(:@pipe1)
#
###############################################################################
bStartup = true
while bStartup || __s != ""
# If startup required skip evaluation step
if !bStartup
#Evaluate command
begin
__ret = __s + "\n>" + __b.eval(__s).to_s
rescue
__ret = __s + "\n> Error: " + $!.to_s
end
puts __ret
else
#REPL is already running
bStartup = false
end
#Read user input & print previous output
__s = WSApplication.input_box(__ret,winName,"")
__s == nil ? __s = "" : nil
end
end
虽然您想要实现的目标尚不清楚,而且肯定有很多方法可以正确地做到这一点,但是 每个 ruby 方法都可以用 Object#send
方法:
def launchREPL(scope)
scope.send :local_variables #⇒ here you go
end
a = 42
launchREPL(binding).include?(:a)
#⇒ true
旁注:你的“4行”通常是这样写的ruby:
local_variables.map { |var| [var, eval(var.to_s)] }
他们应该这样写(注意Binding#local_variable_get
):
local_variables.map { |var| [var, binding.local_variable_get(var)] }
总结:
def launchREPL(scope)
vars = scope.send(:local_variables).map do |var|
[var, scope.local_variable_get(var)]
end
# some other code
end
a = 42
launchREPL(binding).to_h[:a]
#⇒ 42
这不适合评论,所以我会 post 它作为答案。
def launchREPL(scope = nil, winName = "Ruby REPL")
puts '"Starting REPL..."'
scope.eval('local_variables').each do |var|
instance_variable_set "@#{var}", scope.eval(var.to_s)
end if scope
s = ""
loop do
ret = begin
"#{s}\n> #{eval(s)}"
rescue => e
"#{s}\n> Error: #{e.message}"
end
puts ret
# s = WSApplication.input_box(ret, winName, "")
# break if s.empty?
s = "100 * @a" # remove this line and uncomment 2 above
end
end
a = 42
launchREPL(binding)
你的函数应该这样写(我刚刚让它看起来像 ruby 代码。)上面的工作(目前它根本没有 break
,但你可以看到它正在无限地计算 4200
。)