Ruby で名前を使用してプログラムで変数にアクセスする

StackOverflow https://stackoverflow.com/questions/58482

  •  09-06-2019
  •  | 
  •  

質問

これが Ruby で可能かどうかは完全にはわかりませんが、簡単に実行できる方法があることを願っています。変数を宣言し、後で変数の名前を調べたいと考えています。つまり、この単純なスニペットの場合は次のようになります。

foo = ["goo", "baz"]

配列の名前 (ここでは「foo」) を戻すにはどうすればよいですか?本当に可能な場合、これは任意の変数 (スカラー、ハッシュなど) で機能しますか?

編集:基本的に私がやろうとしていることは次のとおりです。私は 3 つの重要な変数を持つクラスをラップする SOAP サーバーを作成しています。検証コードは基本的に次のとおりです。

  [foo, goo, bar].each { |param|
      if param.class != Array
        puts "param_name wasn't an Array. It was a/an #{param.class}"
        return "Error: param_name wasn't an Array"
      end
      }

そこで私の質問は次のとおりです。「param_name」のインスタンスを foo、goo、または bar に置き換えることはできますか?これらのオブジェクトはすべて配列であるため、これまでに受け取った答えは機能しないようです(全体を再設計することを除いて) dbrの答え)

役に立ちましたか?

解決

問題を方向転換したらどうなるでしょうか?変数から名前を取得しようとする代わりに、名前から変数を取得します。

["foo", "goo", "bar"].each { |param_name|
  param = eval(param_name)
  if param.class != Array
    puts "#{param_name} wasn't an Array. It was a/an #{param.class}"
    return "Error: #{param_name} wasn't an Array"
  end
  }

(配列ではなく) 変数がまったく定義されていない可能性がある場合は、eval を維持するために「param = ...」行の末尾に「rescue nil」を追加する必要があります。例外をスローすることから...

他のヒント

ソリューションを再設計する必要があります。たとえあなたが できた それをしてください(できません)、この質問には合理的な答えがありません。

get_name メソッドを想像してください。

a = 1
get_name(a)

これは「a」を返す必要があることにおそらく誰もが同意するでしょう。

b = a
get_name(b)

「b」、「a」、または両方を含む配列を返す必要がありますか?

[b,a].each do |arg|
  get_name(arg)
end

'arg'、'b'、または 'a' を返す必要がありますか?

def do_stuff( arg )
  get_name(arg)
do
do_stuff(b)

'arg'、'b'、または 'a'、あるいはそれらすべての配列を返す必要がありますか?たとえ配列が返されたとしても、順序はどうなるでしょうか?また、結果の解釈方法はどうすればわかるでしょうか?

上記のすべての質問に対する答えは、「当時私が望む特定のものに依存します」です。 Rubyの問題をどのように解決できるかわかりません。

あなたは、はるかに簡単な解決策がある問題を解決しようとしているようです。

データをハッシュに保存しないのはなぜでしょうか?もし、するなら..

data_container = {'foo' => ['goo', 'baz']}

..そうすると、「foo」という名前を取得するのはまったく簡単になります。

そうは言っても、問題のコンテキストを何も与えていないので、これを実行できない理由がある可能性があります。

[編集] 説明した結果、問題があることがわかりましたが、これが問題ではないと思います。[foo, bar, bla] を使用すると、次のように言うのと同じです。 ['content 1', 'content 2', 'etc']. 。実際の変数名はまったく無関係です (というより、そうあるべきです)。変数の名前が重要である場合、まさにそれがハッシュが存在する理由です。

問題は [foo, bar] などの反復処理にあるのではなく、SOAP サーバーがデータを返す方法やデータをどのように使用しようとしているかという根本的な問題です。

解決策は、SOAP サーバーがハッシュを返すようにするか、常に 3 つの要素があることがわかっているので、次のようなことはできないかのどちらかだと思います。

{"foo" => foo, "goo" => goo, "bar"=>bar}.each do |param_name, param|
      if param.class != Array
        puts "#{param_name} wasn't an Array. It was a/an #{param.class}"
        puts "Error: #{param_name} wasn't an Array"
      end
end

OK、これはインスタンス メソッドでも機能します。特定の要件 (コメントに入力したもの) に基づいて、次のようにすることができます。

local_variables.each do |var|
  puts var if (eval(var).class != Fixnum)
end

交換するだけ Fixnum 特定の型チェックを使用します。

ローカル変数名を取得する方法がわかりません。ただし、使用できます instance_variables メソッドを使用すると、オブジェクト内のすべてのインスタンス変数名の配列が返されます。

簡単な呼び出し:

object.instance_variables

または

self.instance_variables

すべてのインスタンス変数名の配列を取得します。

基礎を築く ジョシュムスムーア, 、次のようなものがおそらくそれを行うでしょう:

# Returns the first instance variable whose value == x
# Returns nil if no name maps to the given value
def instance_variable_name_for(x)
  self.instance_variables.find do |var|
    x == self.instance_variable_get(var)
  end
end

あるよ Kernel::local_variables, 、しかし、これがメソッドのローカル変数で機能するかどうかはわかりません。また、達成したいことを行うように操作できるかどうかもわかりません。

素晴らしい質問です。あなたの動機はよくわかります。まず、特定の種類の特別なオブジェクトがあり、特定の状況下で、それらが割り当てられている変数についての知識を持っていることに注意してください。これらの特別なオブジェクトは次のとおりです。 Module インスタンス、 Class インスタンスと Struct インスタンス:

Dog = Class.new
Dog.name # Dog

問題は、これは代入が実行される変数が定数である場合にのみ機能するということです。(Ruby 定数が感情的に敏感な変数にすぎないことは誰もが知っています。) したがって、次のようになります。

x = Module.new # creating an anonymous module
x.name #=> nil # the module does not know that it has been assigned to x
Animal = x # but will notice once we assign it to a constant
x.name #=> "Animal"

どの変数に割り当てられているかを認識するオブジェクトのこの動作は、一般に次のように呼ばれます。 一定の魔法 (定数に限定されているため)。しかし、これは非常に望ましいことです 一定の魔法 特定のオブジェクトに対してのみ機能します。

Rover = Dog.new
Rover.name #=> raises NoMethodError

幸いなことに、 宝石を書きました y_support/name_magic, 、これはあなたのために次のように処理されます。

 # first, gem install y_support
require 'y_support/name_magic'

class Cat
  include NameMagic
end

実際、これは定数でのみ機能します(つまり、大文字で始まる変数) は、それほど大きな制限ではありません。実際、オブジェクトに名前を付けるか付けないかは自由に選べます。

tmp = Cat.new # nameless kitty
tmp.name #=> nil
Josie = tmp # by assigning to a constant, we name the kitty Josie
tmp.name #=> :Josie

残念ながら、配列リテラルは使用せずに内部的に構築されるため、これは配列リテラルでは機能しません。 #new メソッド、その上で NameMagic 依存します。したがって、目的を達成するには、サブクラス化する必要があります Array:

require 'y_support/name_magic'
class MyArr < Array
  include NameMagic
end

foo = MyArr.new ["goo", "baz"] # not named yet
foo.name #=> nil
Foo = foo # but assignment to a constant is noticed
foo.name #=> :Foo

# You can even list the instances
MyArr.instances #=> [["goo", "baz"]]
MyArr.instance_names #=> [:Foo]

# Get an instance by name:
MyArr.instance "Foo" #=> ["goo", "baz"]
MyArr.instance :Foo #=> ["goo", "baz"]

# Rename it:
Foo.name = "Quux"
Foo.name #=> :Quux

# Or forget the name again:
MyArr.forget :Quux
Foo.name #=> nil

# In addition, you can name the object upon creation even without assignment
u = MyArr.new [1, 2], name: :Pair
u.name #=> :Pair
v = MyArr.new [1, 2, 3], ɴ: :Trinity
v.name #=> :Trinity

私が一定の魔法を模倣する行動を実現したのは、 すべての名前空間内のすべての定数を検索する 現在の Ruby オブジェクト空間の。これにはほんの数秒が無駄になりますが、検索は 1 回だけ実行されるため、オブジェクトがその名前を見つけてしまえば、パフォーマンスに悪影響を与えることはありません。今後はRubyコアチームが 約束した const_assigned.

それはできません。振り出しに戻ってソリューションを再設計する必要があります。

Foo はデータへのポインタを保持する場所にすぎません。データには、それが何を指しているのかがわかりません。Smalltalk システムでは、VM にオブジェクトへのすべてのポインタを要求できますが、それでは foo 変数を含むオブジェクトのみが取得され、foo 自体は取得されません。Ruby では変数を参照する実際の方法はありません。1 つの回答で述べたように、データの出所などを参照するタグをデータ内に配置することはできますが、一般に、これはほとんどの問題に対する良いアプローチではありません。最初にハッシュを使用して値を受け取ることも、DBR の回答のように検証目的で引数名を知るためにハッシュを使用してループに渡すこともできます。

あなたの質問に対する実際の答えに最も近いのは、 each の代わりに Enumerable メソッド each_with_index を使用することです。つまり、次のようになります。

my_array = [foo, baz, bar]
my_array.each_with_index do |item, index|
  if item.class != Array
    puts "#{my_array[index]} wasn't an Array. It was a/an #{item.class}"
  end
end

each/each_with_index に渡していたブロックから return ステートメントを削除しました。これは何も行わず、意味もなかったからです。each と each_with_index はどちらも、操作対象の配列を返します。

ここで、ブロック内のスコープについても注目すべき点があります。ブロックの外で変数を定義した場合、その変数はブロック内で使用できます。つまり、ブロック内で直接 foo、bar、baz を参照できます。逆は当てはまりません。ブロック内で初めて作成した変数は、ブロックの外では使用できません。

最後に、複数行ブロックでは do/end 構文が好まれますが、これは単にスタイルの問題ですが、最近の Ruby コードでは普遍的ではあります。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top