1.メタプログラミングパタヌン-25 kyu。 評䟡方法

プログラミングは今でも時々行っおいたすが、埐々にそのスタむルを倉えおおり、メタプログラミングにたすたす関連しおいたす。 同時に、普通のプログラミングが嫌だずは蚀えたせん。 プログラマヌず同じように、コヌドのモゞュヌル性、簡朔性、わかりやすさ、柔軟性をたすたす高める方法を暡玢しおいたす。メタプログラミングでは、未開発の可胜性がありたすLispからの無限の無限のむンタヌネットメタプログラミングの措氎にもかかわらず。 :)

Rubyでのメタプログラミングに関するブログを始めたいず思いたす。

Rubyの遞択は、Rubyプログラマヌの環境でのメタプログラミングの文化がすでに倧郚分を圢成しおおり、メタプログラミングの芁玠がRubyプログラマヌの日垞䜜業の構造になっおいるずいう事実によるものです。さらに、他の動的蚀語よりもよく知られおいたす。

私はPhysTechでRubyRailsMetaprogrammingに関する講矩を行っおいたす。 講矩の1぀からの資料はここで取るこずができたす 。 写真には䞻なものの簡単な芁玄がありたす。

このブログでは、䞀貫した詳现な方法でトピックを玹介しようずしたす。 タスクは単玔ではないため、事前に深呌吞したす。 あなたの励たしのフィヌドバックを期埅しおいたす。

私は簡単なものから始めたしょう-定矩付きです。

スクリプト蚀語でのメタプログラミングは、実行時に名前空間を倉曎する機胜を利甚するプログラム䜜成スタむルです。

名前空間ずは、クラス、メ゜ッド、および倉数グロヌバル、ロヌカル、むンスタンス倉数、およびクラス倉数を意味したす。 倉曎ずは、クラス、メ゜ッド、倉数を䜜成、倉曎、削陀するこずを意味したす。

ほずんどのスクリプト蚀語では、名前空間はランタむムモヌドでのみ構築されたす。 しかし、倚くはこれを芚えおいないので、定矩でこれを匷調したした。 定矩からランタむムの䜙分な蚀及を削陀するず、「with Benefit」ずいうフレヌズが残りたす。 それが本質です。

圹に立たない非プログラミングの䟋 eval "s = 'eval s'; eval s"

電卓



私は、幌少期にBK-0010の関数をプロットするプログラムをどのように曞いたかを芚えおいたす。 関数はハヌドコヌディングされおおり、プログラムの実行䞭にリストから1぀の関数を遞択し、範囲[x0、x1]を指定するこずだけが可胜で、Y軞に沿った範囲芋よ、プログラマヌの考えすべおずすべおを自動化できるがプログラムによっお自動的に遞択されたした。

私は自分のBASICプログラムを芋お、゚クスタシヌを経隓したした。 しかし、それから悲しい思いが思い぀きたした。 しかし、私が必芁ずする関数の公匏をプログラムに盎接組み蟌むこずができないのは残念なこずです。

Ndaa ... 8幎生、1992幎、キロボチェペツク垂。 それ以来倚くのこずが流れおいたすが、問題は同じです

なんで

以䞋は、Ruby蚀語のむンタラクティブな「蚈算機」のコヌドです。

行= readline
   eval行.inspectを眮きたす
終わり

たたはそれ以䞊
 whileprint ">"; trueおよびline = readline
   eval行.inspectを眮きたす
終わり

たたは右
' readline 'が 必芁
while line = Readline .readline " > " 
始める
eval 行.inspectを眮きたす
レスキュヌ => e
puts e.to_s + " \ n " + e.backtrace.join " \ n " 
終わり
終わり


実行䟋
 artem @ laptop〜/メタレクチャヌ$ ruby​​ console.rb 
 > 1 + 2
 3
 >「こんにちは」
 「こんにちは」
 > def fibn0..1=== n  1fibn-1+ fibn-2終了
なし
 >0 ... 10.map {| n |  fibn}
 [1、1、2、3、5、8、13、21、34、55]
 > 1/0
 評䟡1 `/ 'で0で陀算
 console.rb4
 評䟡1  
 >終了
アルテム@ラップトップ〜/メタ講矩$

スクリプト蚀語には、文字列を取埗し、珟圚のコンテキストでほが eval呌び出された堎所でプログラマヌによっお曞かれたように、その文字列を実行するevalメ゜ッドがありたす。

実際、 evalようなツヌルは、コンパむルされたプログラミング蚀語でも利甚できたす。

ずころで、私はevalメ゜ッドをメタプログラミングに垰するこずはせず、このレッスンでは非垞に有害なメ゜ッドずさえ呌びたす。 察話型ルビヌ| python | perl | ...-シェルは、おそらく䜿甚すべき数少ない䟋の1぀です。 evalメ゜ッドの危険性eval以䞋でevalたす。

attr_accessor



rubist初心者でもattr_accessorコンストラクトを䜿甚しおクラスむンスタンスの属性を決定したすが、それらがどんな皮類の獣であるかを垞に認識しおいるわけではありたせん。

次のステヌトメントからattr_accessor匏の意味をattr_accessorたすcode
クラスの 歌
attr_accessor タむトル 、 長さ
終わり

結果によりコヌドず同等
クラスの 歌
def タむトル
@title
終わり
def title = v
@title = v
終わり
def length
@length
終わり
def length = v
@length = v
終わり
終わり

定矩は次のずおりです

肉県で芋るず、 attr_accessorは、コヌドを簡朔でattr_accessorものにするずいうプログラマヌの内郚的な芁望を満​​たすため、䟿利です。 attr_accessorコンストラクトは、「クラスむンスタンスの次の属性のattr_accessorメ゜ッドが必芁」ず翻蚳できたす。

attr_accessorコンストラクトattr_accessor未知の獣でさえattr_accessorたせん読み取り-組み蟌みの蚀語コンストラクトはありたせんが、自分でプログラムできる通垞のメ゜ッドです。 evalメ゜ッドを䜿甚しおこれを行いたしょう。

def attr_accessor *メ゜ッド
methods.each do | 方法 |
評䟡 {
def {メ゜ッド}
@ {メ゜ッド}
終わり
def { method } =v
@ {メ゜ッド} = v
終わり
    
}
終わり
終わり

これで、属性名の配列を匕数ずしお受け取るattr_accessorは、属性に察応するsetメ゜ッドずgetメ゜ッドを定矩する各名前のコヌド行を実行したす。

Rubyにはクラスたたはメ゜ッド定矩の抂念がなかったため、 attr_accessorようなメ゜ッドをattr_accessorが登堎したした。 「 class Song 」ずいう行を曞いたので、通垞の蚈算を実行できる新しいコンテキストに移動したした。「 def xxx() ... end 」ずいう構文は、匏の1぀に過ぎず、蚈算結果は垞にnil Ruby v1.8では 、サヌドパヌティの効果は、この構築が実行されたコンテキストのクラスの「 xxx 」メ゜ッドの倖芳に珟れたす。

関数定矩は完了したしたか クラスコンテキストに移動したしたか どんなナンセンス -どこから来たC ++プログラマヌに尋ねたす。 はい、正確に。

「 class Song 」は、クラス定矩を前にフレヌム化せず、ネヌムスペヌスのスコヌプが倉曎される特別なコンテキストに切り替えたす。 ぀たり、このコンテキストで呌び出すこずができるいく぀かの新しいメ゜ッドが衚瀺され、特定の呜什の実行の倀ず効果が倉化するなどです。 など

テキスト「 def xxx() ... end 」は実際には匏であり、Ruby仮想マシンによっお実行されたす。 同時に、メ゜ッド定矩の内郚は実行されたせんが、コヌドはバむトに倉換され、メ゜ッド名の䞋に保存されたす。

Qクラスのコンテキストはどういう意味ですか


Aこれは、匏selfが䜕らかのクラスに等しいようなコンテキストです。

次のコヌドを実行したす。
「 {1 self .inspect } からhi1を眮く」
クラス abc
「 { self.inspect } からhi2を眮く」
こんにちは
{ self .inspect } からhi3を眮きたす
終わり
終わり

hi1ずhi2の行が印刷されたす。 远加するずhi3の行が衚瀺されたす
 Abc.new.hi

合蚈取埗
 artem @ laptop〜/メタレクチャヌ$ ruby​​ self_in_contexts.rb 
メむンからhi1
 hi2 from Abc
 <Abc0xb7c3d9dc>のhi3
アルテム@ラップトップ〜/メタ講矩$

あなたが曞くずきそれを理解する必芁がありたす
 my_methodarg1、arg2

次に、本質的に「 self. 」が暗黙的に前で眮換されたす。
 self.my_methodarg1、arg2


ただし、これら2぀の匏は、堎合によっおは同等ではありたせん。
たずえば、 my_methodがprivateメ゜ッドの堎合、匏self.my_methodはprivateメ゜ッドの呌び出しで゚ラヌをself.my_methodたす。 これらはRuby実装の機胜 privateメ゜ッドであり、ドットを介しお呌び出すこずのできないメ゜ッドがありたす。


さお、話を止めおください。 䞊蚘attr_accessorコヌドをattr_accessorするようにしたす。
クラス モゞュヌル
def attr_accessor *メ゜ッド
methods.each do | 方法 |
class_eval {
def {メ゜ッド}
@ {メ゜ッド}
終わり
def { method } =v
@ {メ゜ッド} = v
終わり
      
}
終わり
終わり
終わり

私たちは䜕をしたしたか Moduleクラスのコンテキストにメ゜ッド定矩を配眮し、 evalをclass_eval眮き換えclass_eval 。

なぜこれをしたのですか 理由がありたす
*どのオブゞェクトが利甚可胜になるかを理解せずにメ゜ッドを蚘述するのは良くありたせん。 クラス Classクラスのむンスタンスおよびモゞュヌル Moduleクラスのむンスタンスのコンテキストで䜿甚できるattr_accessorメ゜ッドを蚘述する必芁がありたす。 ClassクラスはModuleクラスを継承するため、このメ゜ッドをModuleむンスタンスのメ゜ッドずしお定矩するだけで十分です。この堎合、モゞュヌルずクラスの䞡方で䜿甚できたす。
* class_evalメ゜ッドにはevalずの違いがあり、特に埌者は、匏「 def ... end 」を実行するず、 attr_accessorメ゜ッド内にロヌカルに存圚し、 attr_accessorメ゜ッドの実行䞭にのみ䜿甚可胜なメ゜ッドの定矩を䜜成したすこれは文曞化されおいない機胜「 def inside def 」です。 class_evalメ゜ッドは、正しいコンテキストで指定されたコヌドを実行するため、「 def 」が目的の結果を生成し始めたす。 class_evalメ゜ッドclass_evalアクティブであり、その匕数が文字列ではなくブロックである堎合に正確にメタプログラミングで䜿甚されたす。

これでコヌドが機胜したした。 しかし、圌は間違っおいたす。 「 class Module 」ず「 class_eval 」のないものを含む、他の間違った゜リュヌションがありたす。 それらの1぀を次に瀺したす。

def attr_accessor *メ゜ッド
methods.each do | 方法 |
評䟡 {
クラス { self }
def {メ゜ッド}
@ {メ゜ッド}
終わり
def { method } =v
@ {メ゜ッド} = v
終わり
終わり
    
}
終わり
終わり


最埌のオプションは、このコンテキストで匏selfが等しいかどうかに応じお、クラスのコンテキストではなくそれを呌び出しお䜕か悪い結果を埗るこずができるずいう点で悪いです。 䟋
s = " クラス "
s.instance_eval { attr_accessor hahaha }
Array .hahaha = 3 予想倖に、Arrayにはhahaha属性がありたす
Array .hahahaを眮きたす


最も重芁なこず
説明されおいるevalを䜿甚したattr_assessor定矩は、セキュリティの欠劂のために悪いです-敵の悪意からも、プログラマヌの愚かさからも保護されおいたせん method倉数の倀がメ゜ッド名の有効な文字列でない堎合、たずえば、文字列「 llalala(); puts `cat /etc/passwd`; puts "、結果は予枬できたせん。 プログラムの実行䞭に゚ラヌ䟋倖が衚瀺されない堎合がありたす。 「ロケットがすでに飛んでいる」ずきだけ驚きが来るでしょうc。 しかし、終わりを芋぀けられないずきに遅く珟れる間違いほど悪いものはありたせん。

最埌に、 attr_accessorの定矩の正しいバヌゞョンを曞きたす。 圌は、間違ったものずは異なり、ナニヌクです

クラス モゞュヌル
def attr_accessor *メ゜ッド
methods.each do | 方法 |
method.is_a Symbol でない限り、 TypeError .new " メ゜ッド名はシンボルではありたせん " を発生させたす。
define_methodメ゜ッド do
instance_variable_get " @ { method } " 
終わり
define_method " { method } = "  do | v |
instance_variable_set " @ { method } " 、v
終わり
終わり
終わり
終わり


デフォルト倀のattr_accessor


倚くの堎合、デフォルト倀で属性を蚘述したす。 「 ||= 」ずいうむディオムを䜿甚しおこれを行いたす。これは、「初期化されおいない堎合は、右にあるものによっお巊にあるものを初期化する」ず倧たかに倉換したす。

 class Song def length @length ||= 0 end def title @title ||= "no title" end end Song.new.length #=> 0 Song.new.title #=> "no title" 

このような決定の埌、新しい曲のlength属性の倀は0になりたす。

パむクによるず、私は欲しい...このコヌドを私が望むように動䜜させおください!!!
クラスの歌
   attr_accessorlength ,: default => 0
   attr_accessortitle ,: default => "no title"
終わり

次の行からclass_evalを䜿甚しお、トレヌニング目的専甚の間違ったコヌドをclass_evalしたす。
クラス モゞュヌル
def attr_accessor *メ゜ッド
options = methods.last.is_a ハッシュ  methods.pop{}
methods.each do | 方法 |
class_eval {
def {メ゜ッド}
\そのように曞かないでください
@ {メ゜ッド}   { " || = { options [ default ] } " if options [ default ] }
終わり
def { method } =v
@ {メ゜ッド} = v
終わり
      
}
終わり
終わり
終わり
奇跡があるかもしれたせん!!!
クラスの歌
   attr_accessorlength ,: default => 42
終わり
 Song.new.length出力42を眮きたす!!!


間違ったコヌドも時々機胜したす。 しかし、これはもちろん、それを曞いたプログラマヌによっお解雇されない理由ではありたせん。

するずき
クラスの 歌
attr_accessor length 、 default => 42
attr_accessor title 、 default => " no title "
終わり
puts Song .new.length outputs 42 !!!
Song .new.title oooooops !!!


私たちは神秘的になりたす
 artem @ laptop〜/メタレクチャヌ$ ruby​​ bad_attr_accessor.rb 
 42
 評䟡5「タむトル」スタックレベルが深すぎるSystemStackError
	 from評䟡5「タむトル」
	 bad_attr_accessor.rbから27
アルテム@ラップトップ〜/メタ講矩$ 

なぜそんなに迷惑なのですか 実際には、基本的な問題があるずいうこずです。文字列にいく぀かのオブゞェクトを挿入するこずは、単に䞍可胜です。

デフォルト倀のattr_accessorの問題は、 attr_accessorように正しく解決されたす。

クラス モゞュヌル
def attr_accessor *メ゜ッド
options = methods.last.is_a ハッシュ  methods.pop{}
methods.each do | 方法 |
method.is_a Symbol でない限り、 TypeError .new " メ゜ッド名はシンボルではありたせん " を発生させたす。
define_methodメ゜ッド do
instance_variable_get " @ { method } " ||
instance_variable_set " @ { method } " 、オプション[ デフォルト ]
終わり
define_method " { method } = "  do | v |
instance_variable_set " @ { method } " 、v
終わり
終わり
終わり
終わり


そのため、怜蚎した䟋では、メタプログラミングはメ゜ッドを定矩するメ゜ッドを蚘述するように芋えたす。

初心者のメタプログラマヌがこれらの怜玢をグヌグルで怜玢するのは理にかなっおいたす
1. ruby​​ doc attr_accessor
2. ruby​​ doc Kernel eval
3. ruby​​ docモゞュヌルclass_eval
4. ruby​​ docオブゞェクトinstance_eval
5. ruby​​ doc Object is_a
最初のリンクは正しいです。

eval 、および䞍玔物、キャッシング、RPC、DSLタスクを新しい抜象化レベルに導くこずができる修食子メ゜ッド、遅延遅延コンピュヌティングの抂念を継続するパタヌンなどに぀いおメタプログラミングを行う方法に぀いおは、ブログの次号を参照しおください。

Source: https://habr.com/ru/post/J49951/


All Articles