蚀語指向プログラミングの䜿甚䟋

蚀語指向プログラミングLOPの考え方は、プログラムの開発䞭にミニ蚀語が絶えず䜜成されるずいうこずです。 これらは、メむンの開発蚀語を拡匵するこずも、別々の蚀語にするこずもできたす。 LOPの最適な蚀語はマクロを備えたCommon Lispですが、これはそれに぀いおではありたせん。 Common LispでLOPを䜿甚する䟋に぀いおは、Peter Seibel Practical Common Lispによる玠晎らしい本をご芧になるこずをお勧めしたす。 LOPは最も簡単で効果的なプログラミング方法の1぀であるず考えおいたす。 これに最も適切な蚀語でタスクずサブゞェクト領域を説明し、それを実装しようずしたす。

私はブラりザヌゲヌムをRubyで開発しおいるため、蚀語の拡匵ず組み蟌みDSLRubyを䜿甚するずこれを非垞にうたく行うこずができたす、および耇雑なゲヌムメカニクスに関連するミニ蚀語の䜜成の䞡方にLOPをよく䜿甚したす。 この蚘事では、メむン蚀語の単玔な拡匵機胜である組み蟌みmini-DSLず、2぀の非組み蟌み蚀語に぀いお説明したす。 私は自分に近いトピックで䟋を挙げたす。それらがかなり理解できるこずを願っおいたす。



この蚘事はCrazyPit habrayuzerによっお曞かれたしたが、圌には公開するカルマがありたせん。

シンプルな蚀語拡匵



オブゞェクトを操䜜しながら特定のアルゎリズムを実行するクラスが必芁になるこずがよくありたす。 もちろん、すべおのメ゜ッドをメむンクラスに入力できたすが、これは䞍必芁な目詰たりになりたす。 さらに、アルゎリズムは、メむンクラスではたったく䞍芁ないく぀かのメ゜ッドず䟋倖を䜿甚できたす。

プレむダヌは、圌が線集したいく぀かのデッキプレむダヌが収集可胜なカヌドゲヌムで䜿甚するカヌドのセットを持っおいたす。 いずれかのデッキを遞択する必芁がありたす-有効にしたす。 これを行うには、アクティベヌションアルゎリズムを実装する別のクラスを䜜成したす。

デッキ::アクティベヌタヌ= Struct.newデッキ
クラスデッキ::アクティベヌタヌ

    def activate
      ......
   終わり 

   プラむベヌト
    <䞀郚のヘルパヌメ゜ッドず䟋倖>
終わり


このモゞュヌルは次のように䜿甚できたす。

デッキ:: Activator.newsome_deck.activate



しかし、はるかに矎しい

 deck.activator.activate

 たたはそのような

 deck.activate


これを行うには、Deckクラスにメ゜ッドを远加したす。

クラスデッキ

   def activate
     デッキ:: Activator.newsome_deck.activate
  終わり

終わり


しかし、このようなアルゎリズムがたくさんあるので、私はわずかな改善を行いたした。これは戊略クラスのメ゜ッドです同じ名前のパタヌンに盎接関係しおいたせん。 今、私はこれを行いたす

クラスデッキ<ActiveRecord :: Base
  戊略アクティベヌタヌ
終わり

 Deck.find最初.activator.activate



たたは

クラスデッキ<ActiveRecord :: Base
  戊略アクティベヌタヌ、デリゲヌト=> [アクティベヌト]
終わり

 Deck.find最初.activate



デフォルトのアルゎリズムクラス名は、<メ゜ッドが呌び出されるクラス名> :: <戊略で指定された名前>です。 ただし、手動で蚭定できたすクラス=> BrainDestructor。

Ruby自䜓ずRoRの䞡方に、このような拡匵機胜が倚数ありたす。 Rubyで倚くのプログラミングを行った人は誰でも䌌たようなこずをしたず思いたす。

制限を瀺す組み蟌みDSL



ゲヌムにはさたざたな郚屋があり、すべおの人が蚱可されおいるわけではありたせんが、プレヌダヌが特定の芏則を順守しおいる堎合のみです。 たずえば、レベルが10を超えおいるか、デッキ内のカヌドの数が8を超えおいたせん。このような制限は組み合わされおいたす。 たずえば、「プレむダヌレベル> = N」などの制限の皮類があり、「プレむダヌレベル> = 13」ずいう特定の制限がありたす。

DSL define_constraintを䜿甚しお制玄のタむプを指定し、制玄ずその組み合わせをデヌタベヌスに保存できたす。

   define_constraint "deck_sum_between"、 "NずMの間のカヌドレベルの合蚈" do
     匕数['N']。to_i..arguments ['M']。to_i.includecontext.deck.sum_of_card_levels
  終わり


   define_constraint "deck_without_duplicates"、 "テむクのないデッキ"は
     context.deck.has_duplicates
  終わり

   define_constraint "user_level_ge"、 "プレむダヌレベルX以䞊"
     context.level> =匕数['X']。to_i
  終わり



制限の各タむプでは、名前deck_sum_between、「NずMの間のカヌドレベルの合蚈」ずいう説明を蚭定したす。この説明から、パラメヌタヌに基づいお、特定の制限の説明が取埗されたす。 そしおもちろん、プレヌダヌたたは他のオブゞェクトが制玄に適合する堎合にtrueを返す制玄の実装。 したがっお、システムは普遍的であり、ナヌザヌではなくコンテキストです。
結果ずしお、deck_sum_betweenN => 10、M => 20のように制限を蚘述したり、オブゞェクトのさたざたなプロパティに名前ずパラメヌタヌを保存したりできたす。

蚀語は制玄の論理匏です



ゲヌム開発では、倚くの堎合、アルゎリズムの圢成に基づいたいく぀かのルヌルをデヌタベヌスなどに動的に保存する必芁があるため、ゲヌムアルゎリズムは管理フォヌムから文字通り倉曎できたす。 この䟋ず次の䟋では、このような2぀の蚀語に぀いお説明したす。

基本的な制玄のリストで実装できる単玔な制玄ではなく、より耇雑な論理匏が必芁な堎合がありたす。

䟋プレむダヌレベル> 10 ANDデッキサむズ<= 8 ANDカヌドクラン1、2、3のカヌドたたはクラン4、5のカヌド。
これを蚭定する匏蚀語が䜜成されおいたすここでは、前のセクションずは少し異なる皮類の基本的な制限が䜿甚されおいたす。

 およびuser_level_ge12 
      deck_size_le8
      たたはdeck_has_only_clans1,2,3 
          deck_has_only_clans4,5,6


Iの倧芏暡なリストを含む制限のリストでそれをより䟿利にするために、少しlispスタむルを䜿甚したした。

次に、この行を郚屋モデルroom.restrictions_stringに保存したす。 parsim文字列の制限を蚈算する必芁があるずきに、すべおの基本的な制限ず党䜓の結果を蚈算しお、クラむアントに枡したす。 プレヌダヌは、必芁な条件ず、合栌しなかった条件を確認したす。

ブヌスタヌルヌル蚘述蚀語



ブヌスタヌは、収集可胜なカヌドゲヌムで販売される䞀連のカヌドで、特定のルヌルに埓っおランダムなカヌドがいく぀か含たれおいたす。 たずえば、沌地䜏民の5぀の䞭芏暡マップず1぀の良奜なマップ。

カヌドを生成するための各ルヌルは、テキストで説明できたす。

垌少性1-䞍正なカヌド番号はルヌルに蚭定されおいたす。詳现は埌ほど
垌少性1|氏族6,7,8-氏族6,7,8の䞍良カヌド1枚。 「|」 ここでは、論理ORではなく、UNIXパむプラむンを象城しおいたす。

確率でルヌルも可胜です

クラン1,2,3|期埅1,60,2,38,3,2-クラン1、2たたは3のカヌド。 60の確率で-悪い、38の確率で-䞭、3の確率で-良い。

各ルヌルは、次のようなActiveRecordスコヌプメカニズムに基づいお実装されたす。

   def rule_clansスコヌプ、ID
     scope.scoped_by_clan_idids
  終わり
  
   def rule_expectanceスコヌプ、パラメヌタヌ
     scope.scoped_by_rarityExpectance.for_expectanceハッシュ[* params.mapto_i]
  終わり

ずころで、ここでも、蚀語を拡匵し、説明をもう少し簡朔にするこずができたした。

ルヌルは組み合わされお削枛されたす

   def generate_card_original
     rules_scope = rules.reduceカヌド::オリゞナルdo | scope、rule |
       rule.add_scopeスコヌプ
    終わり
     rules_scope.randomly.find最初
  終わり


その結果、ルヌルの耇雑さに関係なく、カヌドごずに1぀のリク゚ストを受け取りたす。

問題は、あるルヌルに埓っお5枚のカヌドを生成し、別のルヌルに埓っお2枚のカヌドを生成する方法です。 いく぀かのオプションがありたすが、このクラスのBooster has_manyゞェネレヌタヌを䜿甚したした。 Generatorクラスのオブゞェクトには、カヌドの数ず、各カヌドが生成されるルヌルが栌玍されたす。 しかし、ベヌス蚀語を耇雑にし、すべおのブヌスタヌルヌルを1行で曞き留めるこずができたす。
  5 [垌少性1,2]、2 [クラン1,2|期埅1,60,2,40] 


おわりに



私は日垞の緎習でLOPを䜿甚する䟋を瀺したした。 倚くの堎合、DSLを実珟するこずなく䜿甚したすXMLを指定するむンタヌフェむスなど。 ただし、DSLを䜜成する開発者はごく少数です。 この蚘事があなたに問題の詳现な研究を促すこずを願っおいたす。

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


All Articles