30

変数がvar = "some_name"あり、新しいオブジェクトを作成して に割り当てたいと考えていsome_nameます。どうすればいいですか?例えば

var = "some_name"
some_name = Struct.new(:name) # I need this
a = some_name.new('blah') # so that I can do this.
4

4 に答える 4

39

Ruby 1.9+ では動的にローカル変数を作成することはできません (Ruby 1.8 では 経由で作成できましたeval):

eval 'foo = "bar"'
foo  # NameError: undefined local variable or method `foo' for main:Object

ただし、これらは eval-ed コード自体の中で使用できます。

eval 'foo = "bar"; foo + "baz"'
#=> "barbaz"

Ruby 2.1 が追加されましlocal_variable_setたが、新しいローカル変数を作成することもできません:

binding.local_variable_set :foo, 'bar'
foo # NameError: undefined local variable or method `foo' for main:Object

この動作は、 Ruby 自体を変更しない限り変更できません。別の方法として、多くのローカル変数ではなく、ハッシュなどの別のデータ構造内にデータを格納することを検討することもできます。

hash = {}
hash[:my_var] = :foo

evalと の両方で、既存のローカル変数を再割り当てできることに注意してくださいlocal_variable_set

foo = nil
eval 'foo = "bar"'
foo  #=> "bar"
binding.local_variable_set :foo, 'baz'
foo  #=> "baz"
于 2013-08-31T21:26:38.747 に答える
5

ruby 2.2.x といえば、現在のコンテキスト/バインディングでローカル変数をプログラムで作成できないのは事実ですが、ハンドルを持っている特定のバインディングで変数を設定することはできます。

b = binding
b.local_variable_set :gaga, 5
b.eval "gaga"
=> 5

ここで興味深いのは、呼び出しがbinding毎回新しいバインディングを提供することです。したがって、関心のあるバインディングのハンドルを取得し、必要な変数が設定されたら、そのコンテキストで評価する必要があります。

これはどのように役立ちますか?<%= myvar %>たとえば、ERB を評価したいのですが、ERBの代わりに<%= opts[:myvar] %>またはそのようなものを使用できれば、ERB を書く方がはるかに優れています。

新しいバインディングを作成するために、モジュールクラスメソッドを使用しています (誰かがこれを適切に呼び出す方法を修正してくれると確信しています。Java では静的メソッドと呼びます)。特定の変数が設定されたクリーンなバインディングを取得します。

module M
  def self.clean_binding
    binding
  end

  def self.binding_from_hash(**vars)
    b = self.clean_binding
    vars.each do |k, v|
      b.local_variable_set k.to_sym, v
    end
    return b
  end
end
my_nice_binding = M.binding_from_hash(a: 5, **other_opts)

これで、目的の変数のみを含むバインディングが作成されました。ERB やその他の (サード パーティの可能性がある)信頼できるコード (これはいかなる種類のサンドボックスでもありません) のより適切に管理された評価に使用できます。インターフェイスを定義するようなものです。

更新: バインディングに関するいくつかの追加メモ。それらを作成する場所も、メソッドの可用性と定数の解決に影響します。上記の例では、適度にクリーンなバインディングを作成しています。しかし、あるオブジェクトのインスタンス メソッドを利用できるようにしたい場合は、同様のメソッドを使用してバインドを作成できますが、そのオブジェクトのクラス内に作成できます。例えば

module MyRooTModule
  class Work
    def my_instance_method
      ...
    end
    def not_so_clean_binding
      binding
    end
  end
  class SomeOtherClass
  end
end

これで、コードがオブジェクトmy_object.not_so_clean_bindingを呼び出すことができるようになります。同様に、たとえばコード内で の代わりにこのバインディングを使用して呼び出すことができます。そのため、バインディングを作成する際には、ローカル変数だけでなく、より多くの考慮事項が必要になる場合があります。HTH#my_instance_methodmy_objectSomeOtherClass.newMyRootModule::SomeOtherClass.new

于 2015-12-28T21:36:35.397 に答える
2

他の人が指摘しているように、Ruby ではローカル変数を動的に作成することはできませんが、メソッドを使用してこの動作をある程度シミュレートできます。

hash_of_variables = {var1: "Value 1", var2: "Value 2"}

hash_of_variables.each do |var, val|
  define_method(var) do
    instance_variable_get("@__#{var}")
  end
  instance_variable_set("@__#{var}", val)
end

puts var1
puts var2
var1 = var2.upcase
puts var1

版画:

Value 1
Value 2
VALUE 2

一部のライブラリは、この手法を組み合わせてinstance_exec、ブロック内のローカル変数のように見えるものを公開します。

def with_vars(vars_hash, &block)
  scope = Object.new
  vars_hash.each do |var, val|
    scope.send(:define_singleton_method, var) do
      scope.instance_variable_get("@__#{var}")
    end
    scope.instance_variable_set("@__#{var}", val)
  end
  scope.instance_exec(&block)
end

with_vars(a: 1, b:2) do
  puts a + b
end

プリント: 3

ただし、抽象化は決して完璧ではないことに注意してください。

with_vars(a: 1, b:2) do
  a = a + 1
  puts a
end

結果: undefined method `+' for nil:NilClass。これは、メソッドよりも優先される、a=に初期化された実際のローカル変数を定義するためです。次に呼び出され、メソッドがないため、エラーがスローされます。nilaa.+(1)nil+

したがって、この方法は読み取り専用のローカル変数をシミュレートするのに非常に便利ですが、ブロック内で変数を再割り当てしようとすると、常にうまく機能するとは限りません。

于 2015-08-14T16:36:04.223 に答える