【问题标题】:In Ruby, is there no way to dynamically define a local variable in the current context? [duplicate]在 Ruby 中,有没有办法在当前上下文中动态定义局部变量? [复制]
【发布时间】:2013-10-19 17:51:08
【问题描述】:

我想知道是否有一种方法可以让我在当前上下文中动态定义以前未定义的变量。例如:

foo # => NameError: undefined method or local variable ...
# Some method call which sets foo = 1 in the local context
foo # => 1

换一种说法,鉴于foo 未定义,我正在寻找任何可以让我在不使用foo 变量的情况下定义局部变量foo 的代码(例如,如果我有一些其他变量@987654325 @ 其值为:foo,我不得不依靠它来设置foo 的值。

似乎eval('foo = 1')eval('foo = 1', binding) 或在Ruby 2.1 中binding.local_variable_set(:foo, 1) 都等价于:

1.times do
  foo = 1
end

换句话说,他们将foo 设置在一个本地上下文的上下文中,这样在该上下文之外就无法访问该值。

我想做的事可能吗?

更新:这个问题并不特定于任何特定的局部变量上下文(模块/类、方法、proc、块等)。我有兴趣明确了解可以或不能完成的任何上下文。

【问题讨论】:

  • 您要解决的问题是什么?无论您尝试做什么,在 ruby​​ 中都可能实现,但我认为这不是一件好事。
  • 不一样,但也许define_method(:foo) { 1 } 是一个选项?
  • 我知道它涉及变量范围,但我不明白你的问题。
  • @phoet 我在询问与任何需求无关的 Ruby 语言。这个问题是在另一个 SO 问题的背景下出现的。
  • @spickermann 我同意这将是相似的,但不一样。 :-)

标签: ruby local-variables


【解决方案1】:

Ruby 的魔法似乎提供了一种方法,但根据 Matz 的说法,这只能在 1.8 中通过eval 实现,并且只能在某些上下文中(即irb)。从 1.9 开始,此行为已被取消(“严格禁止”):

Matz 本人在这里称重:https://www.ruby-forum.com/topic/155673#685906

我从某个地方读到现在 Ruby 无法动态创建局部变量。这是真的还是只是一个错误?

局部变量是在编译时创建的,所以局部 eval() 中定义的变量不能在外部访问 评估。在 1.8 中,irb 和 tryruby 会逐行编译,这样 局部变量是从 eval() 溢出的,但在 1.9 中,它是严格的 即使在逐行编译下也被禁止。

          matz.

(此处为非推理替代方案,适用于任何想要这样但不是提问者所拥有的确切技术情况的人):

使用哈希:

local_hash = {}

my_vars.each_pair do |k,v|
   local_hash[k] = v
end

puts local_hash['foo']
#=> 'baz'

【讨论】:

  • 如果您更新您的答案,简单地说“不,它不能完成”并提供 Matz 参考,我将很高兴接受它。这假设除了 eval 无法实现之外,根据 Matz 关于仅在编译时创建的局部变量的解释,没有其他方法可以实现它。
  • 不幸的是我不能这样做...我只知道 Matz 的解释,但不能肯定地告诉你,仅仅因为它已通过 eval 停止,它不能通过另一种方法(除了运行 Ruby 1.8)。所以我可以让门敞开,而不是被 Ruby 黑客大师证明是错误的 :)
  • 好的,至少删除“我确定有办法”评论怎么样,在这种情况下,我会为 Matz 参考 +1。 :-) 虽然他们可能是一种方式,但我认为你没有任何理由“确定”,我不想给任何人虚假的希望。在相关的一点上,正如我关于这个问题的 cmets 所证明的那样,我并没有试图解决任何潜在的问题。我只是想提高我对语言和核心库的理解。因此,您涉及哈希的提议并不真正相关,从我的角度来看,最好将其删除。
  • 完成。但我希望有人证明我错了,并想出一些聪明的办法,虽然我无法想象这可能是什么,因为 Matz 的解释
  • 接受并加 1。:-)
【解决方案2】:

类实例变量对你有用吗?

class Cat
end
Cat.instance_variable_set '@last_words', 'meow, meow, me...'
Cat.instance_variable_get '@last_words' # => "meow, meow, me..."
Cat.new.instance_variable_get '@last_words' # => nil

如果不是,请详细说明上下文以及如何使用局部变量。

【讨论】:

  • 我的问题与局部变量所在的位置(类、方法、块等)无关。根据我之前对问题本身的评论,我没有试图解决潜在的问题。我只是问一个与局部变量声明相关的语言和核心库的问题,以便更好地理解。
【解决方案3】:

在创建局部变量本身的上下文中,确实有一些困难需要克服,但是动态分配仍然没有问题。

>> my_lv = 0
=> 0
>> instance_eval("#{'my_lv'} = 42")
=> 42
>> my_lv
=> 42

因此,只需从收集的输入创建(来自gets,根据需要进行压缩或剥离,它自然会以字符串形式结束)并在其上调用to_sym,并将新符号填充到local_variables 和评估...

>> local_variables << :my_created_lv
=> [:my_lv,
 :__,
 :_,
 :_dir_,
 :_file_,
 :_ex_,
 :_pry_,
 :_out_,
 :_in_,
 :my_created_lv]
>> 

然后您将收集的字符串转换为符号,并在上面显示的代码中分配给它,然后对其进行评估以获取值。

>> eval :my_lv.to_s
>> 24

正如另一个答案中所述,我无法在 Pry 或 IRB 之外轻松复制此内容。

这在 Ruby 的未来版本中发生了变化,因为 Matz 已删除并努力使这种情况不再发生。

【讨论】:

  • 我问的是尚未定义变量的情况。如果变量已经定义并且您只想更改值,则没有问题。
  • 这就是我在插值中使用字符串的原因。其余的都是学术性的。
  • 显然只能在 REPL 中工作。 :(
  • 对不起,我没有关注你的最后两个 cmets。
  • 我拿了我在答案中写的命令,写了一个普通的Ruby程序,试图验证它是否仍然像普通的Ruby程序一样工作,而不是逐行评估,并且它被限制工作在常规的 Ruby 程序中。 REPL 的意思是“读取 - 评估 - 打印 - 循环”。先前的意思只是说可以通过多种方式接收插值内部的字符串,也许是gets.chomp。无论哪种方式,在 Ruby 1.9 及更高版本中都不会轻易被击败。
猜你喜欢
  • 1970-01-01
  • 2021-05-21
  • 2020-11-30
  • 2011-06-25
  • 2011-04-21
  • 2010-12-30
  • 2015-05-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多