web-dev-qa-db-ja.com

ローカル変数をRuby

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

だから次のようなもの:

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

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

25

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

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

13
Dorkus Prime

将来の読者のための追加情報として、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

doc で述べられているように、これは

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

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

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のコードが機能する唯一の理由です。

10
Anno2001

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

なぜ?

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

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

シナリオ:

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

  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
6
Kelvin

定数が必要ないので

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

0
Anno2001