6

私は考えています:

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

同じクラスの2つのプライベート属性を比較するにはどうすればよいですか?

4

4 に答える 4

12

あなたの当面の問題に対するいくつかの良い答えがすでにありますが、コメントを必要とするあなたのコードの他の部分に気付きました. (しかし、それらのほとんどは些細なことです。)

ここに 4 つの簡単なものがありますが、それらはすべてコーディング スタイルに関連しています。

  1. インデント: インデント用に 4 つのスペースと 5 つのスペースを混在させています。通常、インデントのスタイルは1 つだけに固執する方が適切であり、Ruby では一般的に 2 つのスペースです。
  2. メソッドがパラメーターを取らない場合、メソッド定義で括弧を省略するのが通例です。
  3. 同様に、引数なしでメッセージを送信すると、括弧は省略されます。
  4. ブロック内を除き、開き括弧の後と閉じ括弧の前に空白はありません。

とにかく小物ばかりです。大きなものはこれです:

def new
  @a = 1
end

これはあなたが思っていることをしませんこれは、と呼ばれるクラス メソッドではなく、 !と呼ばれるインスタンスメソッドを定義します。X#newX.new

あなたがここで呼んでいるもの:

x = X.new

クラスから継承したというクラスメソッドです。したがって、新しいメソッドを呼び出すことはありません。つまり、 実行されないことを意味します。これは、常に未定義であることを意味します。つまり、常に評価されることを意味します。newClass@a = 1@anil@aself@aothermtrue

Ruby にはコンストラクターがないことを除いて、おそらくやりたいことはコンストラクターを提供することです。Ruby はファクトリ メソッドのみを使用します。

本当にオーバーライドしたいメソッドは、インスタンスメソッドinitializeです。「実際に というクラスメソッドを呼び出しているときに、呼び出されたインスタンスメソッドをオーバーライドする必要があるのはなぜですか?」initializenew

さて、Ruby でのオブジェクトの構築は次のように行われます。オブジェクトの構築は、割り当て初期化の 2 つのフェーズに分割されます。割り当てはallocate、クラスのインスタンス メソッドとして定義され、Class通常は決してオーバーライドされない というパブリック クラス メソッドによって行われます。オブジェクトにメモリ空間を割り当て、いくつかのポインターを設定するだけですが、この時点ではオブジェクトは実際には使用できません。

ここでイニシャライザの出番です。これは と呼ばれるインスタンス メソッドinitializeで、オブジェクトの内部状態を設定し、他のオブジェクトで使用できる一貫性のある完全に定義された状態にします。

したがって、新しいオブジェクトを完全に作成するには、次のことを行う必要があります。

x = X.allocate
x.initialize

[注: Objective-C プログラマーはこれを認識している可能性があります。]

ただし、呼び出すのを忘れがちで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 の柱の 1 つです。Ruby では可視性の制限があるため (たとえば、上記で説明したように)、その理由でインスタンス変数を非表示にする必要は厳密にはありません。ただし、別の理由があります。統一アクセスの原則です。

UAP は、機能の使用方法は、機能の実装方法とは無関係であるべきであると述べています。そのため、機能へのアクセスは常に同じ、つまり統一されている必要があります。この理由は、機能の作成者が、機能のユーザーを壊すことなく、機能が内部でどのように機能するかを自由に変更できるためです。つまり、基本的なモジュール性です。

これは、たとえば、サイズが変数に格納されているか、毎回動的に計算されているか、最初に遅延計算されてから変数に格納されているか、メモ化されているかなどに関係なく、コレクションのサイズの取得は常に同じであるべきであることを意味します。当たり前のように聞こえますが、たとえば Java はこれを間違えます。

obj.size # stored in a field

対。

obj.getSize() # computed

ルビーは簡単な方法を取ります。Ruby では、機能を使用する方法は1 つしかありません。メッセージを送信することです。一方向しかないので、アクセスは簡単に均一です。

つまり、簡単に言うと、別のインスタンスのインスタンス変数にアクセスすることはできません。メッセージ送信を介してのみそのインスタンスと対話できます。つまり、他のオブジェクトがprotectedそのインスタンス変数にアクセスするためのメソッド (この場合は少なくとも可視性) を提供するか、そのオブジェクトのカプセル化に違反する必要があります (したがって、Uniform Access が失われ、カップリングが増加し、将来の破損のリスクがあります)。 ) リフレクションを使用する (この場合は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。Ruby は 2 月に 17 歳になる予定です。stdlib の一部は非常に初期のものです。)

于 2009-12-02T12:31:40.367 に答える
10

いくつかの方法があります

ゲッター:

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

ruby には「友達」や「保護された」アクセスという概念はないと思いますし、「プライベート」でさえ簡単にハッキングされます。ゲッターを使用すると読み取り専用のプロパティが作成され、instance_eval はインスタンス変数の名前を知る必要があることを意味するため、意味合いは似ています。

于 2009-12-02T03:14:33.637 に答える
4

オプションを使用せずinstance_eval(@jleedev が投稿したように)、getterメソッドを使用することを選択した場合でも、それを保持できます。protected

Ruby でメソッドが必要な場合protectedは、次のようにして、同じクラスのオブジェクトからのみ読み取ることができるゲッターを作成します。

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
于 2009-12-02T03:15:17.567 に答える
0

確かではありませんが、これは役立つかもしれません:

クラスの外では、少し難しいです:

# 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.2Fアクセシビリティ

于 2009-12-02T03:20:22.957 に答える