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

Rubyのバインディングとレキシカルスコープ

明けましておめでとうございます。RubyMagicへようこそ。この冬のエピソードでは、バインディングとスコープについて詳しく説明します。だからあなたのスキーを着て、森の奥深くまで私たちに従ってください。

前回は、ブロック、プロシージャ、ラムダを比較して、Rubyのクロージャを調べました。 3つのタイプの違いは別として、クロージャーを定義するものに触れました。 。

クロージャは、環境を備えたファーストクラスの関数です。環境は、クロージャーが作成されたときに存在していた変数へのマッピングです。クロージャーは、別のスコープで定義されている場合でも、これらの変数へのアクセスを保持します。

ファーストクラス関数に相当するRubyを調べましたが、環境をスキップするのが便利でした。 。このエピソードでは、Rubyが字句スコープをどのように処理するかを調べることにより、クロージャー、クラス、およびクラスインスタンスに対してその環境がどのように機能するかを見ていきます。 バインディングを介して 。

字句スコープ

プログラミングでは、スコープはバインディングを指します コードの特定の部分で利用できます。バインディング、または名前バインディング 、変数の名前をその値にバインドするように、名前をメモリ参照にバインドします。スコープは、selfを定義します つまり、呼び出すことができるメソッドと使用可能な変数です。

Rubyは、ほとんどの最新のプログラミング言語と同様に、静的スコープを使用します。これは、多くの場合、字句スコープと呼ばれます。 (動的スコープとは対照的に)。現在のスコープはコードの構造に基づいており、コードの特定の部分で使用可能な変数を決定します。これは、コードがメソッド、ブロック、クラス間をジャンプするとスコープが変わることを意味します。たとえば、これらはすべて異なるローカル変数を持つことができるためです。

def bar
  foo = 1
  foo
end
 
bar #  => 1

このメソッドでは、ローカル変数を作成します メソッド内でそれをコンソールに出力します。変数はスコープ内です メソッド内で作成されたとおりです。

foo = 1
 
def bar
  foo
end
 
bar # => NameError (undefined local variable or method `foo' for main:Object)

この例では、メソッドの外部に変数を作成します。メソッド内で変数を呼び出すと、変数がスコープ外であるため、エラーが発生します。 。ローカル変数は厳密にスコープされています。つまり、引数として渡されない限り、メソッドはそれ自体の外部の変数にアクセスできません。

@foo = 1
 
def bar
  @foo
end
 
bar #  => 1

ローカル変数はローカルで使用できますが、インスタンス変数 クラスインスタンスのすべてのメソッドで使用できます。

継承されたスコープとProcバインディング

前の例で見たように、スコープはコード内の場所に基づいています。メソッドの外部で定義されたローカル変数は、メソッドの内部ではスコープ内にありませんが、インスタンス変数に変換することで使用可能にすることができます。メソッドには独自のバインディングを持つ独自のスコープがあるため、メソッドはメソッドの外部で定義されたローカル変数にアクセスできません。

Proc(拡張によりブロックとラムダを含む)は異なります。 procがインスタンス化されるたびに、ブロックが作成されたコンテキストでローカル変数への参照を継承するバインディングが作成されます。

foo = 1
Proc.new { foo }.call # => 1

この例では、fooという名前の変数を設定します 1へ 。内部的には、2行目に作成されたProcオブジェクトが新しいバインディングを作成します。 procを呼び出すときに、変数の値を要求できます。

バインディングはprocの初期化時に作成されるため、変数が定義されるまでブロックが呼び出されない場合でも、変数を定義する前にprocを作成することはできません。

proc = Proc.new { foo }
foo = 1
proc.call # => NameError (undefined local variable or method `foo' for main:Object)

procを呼び出すと、NameErrorが生成されます 変数はprocのバインディングで定義されていないためです。したがって、procでアクセスされる変数は、procが作成されるか、引数として渡される前に定義する必要があります。

foo = 1
proc = Proc.new { foo }
foo = 2
proc.call # => 2

ただし、procのバインディングは変数をコピーする代わりに参照を保持するため、メインコンテキストで定義された後で変数を変更できます。

foo = 1
Proc.new { foo = 2 }.call
foo #=> 2

この例では、foo 変数は、プロシージャ内のオブジェクトの外部と同じオブジェクトを指します。 proc内で更新して、外部の変数も更新することができます。

バインディング

現在のスコープを追跡するために、Rubyはバインディングを使用します 、コード内の各位置で実行コンテキストをカプセル化します。 binding メソッドはBindingを返します 現在の位置でのバインディングを説明するオブジェクト。

foo = 1
binding.local_variables # => [:foo]

バインディングオブジェクトには、#local_variablesという名前のメソッドがあります これは、現在のスコープで使用可能なすべてのローカル変数の名前を返します。

foo = 1
binding.eval("foo") # => 1

#evalを使用して、バインディングでコードを評価できます。 方法。上記の例は、単にfooを呼び出すだけなので、あまり役に立ちません。 同じ結果になります。ただし、バインディングは渡すことができるオブジェクトであるため、さらに興味深いことに使用できます。例を見てみましょう。

実際の例

ガレージの安全性におけるバインディングについて学習したので、それらをゲレンデに持ち出し、雪の中で遊んでください。言語全体でのRubyのバインディングの内部使用とは別に、バインディングオブジェクトが明示的に使用される状況がいくつかあります。良い例はERB—Rubyのテンプレートシステムです。

require 'erb'
 
x = 1
 
def y
  2
end
 
template = ERB.new("x is <%= x %>, y() returns <%= y %>, self is `<%= self %>`")
template.result(binding) # => "x is 1, y() returns 2, self is `main`"

この例では、xという名前の変数を作成します 、yというメソッド 、および両方を参照するERBテンプレート。次に、現在のバインディングをERB#resultに渡します。 、テンプレート内のERBタグを評価し、変数が入力された文字列を返します。

内部的には、ERBはBinding#evalを使用します 渡されたバインディングのスコープ内の各ERBタグの内容を評価します。上記の例で機能する簡略化された実装は、次のようになります。

class DiyErb
  def initialize(template)
    @template = template
  end
 
  def result(binding)
    @template.gsub(/<%=(.+?)%>/) do
      binding.eval($1)
    end
  end
end
 
x = 1
 
def y
  2
end
 
template = DiyErb.new("x is <%= x %>, y() returns <%= y %>, self is `<%= self %>`")
template.result(binding) # => "x is 1, y() returns 2, self is `main`"

DiyErb クラスは初期化時にテンプレート文字列を受け取ります。その#result メソッドはすべてのERBタグを検索し、それらをその内容を評価した結果に置き換えます。そのために、Binding#evalを呼び出します。 渡されたバインディングで、ERBタグの内容を使用します。

#resultを呼び出すときに現在のバインディングを渡す メソッド、eval 呼び出しは、メソッドの外部、さらにはクラスの外部で定義された変数に、明示的に渡すことなくアクセスできます。

森の中であなたを失いましたか?

森の中へのスキー旅行を楽しんでいただけたでしょうか。スコープとクロージャーを詳しく調べた後、それらについて詳しく説明しました。森の中であなたを失っていないことを願っています。バインディングについて詳しく知りたい場合、または他のRubyトピックについて詳しく知りたい場合は、お知らせください。

私たちをフォローしてくれてありがとう、そして次の開発者のためにそれらを残す前にあなたのバインディングから雪を蹴ってください。これらの魔法の旅が好きなら、Ruby Magicに登録して、月に1回程度の新しい記事が公開されたときに電子メールを受信することをお勧めします。


  1. RuboCopを使用したRubyコードのリンティングと自動フォーマット

    リンティングは、プログラムおよびスタイルのエラーについてソースコードを自動チェックすることです。このチェックは、リンターと呼ばれる静的コード分析ツールによって実行されます。ただし、コードフォーマッタは、事前に構成された一連のルールに厳密に準拠するようにソースコードをフォーマットするためのツールです。リンターは通常違反を報告しますが、問題を修正するのは通常プログラマー次第ですが、コードフォーマッターはそのルールをソースコードに直接適用する傾向があるため、フォーマットの間違いを自動的に修正します。 プロジェクトでより一貫性のあるコードスタイルを作成するタスクでは、通常、個別のリンティングツールと

  2. LoggerとLogrageを使用してRubyにログインする

    Rubyでのログの操作 ロギングは、アプリケーションが通常対処する主要なタスクの1つです。ログは、たとえば、必要なときに使用されます アプリ内で何が起こっているかを確認します それらを監視する、または 特定のデータの指標を収集します。 新しいプログラミング言語を学ぶとき、情報を記録するための最初の明白な選択は、ネイティブメカニズムです。通常、それは簡単で、文書化されており、コミュニティ全体に広く行き渡っています。 ログデータは、使用している会社、ビジネス、アプリケーションの種類によって大きく異なります。したがって、あなたとあなたのチームが選択したロギングソリューションがその全体的な使