在 Ruby 中动态设置局部变量

发布于 2024-10-16 18:51:31 字数 218 浏览 3 评论 0原文

我对在 Ruby 中动态设置局部变量感兴趣。不创建方法、常量或实例变量。

所以类似:

args[:a] = 1
args.each_pair do |k,v|
  Object.make_instance_var k,v
end
puts a
> 1

我特别想要局部变量,因为所讨论的方法存在于模型中,并且我不想污染全局或对象空间。

I'm interested in dynamically setting local variables in Ruby. Not creating methods, constants, or instance variables.

So something like:

args[:a] = 1
args.each_pair do |k,v|
  Object.make_instance_var k,v
end
puts a
> 1

I want locally variables specifically because the method in question lives in a model and I dont want to pollute the global or object space.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

演多会厌 2024-10-23 18:51:31

作为给未来读者的附加信息,从 ruby​​ 2.1.0 开始,您可以使用 binding.local_variable_getbinding.local_variable_set

def foo
  a = 1
  b = binding
  b.local_variable_set(:a, 2) # set existing local variable `a'
  b.local_variable_set(:c, 3) # create new local variable `c'
                              # `c' exists only in binding.
  b.local_variable_get(:a) #=> 2
  b.local_variable_get(:c) #=> 3
  p a #=> 2
  p c #=> NameError
end

文档,它的行为类似于

binding.eval("#{symbol} = #{obj}")
binding.eval("#{symbol}")

As an additional information for future readers, starting from ruby 2.1.0 you can using binding.local_variable_get and binding.local_variable_set:

def foo
  a = 1
  b = binding
  b.local_variable_set(:a, 2) # set existing local variable `a'
  b.local_variable_set(:c, 3) # create new local variable `c'
                              # `c' exists only in binding.
  b.local_variable_get(:a) #=> 2
  b.local_variable_get(:c) #=> 3
  p a #=> 2
  p c #=> NameError
end

As stated in the doc, it is a similar behavior to

binding.eval("#{symbol} = #{obj}")
binding.eval("#{symbol}")
っ左 2024-10-23 18:51:31

这里的问题是each_pair 内的块具有不同的范围。其中分配的任何局部变量只能在其中访问。例如,这个:

args = {}
args[:a] = 1
args[:b] = 2

args.each_pair do |k,v|
  key = k.to_s
  eval('key = v')
  eval('puts key')
end

puts a

产生这个:

1
2
undefined local variable or method `a' for main:Object (NameError)

为了解决这个问题,您可以创建一个本地散列,将键分配给这个散列,然后在那里访问它们,如下所示:

args = {}
args[:a] = 1
args[:b] = 2

localHash = {}
args.each_pair do |k,v|
  key = k.to_s
  localHash[key] = v
end

puts localHash['a']
puts localHash['b']

当然,在这个例子中,它只是复制原始散列键的字符串。不过,我假设实际的用例更复杂。

The problem here is that the block inside each_pair has a different scope. Any local variables assigned therein will only be accessible therein. For instance, this:

args = {}
args[:a] = 1
args[:b] = 2

args.each_pair do |k,v|
  key = k.to_s
  eval('key = v')
  eval('puts key')
end

puts a

Produces this:

1
2
undefined local variable or method `a' for main:Object (NameError)

In order to get around this, you could create a local hash, assign keys to this hash, and access them there, like so:

args = {}
args[:a] = 1
args[:b] = 2

localHash = {}
args.each_pair do |k,v|
  key = k.to_s
  localHash[key] = v
end

puts localHash['a']
puts localHash['b']

Of course, in this example, it's merely copying the original hash with strings for keys. I'm assuming that the actual use-case, though, is more complex.

夜访吸血鬼 2024-10-23 18:51:31

有趣的是,您可以更改局部变量,但不能设置它:

def test
  x=3
  eval('x=7;')
  puts x
end

test =>
7

def test
  eval('x=7;')
  puts x
end

测试 =>
NameError: main:Object 的未定义局部变量或方法“x”

这是 Dorkus Prime 代码有效的唯一原因。

interesting, you can change a local variable but you cannot set it:

def test
  x=3
  eval('x=7;')
  puts x
end

test =>
7

def test
  eval('x=7;')
  puts x
end

test =>
NameError: undefined local variable or method `x' for main:Object

This is the only reason why Dorkus Prime's code works.

为你拒绝所有暧昧 2024-10-23 18:51:31

我建议您使用哈希(但请继续阅读其他替代方案)。

为什么?

允许任意命名参数会导致代码极其不稳定。

假设您有一个方法 foo,您想要接受这些理论上的命名参数。

场景:

  1. 被调用的方法 (foo) 需要调用一个不带参数的私有方法(我们称之为 bar)。如果您向 foo 传递一个想要存储在局部变量 bar 中的参数,它将屏蔽 bar 方法。解决方法是在调用 bar 时使用显式括号。

  2. 假设foo的代码分配了一个局部变量。但随后调用者决定传入一个与该局部变量同名的 arg。分配将破坏参数。

基本上,方法的调用者永远不能改变方法的逻辑。

替代方案

另一种中间立场涉及OpenStruct。与使用哈希相比,它需要更少的输入。

require 'ostruct'
os = OpenStruct.new(:a => 1, :b => 2)
os.a  # => 1
os.a = 2  # settable
os.foo  # => nil

请注意,OpenStruct 允许您访问不存在的成员 - 它将返回 nil。如果您想要更严格的版本,请改用 Struct

这将创建一个匿名类,然后实例化该类。

h = {:a=>1, :b=>2}
obj = Struct.new(* h.keys).new(* h.values)
obj.a  # => 1
obj.a = 2  # settable
obj.foo  # NoMethodError

I suggest you use the hash (but keep reading for other alternatives).

Why?

Allowing arbitrary named arguments makes for extremely unstable code.

Let's say you have a method foo that you want to accept these theoretical named arguments.

Scenarios:

  1. The called method (foo) needs to call a private method (let's call it bar) that takes no arguments. If you pass an argument to foo that you wanted to be stored in local variable bar, it will mask the bar method. The workaround is to have explicit parentheses when calling bar.

  2. Let's say foo's code assigns a local variable. But then the caller decides to pass in an arg with the same name as that local variable. The assign will clobber the argument.

Basically, a method's caller must never be able to alter the logic of the method.

Alternatives

An alternate middle ground involves OpenStruct. It's less typing than using a hash.

require 'ostruct'
os = OpenStruct.new(:a => 1, :b => 2)
os.a  # => 1
os.a = 2  # settable
os.foo  # => nil

Note that OpenStruct allows you access non-existent members - it'll return nil. If you want a stricter version, use Struct instead.

This creates an anonymous class, then instantiates the class.

h = {:a=>1, :b=>2}
obj = Struct.new(* h.keys).new(* h.values)
obj.a  # => 1
obj.a = 2  # settable
obj.foo  # NoMethodError
我三岁 2024-10-23 18:51:31

因为您不需要常量

args = {}
args[:a] = 1
args[:b] = 2

args.each_pair{|k,v|eval "@#{k}=#{v};"}

puts @b

2

您可能会发现这种方法很有趣(在正确的上下文中评估变量)

fn="b*b"
vars=""
args.each_pair{|k,v|vars+="#{k}=#{v};"}
eval vars + fn

4

since you don't want constants

args = {}
args[:a] = 1
args[:b] = 2

args.each_pair{|k,v|eval "@#{k}=#{v};"}

puts @b

2

you might find this approach interesting ( evaluate the variables in the right context)

fn="b*b"
vars=""
args.each_pair{|k,v|vars+="#{k}=#{v};"}
eval vars + fn

4

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文