30

Rubyでローカル変数を動的に設定することに興味があります。メソッド、定数、またはインスタンス変数を作成しません。

だから次のようなもの:

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

問題のメソッドがモデル内に存在し、グローバル空間またはオブジェクト空間を汚染したくないため、特にローカル変数が必要です。

4

5 に答える 5

28

将来の読者のための追加情報として、ruby 2.1.0以降、以下を使用できbinding.local_variable_getます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

ドキュメントに記載されているように、これは

binding.eval("#{symbol} = #{obj}")
binding.eval("#{symbol}")
于 2015-02-27T14:39:25.550 に答える
14

ここでの問題は、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']

もちろん、この例では、キーの文字列を含む元のハッシュをコピーしているだけです。ただし、実際のユースケースはもっと複雑だと思います。

于 2011-02-10T23:32:58.343 に答える
10

興味深いことに、ローカル変数を変更することはできますが、設定することはできません。

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

テスト=>7

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

test => NameError:main:Objectの未定義のローカル変数またはメソッド `x'

これが、DorkusPrimeのコードが機能する唯一の理由です。

于 2013-01-10T19:19:40.790 に答える
6

ハッシュを使用することをお勧めします(ただし、他の選択肢については読み続けてください)。

なんで?

任意の名前付き引数を許可すると、コードが非常に不安定になります。

fooこれらの理論上の名前付き引数を受け入れたいメソッドがあるとしましょう。

シナリオ:

  1. 呼び出されたメソッド( )は、引数をとらないfooプライベートメソッド(呼び出しましょう)を呼び出す必要があります。ローカル変数に格納したいbar引数を渡すと、メソッドがマスクされます。回避策は、を呼び出すときに明示的な括弧を使用することです。foobarbarbar

  2. fooのコードがローカル変数を割り当てているとしましょう。ただし、呼び出し元は、そのローカル変数と同じ名前の引数を渡すことにします。割り当ては引数を覆い隠します。

基本的に、メソッドの呼び出し元は、メソッドのロジックを変更できないようにする必要があります。

代替案

別の中間点にはが含まれ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
于 2013-06-28T16:53:57.497 に答える
1

定数が必要ないので

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

于 2013-01-10T19:10:34.447 に答える