سؤال

أنا أفكر في:

class X
    def new()
        @a = 1
    end
    def m( other ) 
         @a == other.@a
    end
end

x = X.new()
y = X.new()
x.m( y ) 

لكنه لا يعمل.

رسالة الخطأ هي:

syntax error, unexpected tIVAR

كيف يمكنني مقارنة سماتهما الخاصة من نفس الفصل بعد ذلك؟

هل كانت مفيدة؟

المحلول

هناك العديد من الطرق

Getter:

class X
  attr_reader :a
  def m( other )
    a == other.a
  end
end

instance_eval:

class X
  def m( other )
    @a == other.instance_eval { @a }
  end
end

instance_variable_get:

class X
  def m( other )
    @a == other.instance_variable_get :@a
  end
end

لا أعتقد أن الروبي لديه مفهوم "صديق" أو "محمي"، وحتى "خاص" يتم اختراقه بسهولة. يؤدي استخدام Getter إلى إنشاء خاصية للقراءة فقط، ويعني مثيل_EVAL أن عليك معرفة اسم المتغير المثيل، وبالتالي فإن الدلالة مشابهة.

نصائح أخرى

كانت هناك بالفعل العديد من الإجابات الجيدة على مشكلتك الفورية، لكنني لاحظت بعض القطع الأخرى من التعليمات البرمجية التي تضمن التعليق. (معظمهم تافهة، رغم ذلك.)

فيما يلي أربعة منها تافهة، كلها مرتبطة بأسلوب الترميز:

  1. المسافة البادئة: أنت خلط 4 مساحات للمسافة البادئة و 5 مسافات. من الأفضل عموما التمسك فقط واحد أسلوب المسافة البادئة، وفي روبي هو عموما 2 مساحات.
  2. إذا كانت هناك طريقة لا تأخذ أي معلمات، فمن المعتاد أن تغادر النظارات النارية في تعريف الطريقة.
  3. وبالمثل، إذا قمت بإرسال رسالة دون وسيطات، يتم إيقاف تشغيل الجوارب.
  4. لا توجد مسافة بيضاء بعد خطوة فتح وقبل إغلاق واحد، إلا في كتل.

على أي حال، هذه مجرد الاشياء الصغيرة. الاشياء الكبيرة هي:

def new
  @a = 1
end

هذا لا ليس افعل ما تعتقد أنه يفعل ذلك! هذا يحدد نموذج طريقة دعا X#new و ليس طريقة فئة تسمى X.new!

ما تتصل هنا:

x = X.new

هو صف دراسي طريقة دعا new, ، الذي ورثته من Class صف دراسي. لذلك، أنت لا تتصل بهذه الطريقة الجديدة، مما يعني @a = 1 لا يتم تنفيذها أبدا، مما يعني @a غير محدد دائما، مما يعني أنه ستقيم دائما ل nil مما يعني @a من self و ال @a من other سوف تكون دائما نفس الشيء الذي يعني m سوف يكون دائما true!

ما تريد القيام به هو توفير منشئ، باستثناء روبي لا يملك منشئون. روسي يستخدم فقط طرق المصنع.

طريقة لك حقا مطلوب لتجاوز هو نموذج طريقة initialize. وبعد الآن ربما تسأل نفسك: "لماذا يجب أن أتجاوز نموذج طريقة دعا initialize عندما أنا فعلا استدعاء صف دراسي طريقة دعا new?"

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

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

لذلك، من أجل إنشاء كائن جديد تماما، ما عليك القيام به هو:

x = X.allocate
x.initialize

ملاحظة: قد يتعرف المبرمجون الهدف على هذا.

ومع ذلك، لأنه من السهل جدا أن تنسى الاتصال initialize وكقاعدة عامة يجب أن يكون كائن صالح تماما بعد البناء، هناك طريقة مصنع راحة تسمى Class#new, ، والتي كلها تعمل من أجلك وتبدو شيء مثل هذا:

class Class
  def new(*args, &block)
    obj = alloc
    obj.initialize(*args, &block)

    return obj
  end
end

ملاحظة: في الواقع، initialize هو خاص، لذلك يجب استخدام الانعكاس للتحايل على قيود الوصول مثل هذا: obj.send(:initialize, *args, &block)]

أخيرا، اسمحوا لي أن أشرح ما الذي يحدث في حياتك m طريقة. (أوضح الآخرون بالفعل كيفية حلها.)

في Ruby، لا توجد وسيلة (ملاحظة: في Ruby، "لا توجد طريقة" يترجم فعلا إلى "هناك دائما طريقة تنطوي على انعكاس") للوصول إلى متغير مثيل من خارج المثيل. لهذا السبب يسمى متغير مثيل بعد كل شيء، لأنه ينتمي إلى المثيل. هذه إرث من SmallTalk: في Smalltalk لا توجد قيود على الرؤية، الكل الأساليب عامة. وبالتالي، فإن المثيل المتغيرات هي فقط طريقة للقيام بتغليفها في Smalltalk، وبعد كل شيء، تعد التغليف واحدا من أعمدة OO. في روبي، هناك نكون قيود الرؤية (كما رأينا أعلاه، على سبيل المثال)، لذلك فهي ليست ضرورية بدقة لإخفاء متغيرات المثيل لهذا السبب. هناك سبب آخر، ومع ذلك: مبدأ الوصول الموحد.

تنص uap على كيفية استعمال يجب أن تكون الميزة مستقلة عن كيفية وجود ميزة منفذ. وبعد لذلك، يجب أن تكون الوصول إلى ميزة هي نفسها، أي موحدة. السبب في ذلك هو أن مؤلف الميزة مجاني لتغيير كيفية عمل الميزة داخليا، دون كسر مستخدمي الميزة. بمعنى آخر، إنها مشرفة أساسية.

هذه الوسيلة على سبيل المثال أن الحصول على حجم المجموعة يجب أن تكون هي نفسها دائما، بغض النظر عما إذا كان الحجم يتم تخزينه في متغير، محسوب ديناميكيا في كل مرة، قامت بحسابها في المرة الأولى ثم تخزينها في متغير أو مذكرت أو أيا كان. يبدو واضحا، ولكن على سبيل المثال جافا يحصل هذا الخطأ:

obj.size # stored in a field

ضد.

obj.getSize() # computed

روبي يأخذ السهل الخروج. في روبي، هناك فقط واحد طريقة لاستخدام ميزة: إرسال رسالة. نظرا لوجود طريقة واحدة فقط، فإن الوصول موحدة تافهة.

لذلك، لجعل قصة قصيرة طويلة: يمكنك ببساطة عدم الوصول إلى متغير مثيل مثيل آخر. يمكنك التفاعل فقط مع هذه الحالة عبر إرسال الرسائل. مما يعني أن الكائن الآخر يجب أن يوفر لك طريقة (في هذه الحالة على الأقل protected الرؤية) للوصول إلى متغير مثيلها، أو عليك أن تنتهك مغلفة الكائن (وبالتالي فقدان الوصول الموحد، وزيادة اقتران وكسر المستقبل في المستقبل) باستخدام الانعكاس (في هذه الحالة instance_variable_get).

هنا هو في كل مجدها:

#!/usr/bin/env ruby

class X
  def initialize(a=1)
    @a = a
  end

  def m(other) 
    @a == other.a
  end

  protected

  attr_reader :a
end

require 'test/unit'
class TestX < Test::Unit::TestCase
  def test_that_m_evaluates_to_true_when_passed_two_empty_xs
    x, y = X.new, X.new
    assert x.m(y)
  end
  def test_that_m_evaluates_to_true_when_passed_two_xs_with_equal_attributes
    assert X.new('foo').m(X.new('foo'))
  end
end

أو بدلا من ذلك:

class X
  def m(other) 
    @a == other.instance_variable_get(:@a)
  end
end

أي واحد من هذين اثنين أنت اخترت مسألة ذوق شخصي، أود أن أقول. ال Set يستخدم الفصل في المكتبة القياسية إصدار الانعكاس، على الرغم من هو - هي الاستخدامات instance_eval في حين أن:

class X
  def m(other) 
    @a == other.instance_eval { @a }
  end
end

(ليس لدي أي فكرة لماذا. ربما instance_variable_get ببساطة لم تكن موجودة عندما Set كتب. يبلغ عمر روبي 17 عاما في فبراير، وبعض الأشياء في Stdlib من الأيام الأولى.)

إذا كنت لا تستخدم instance_eval الخيار (as @ jleedev نشر)، واختر استخدام getter الطريقة، لا يزال بإمكانك الاحتفاظ بها protected

إذا كنت تريد protected الطريقة في Ruby، فقط قم بما يلي لإنشاء أحد Getter يمكن قراءته إلا من كائنات من نفس الفئة:

class X
    def new()
        @a = 1
    end
    def m( other ) 
        @a == other.a
    end

    protected
    def a 
      @a
    end
end

x = X.new()
y = X.new()
x.m( y ) # Returns true
x.a      # Throws error

لست متأكدا، ولكن هذا قد يساعد:

خارج الفصل، إنه أصعب قليلا:

# Doesn't work:
irb -> a.@foo
SyntaxError: compile error
(irb):9: syntax error, unexpected tIVAR
        from (irb):9

# But you can access it this way:
irb -> a.instance_variable_get(:@foo)
    => []

http://whynotwiki.com/ruby_/_variables_and_constants#variable_scope.2faciessivity.

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