الوصول إلى المتغيرات برمجياً بالاسم في روبي

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

  •  09-06-2019
  •  | 
  •  

سؤال

لست متأكدًا تمامًا مما إذا كان هذا ممكنًا في روبي، ولكن نأمل أن تكون هناك طريقة سهلة للقيام بذلك.أريد الإعلان عن متغير ومعرفة اسم المتغير لاحقًا.يعني بالنسبة لهذا المقتطف البسيط:

foo = ["goo", "baz"]

كيف يمكنني الحصول على اسم المصفوفة (هنا، "foo") مرة أخرى؟إذا كان ذلك ممكنًا بالفعل، فهل يعمل هذا على أي متغير (على سبيل المثال، العددية، والتجزئة، وما إلى ذلك)؟

يحرر:إليك ما أحاول القيام به بشكل أساسي.أنا أكتب خادم 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
  }

إذا كانت هناك فرصة لعدم تعريف أحد المتغيرات على الإطلاق (بدلاً من عدم كونها مصفوفة)، فقد ترغب في إضافة "rescue nil" إلى نهاية السطر "param = ..." للحفاظ على التقييم من طرح الاستثناء..

نصائح أخرى

تحتاج إلى إعادة تصميم الحل الخاص بك.حتى لو كنت استطاع افعل ذلك (لا يمكنك)، فالسؤال ببساطة ليس له إجابة معقولة.

تخيل طريقة get_name.

a = 1
get_name(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" أو ربما مصفوفة كل منهم؟حتى لو قامت بإرجاع مصفوفة، فماذا سيكون الترتيب وكيف أعرف كيفية تفسير النتائج؟

الإجابة على جميع الأسئلة أعلاه هي "الأمر يعتمد على الشيء المعين الذي أريده في ذلك الوقت." لست متأكدًا من كيفية حل هذه المشكلة لروبي.

يبدو أنك تحاول حل مشكلة لها حل أسهل بكثير..

لماذا لا نقوم فقط بتخزين البيانات في تجزئة؟اذا فعلت..

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

.. ومن التافه تمامًا الحصول على اسم "foo".

ومع ذلك، لم تقدم أي سياق للمشكلة، لذلك قد يكون هناك سبب يمنعك من القيام بذلك.

[يحرر] بعد التوضيح أرى المشكلة، لكن لا أعتقد أن هذه هي المشكلة..مع [foo, bar, bla]، فهو يعادل القول ['content 1', 'content 2', 'etc'].اسم المتغيرات الفعلي (أو بالأحرى، ينبغي أن يكون) غير ذي صلة على الإطلاق.إذا كان اسم المتغير مهمًا، فهذا هو بالضبط سبب وجود التجزئة.

لا تكمن المشكلة في التكرار عبر [foo, bar] وما إلى ذلك، إنها المشكلة الأساسية في كيفية استعادة خادم SOAP للبيانات و/أو كيف تحاول استخدامها.

أود أن أقول إن الحل هو إما جعل خادم SOAP يعرض التجزئات، أو بما أنك تعلم أنه سيكون هناك دائمًا ثلاثة عناصر، ألا يمكنك فعل شيء مثل..

{"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

حسنًا، إنها تعمل في طرق المثيل أيضًا، واستنادًا إلى متطلباتك المحددة (التي وضعتها في التعليق)، يمكنك القيام بذلك:

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

فقط استبدل Fixnum مع فحص نوع معين الخاص بك.

لا أعرف أي طريقة للحصول على اسم متغير محلي.ولكن، يمكنك استخدام instance_variables الطريقة، سيؤدي ذلك إلى إرجاع مصفوفة من كافة أسماء متغيرات المثيلات في الكائن.

مكالمة بسيطة:

object.instance_variables

أو

self.instance_variables

للحصول على مجموعة من كافة أسماء متغيرات المثيل.

بناء على joshmsmoore, ، شيء مثل هذا من المحتمل أن يفعل ذلك:

# 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

المهم هو أن هذا لا يعمل إلا عندما يكون المتغير الذي يتم تنفيذ المهمة عليه ثابتًا.(نعلم جميعًا أن ثوابت روبي ليست أكثر من متغيرات حساسة عاطفيًا.) وبالتالي:

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

لقد حققت سلوك تقليد السحر المستمر من خلال البحث في جميع الثوابت في جميع مساحات الأسماء من مساحة كائن روبي الحالية.يؤدي هذا إلى إهدار جزء من الثانية، ولكن بما أن البحث يتم مرة واحدة فقط، فلا توجد عقوبة على الأداء بمجرد اكتشاف الكائن لاسمه.في المستقبل، فريق روبي الأساسي قد وعد const_assigned خطاف.

لا يمكنك ذلك، فأنت بحاجة إلى العودة إلى لوحة الرسم وإعادة هندسة الحل الخاص بك.

Foo هو مجرد موقع لوضع مؤشر على البيانات.البيانات ليس لديها معرفة بما يشير إليها.في أنظمة Smalltalk، يمكنك أن تطلب من VM جميع المؤشرات إلى كائن ما، ولكن ذلك لن يؤدي إلا إلى الحصول على الكائن الذي يحتوي على المتغير foo، وليس foo نفسه.لا توجد طريقة حقيقية للإشارة إلى شيء قابل للتنفيذ في روبي.كما هو مذكور في إحدى الإجابات، لا يزال بإمكانك وضع علامة في البيانات تشير إلى مصدرها أو ما شابه، ولكن بشكل عام لا يعد هذا نهجًا جيدًا لمعظم المشكلات.يمكنك استخدام التجزئة لتلقي القيم في المقام الأول، أو استخدام التجزئة لتمريرها إلى حلقتك حتى تعرف اسم الوسيطة لأغراض التحقق من الصحة كما في إجابة DBR.

أقرب شيء إلى إجابة حقيقية لسؤالك هو استخدام التابع Enumerable every_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

لقد قمت بإزالة عبارة الإرجاع من الكتلة التي كنت تمررها إلى every/each_with_index لأنها لم تفعل/تعني أي شيء.يقوم كل منهما وeach_with_index بإرجاع المصفوفة التي كانا يعملان عليها.

هناك أيضًا شيء يتعلق بالنطاق في الكتل الجدير بالملاحظة هنا:إذا قمت بتعريف متغير خارج الكتلة، فسيكون متاحًا بداخلها.بمعنى آخر، يمكنك الإشارة إلى foo وbar وbaz مباشرة داخل الكتلة.والعكس ليس صحيحا:المتغيرات التي تقوم بإنشائها لأول مرة داخل الكتلة لن تكون متاحة خارجها.

أخيرًا، يُفضل استخدام صيغة do/end للكتل متعددة الأسطر، ولكن هذه مجرد مسألة أسلوب، على الرغم من أنها عالمية في كود روبي لأي قديم حديث.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top