Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Rubyメソッドルックアップを理解する

メソッドを呼び出すとどうなると思いますか?同じ名前の別のメソッドがある場合、Rubyはどのメソッドを呼び出すかをどのように決定しますか?メソッドがどこに格納または供給されているのか疑問に思ったことはありますか?

Rubyは、定義された「ウェイ」または「パターン」を使用して、呼び出す適切なメソッドと「メソッドエラーなし」を返す適切なタイミングを決定します。この「ウェイ」をRubyメソッドルックアップパス> 。このチュートリアルでは、Rubyのメソッドルックアップについて詳しく説明します。最後に、Rubyがオブジェクトの階層をどのように通過して、参照しているメソッドを判別するかを十分に理解できます。

私たちが何を学ぶかを完全に理解するには、Rubyの基本を理解している必要があります。モジュールやクラスのようなものについて言及しますが、これはそれらが何をするのかを深く掘り下げることにはなりません。このチュートリアルの目標を達成するために必要な深さのみをカバーします。Rubyがオブジェクトに渡すメッセージ(メソッド)をどのように決定するかを示します。

概要

first_person.valid?などのメソッドを呼び出す場合 、Rubyはいくつかのことを決定する必要があります:

  1. メソッド.valid?はどこにありますか? 定義されています。
  2. .valid?がある場所は複数ありますか? メソッドが定義されていますか?もしそうなら、これはこの文脈で使用するのに適切なものです。

これを理解するためにRubyが従うプロセス(またはパス)は、メソッドルックアップと呼ばれるものです。 。 Rubyは、メソッドを呼び出すことができるように、メソッドが作成された場所を見つける必要があります。次の場所を検索して、正しいメソッドを呼び出していることを確認する必要があります。

  1. シングルトンメソッド:Rubyは、オブジェクトが独自のメソッドを定義する方法を提供します。これらのメソッドはそのオブジェクトでのみ使用可能であり、オブジェクトのインスタンスからはアクセスできません。
  2. 混合モジュールのメソッド:モジュールは、 prependを使用してクラスに混合できます。 、 include 、または extend 。これが発生すると、クラスはモジュールで定義されたメソッドにアクセスでき、Rubyはモジュールに入り、呼び出されたメソッドを検索します。他のモジュールを最初のモジュールに混在させることができ、検索もこれらに進むことを知っておくことも重要です。
  3. インスタンスメソッド:これらはクラスで定義され、そのクラスのインスタンスからアクセスできるメソッドです。
  4. 親クラスのメソッドまたはモジュール:クラスが別のクラスの子である場合、Rubyは親クラスを検索します。検索は、親クラスのシングルトンメソッド、混合モジュール、およびその親クラスに行われます。
  5. Object、Kernel、およびBasicObject:これらはRubyが検索する最後の場所です。これは、Rubyのすべてのオブジェクトが祖先の一部としてこれらを持っているためです。
クラスとモジュール

多くの場合、メソッドはオブジェクトに対して呼び出されます。これらのオブジェクトは、Rubyの組み込みクラスまたは開発者によって作成されたクラスである可能性がある特定のクラスによって作成されます。

class Human
  attr_reader :name

  def initialize(name)
    @name = name
  end

  def hello
    put "Hello! #{name}"
  end
end

その後、 helloを呼び出すことができます Humanのインスタンスで上記で作成したメソッド クラス;たとえば、

john = Human.new("John")
john.hello # Output -> Hello John

hello メソッドはインスタンスメソッドです。これが、 Humanのインスタンスで呼び出すことができる理由です。 クラス。インスタンスでメソッドを呼び出さない場合があります。このような場合、クラス自体でメソッドを呼び出します。これを実現するには、クラスメソッドを作成する必要があります。上記のクラスのクラスメソッドを定義すると、次のようになります。

  def self.me
    puts "I am a class method"
  end

次に、 Human.meを実行してこれを呼び出すことができます。 。アプリケーションの複雑さが増すにつれて(ここで新しいスタートアップを構築していると想像してください)、2つ以上のクラスに同じことを行う複数のメソッドがある場合があります。これが起こった場合、それは私たちが物事を乾いた状態に保ち、自分自身を繰り返さないようにする必要があることを意味します。問題は、これらのクラス間で機能を共有する方法に関係しています。

以前にモジュールを使用したことがない場合は、これらの「共有」メソッド専用の新しいクラスを作成したくなるかもしれません。ただし、そうすると、特に多重継承を利用する必要がある場合に、Rubyがサポートしていない悪影響が生じる可能性があります。モジュールは、このケースを処理するための最良の手段です。モジュールはクラスに似ていますが、いくつかの違いがあります。まず、モジュールがどのように見えるかの例を次に示します。

module Movement
  def walk
    puts "I can walk!"
  end
end
  1. 定義はmoduleで始まります classの代わりにキーワード 。
  2. モジュールにインスタンスを含めることはできないため、 Movement.newを使用することはできません。 。
メソッド

メソッドは、特定のオブジェクトによって実行されるアクションと見なすことができます。 [2、3、4]のような配列がある場合 numberListという変数に割り当てられます 、 .push メソッドは、配列が置くために実行できるアクションです。 配列に受け取る値。このコードスニペットは一例です:

john.walk

「オブジェクトのメソッドを呼び出しています」のように言うのが一般的かもしれません。ここで、 john Humanのインスタンスであるオブジェクトを参照します 、および walk 方法です。ただし、推測されるメソッドはオブジェクトのクラス、スーパークラス、または混合から取得される傾向があるため、これは完全には当てはまりません。 モジュール。

john のようなオブジェクトであっても、オブジェクトにメソッドを定義できることを追加することが重要です。 、すべてがRubyのオブジェクトであるため、オブジェクトの作成に使用されるクラスですら。

def john.drip
  puts "My drip is eternal"
end

ドリップ メソッドには、 johnに割り当てられたオブジェクトからのみアクセスできます。 。 ドリップ johnが利用できるシングルトンメソッドです。 物体。このStackOverflowの回答からわかるように、シングルトンメソッドとクラスメソッドの間に違いはないことを知っておくことが重要です。上記の例のようにオブジェクトで定義されたメソッドを参照しているのでない限り、メソッドが特定のオブジェクトに属していると言うのは誤りです。この例では、 walk メソッドはMovementに属しています モジュール、 hello メソッドはHumanに属しています クラス。この理解により、これをさらに一歩進めることが容易になります。つまり、オブジェクトで呼び出されている正確なメソッドを判別するには、Rubyはオブジェクトのクラスまたはスーパークラスまたは混合されたモジュールをチェックする必要があります。 オブジェクトの階層内。

ミキシングモジュール

Rubyは単一継承のみをサポートします。クラスは1つのクラスからのみ継承できます。これにより、子クラスが別のクラスの動作(メソッド)を継承できるようになります。異なるクラス間で共有する必要のある動作がある場合はどうなりますか?たとえば、 walkを作成するには Humanのインスタンスで使用可能なメソッド クラスでは、ミックスインできます Movement Humanのモジュール クラス。したがって、 Humanの書き直し includeを使用するクラス 次のようになります:

require "movement" # Assuming we have the module in a file called movement.rb

class Human
  include Movement

  attr_reader :name

  def initialize(name)
    @name = name
  end

  def hello
    put "Hello! #{name}"
  end
end

これで、 walkを呼び出すことができます インスタンスのメソッド:

john = Human.new("John")
john.walk
含める

インクルードを利用する場合 上記のように、キーワードは、含まれているモジュールのメソッドがインスタンスメソッドとしてクラスに追加されます。これは、含まれているためです モジュールは祖先の間に追加されます Human Movementなどのクラス モジュールはHumanの親と見なすことができます クラス。上に示した例でわかるように、 walkと呼んでいます。 Humanのインスタンスのメソッド クラス。

拡張

含めるに加えて 、Rubyは拡張を提供します キーワード。これにより、モジュールのメソッドがクラスメソッドとしてクラスで使用できるようになります。これは、以前に学習したように、シングルトンメソッドとも呼ばれます。したがって、 Feedingというモジュールがある場合

のように見えます
module Feeding
  def food
    "I make my food :)"
  end
end

次に、この動作を Humanで共有できます。 クラスを要求し、 extend Feedingを追加します 。ただし、 food を呼び出す代わりに、それを使用するには クラスのインスタンスのメソッド。クラスのメソッドを呼び出すのと同じ方法で、クラス自体のメソッドを呼び出します。

Human.food
プレペンド

これはincludeに似ています ただし、この投稿で述べられているように、いくつかの違いがあります。

モジュールをクラスとチェーン内のそのスーパークラスの間に挿入する代わりに、クラス自体の前であっても、チェーンの最下部に挿入することを除いて、実際にはincludeのように機能します。

つまり、クラスインスタンスでメソッドを呼び出すとき、Rubyはクラスを調べる前にモジュールメソッドを調べます。

helloを定義するモジュールがある場合 次に、 Humanにミックスするメソッド prependを使用したクラス 、Rubyは、クラスにあるメソッドではなく、モジュールにあるメソッドを呼び出します。

Rubyの追加の方法を正しく理解するため 動作します。この記事をご覧になることをお勧めします。

メソッドルックアップパス

メソッドを呼び出そうとしたときにRubyインタープリターが最初に見るのは、シングルトンメソッドです。私はこのreplを作成しました。これを試して、可能な結果を​​確認できます。

次のようなモジュールとクラスがたくさんあるとします。

module One
  def another
    puts "From one module"
  end
end

module Two
  def another
    puts "From two module"
  end
end

module Three
  def another
    puts "From three module"
  end
end

class Creature
  def another
    puts "From creature class"
  end
end

これらをHumanに混ぜてみましょう クラス。

class Human < Creature
  prepend Three
  extend Two
  include One

  def another
    puts "Instance method"
  end

  def self.another
    puts "From Human class singleton"
  end
end

モジュールの混合の他に、インスタンスとクラスのメソッドがあります。 Human classは、 Creatureのサブクラスです。 クラス。

最初のルックアップ-シングルトンメソッド

Human.anotherを実行すると 、印刷されるのは From Human class singleton 、これはクラスメソッドにあるものです。クラスメソッドをコメントアウトして再度実行すると、 From two moduleが出力されます。 コンソールに。これは、 extendを使用して混合したモジュールに由来します 。これは、ルックアップがシングルトンメソッド間で開始されることを示しています。 extend Two を削除(またはコメントアウト)した場合 コマンドを再度実行すると、メソッド欠落エラーがスローされます。 。 Rubyがanotherを見つけられなかったため、このエラーが発生します シングルトンメソッドの中でメソッド。

先に進み、インスタンスを作成してクラスインスタンスを利用します:

n = Human.new

インスタンスのシングルトンメソッドも作成します:

def n.another
  puts "From n object"
end

ここで、 n.anotherを実行すると 、呼び出されるバージョンは、 nで定義されたシングルトンメソッドです。 物体。 Rubyがextendを使用して混合されたモジュールを呼び出さないように見える理由 この場合は、クラスのインスタンスでメソッドを呼び出しているためです。シングルトンメソッドは、 extendを使用して混合されたモジュールを含むメソッドよりも関連性が高いことを知っておくことが重要です。 。

2番目のルックアップ-preprendを使用して混合されたモジュール

nでシングルトンメソッドをコメントアウトすると オブジェクトを作成してコマンドを実行すると、呼び出されるメソッドのバージョンは、 prependを使用して混合したモジュールです。 。これは、 prependを使用するためです。 クラス自体の前にモジュールを挿入します。

サードルックアップ-クラス

モジュールをコメントアウトするとThree anotherのバージョン 呼び出されるメソッドは、クラスで定義されたインスタンスメソッドです。

4番目のルックアップ-includeを使用して混合されたモジュール

次にRubyがメソッドを検索する場所は、 includeを使用して混合されたモジュールです。 。したがって、インスタンスメソッドをコメントアウトすると、取得するバージョンはモジュール Oneにあるバージョンになります。 。

5番目のルックアップ-親クラス

クラスに親クラスがある場合、Rubyはクラスを検索します。検索には、親クラスに混合されたモジュールへのアクセスが含まれます。モジュールで定義されたメソッドがCreatureに混在している場合 クラスの場合、メソッドが呼び出されます。

メソッド検索の終わり

メソッドの検索がどこで終了するかは、その祖先を確認することでわかります。 .ancestorsを呼び出す クラスで。 Humanに対してこれを行う クラスは[Three、Human、One、Creature、Object、Kernel、BasicObject]を返します。 。メソッドの検索はBasicObjectで終了します クラス。Rubyのルートクラスです。あるクラスのインスタンスであるすべてのオブジェクトは、 BasicObjectから発生しました クラス。

メソッド検索が開発者定義の親クラスを通過すると、次のようになります。

  • オブジェクト クラス
  • カーネル モジュール
  • BasicObject クラス

method_missing 方法

Rubyをしばらく使用している場合は、おそらく NoMethodErrorに出くわしたことでしょう。 、これは、オブジェクトで不明なメソッドを試行したときに発生します。これは、Rubyがオブジェクトの祖先を調べて、呼び出されたメソッドを見つけられなかった後に発生します。受け取ったエラーメッセージは、 method_missingによって処理されます。 BasicObjectで定義されたメソッド クラス。メソッドを呼び出しているオブジェクトのメソッドをオーバーライドすることができます。これについては、これを確認することで確認できます。

結論

これで、Rubyがオブジェクトで呼び出されたメソッドを理解するためにたどるパスがわかりました。このことを理解すれば、オブジェクトで不明なメソッドを呼び出した結果として発生するエラーを簡単に修正できるはずです。


  1. RubyのGsubメソッドを使用する3つの素晴らしい方法

    Rubyのgsubについて話しましょう 方法と使い方。まず、このメソッドを使用するには文字列が必要です。 なぜですか? gsubの要点は 文字列の一部を置き換えることです。 実際 : 「gsub」の「sub」は「substitute」を表し、「g」は「global」を表します。 ここに文字列の例があります : str = white chocolate 「白」という単語を「暗い」という単語に置き換えたいとしましょう。 方法は次のとおりです : str.gsub(white, dark) これは言っています : 与えられた文字列str 、最初の単語のすべての出現箇所を置き換

  2. Ruby Freezeメソッド–オブジェクトの可変性を理解する

    オブジェクトが可変であるとはどういう意味ですか? 派手な言葉で混乱させないでください。「可変性 」は、オブジェクトの内部状態を変更できることを意味します。これは、凍結されたオブジェクトを除く、すべてのオブジェクトのデフォルトです。 、または特別なオブジェクトのリストの一部であるもの。 つまり、Rubyのすべてのオブジェクトが変更可能というわけではありません! 例 : 数字や記号、さらにはtrueには意味がありません またはfalse (オブジェクトでもあります)変更します。 数字の1は常に1になります。 ただし、他のオブジェクト、特に配列オブジェクトやハッシュオブジェクトなどのデー