Hyを使用してPythonコードを生成する

1. Hyとは



Hyは、Pythonに組み込まれているLisp方言です。







HyのLispのようなコードをPythonの抽象構文ツリー(AST)に変換したおかげで、Hyを使用して、Pythonの美しい世界全体が指先でLispの形になりました。







画像








2. Hy構文について、非常に簡単に



Hyは、それぞれの親に似た一種の言語です(もちろん、Lispにも似ています)。 Lisp構文に詳しくない人のために、この場合に要約できます。







  1. インデントは重要ではありません。 代わりに、括弧で囲まれた式のネストレベル。
  2. すべての関数呼び出しで、関数名は最初の場所に引数のリストを含む括弧に分類されます。 引数リストではコンマは使用されません。
  3. すべてのステートメントは、関数であるかのように書かれています。
  4. コロンは使用されません。
  5. 文字列と辞書のリテラルは以前と同様に機能します。 文字列は二重引用符で囲まれ、タプルは「、」関数の呼び出しのように見えます。


最初はこれは普通ではないように見えますが、実際にはこの構文が単純であるため(使用する特殊文字の数を減らすことにより)、すぐに慣れることができます。







3.用語の説明



使用される用語は個別に指定する必要があります。 英語の主な用語は、クォート、アンクォート、クワジクォート、スプライシング、マクロ展開です。 Practical Common Lispをロシア語に翻訳する際、彼らは「引用」、「引用」、「準引用」という言葉を使います-そして最後の言葉は「マクロを開示する」です。 この翻訳オプションが便利だとは思いません。







この資料では、引用用の「隠蔽」、引用解除用の「開示」、クォージ引用用の「準開示」、スプライシング用の「構造開示」、マクロ展開用の「マクロ展開」を翻訳として使用します。







次のコード例では、これらの操作の構文を見ることができます。









実行とは、フォームがリストの場合は関数呼び出し、それ以外の場合は文字の値へのアクセスを意味します。 リテラルは、実行されるとそのまま残ります。







4.メソッドの本質



操作可能なオブジェクトとしてhy



から構築を取得するには、非表示を使用できます。 マクロだけを拡張しても効果はありません-マクロ拡張コードはすぐに実行されるためです。 拡張機能を検査するだけでも、次のように非表示にすることはできません。







 (macroexpand '(my-macro param1 param2 (do (print "hello!"))))
      
      





したがって、構造の取得にすぐに集中することができます-たとえば、いくつかの入力データで関数を生成します。







忘れてはならないいくつかの困難があります。







  1. 隠されたコンストラクト自体は、 hy



    自体に対して構文的に正しい必要はありません。 私たちの場合、正確さが必要です。
  2. すべての有効なhy



    コンストラクトが正しいPythonコードに変換できるわけではありません。 特に、これは変数名に適用されますhy



    シンボル名のルールhy



    はるかに緩和されています。


変数に適切に生成されたコード構造がある場合(たとえば、生成関数を呼び出した結果)、次のようにPythonでコードを取得できます。







 (with [fd (open "some/python/file.py" "a")] (.write fd "\n") (.write fd (disassemble code True)))
      
      





5.名前の生成



Pythonでコードを生成するとき、たとえばマクロの作成とは異なり、どの名前に新しい文字が含まれているかが重要です。 pythonの場合、新しく生成された関数、クラス、変数の名前。 言い換えれば、Lispの標準的な方法( (gensym)



)は私たちに適合しません。 また、 hy



には、多くのlisp (intern)



の標準がありません。これは、任意の文字列(文法の制限に合わせて調整)を文字に変換するのに役立ちます。







幸いなことに、 hy



コードベース全体が利用可能であり、クイック検索で、 HySymbol



オブジェクトを作成することで(gensym)



機能することをHySymbol



ます。 同じことができます。







次の例は、前述の内容にもかかわらず、マクロです。









 (defmacro make-vars [data] (setv res '()) (for [element data] (setv varname (HySymbol (+ "var" (str element)))) (setv res (cons `(setv ~varname 0) res))) `(do ~@res))
      
      







変数名の生成に加えて、別の有用な詳細があります。 これは、これらのフラグメントのリストをコンパイルし、目的のフレームでリストを展開することにより、フラグメントから生成されたASTのコレクションです。







6.例とコメント



コードを生成するためにhy



を使用すると(単に作業するのではなく)、コードを実行に送信すると非表示になるという側面がいくつかあります。







これは主に、ASTのコンテキストと実行のコンテキストでは、同じ表現が異なることを意味するという事実に関連しています。









などなど。 これから得られる主な結論は、リストされている(および他の)構成要素は、非表示になっているときに、分解時に対応するpython



リテラルに正しく変換されるということです。







python



コードで静的にリストまたは辞書を作成するには、構造展開操作を使用する必要があります。







 (setv class-def [`(defclass ~class-name [~(HySymbol (. meta-base __name__))] [army_name ~army-name faction_base ~(HyString faction) alternate_factions [~@(map HyString alternate-fac-list)] army_id ~army-id army-factions [~@(map HyString army-factions)]] (defn --init-- [self &optional [parent None]] (apply .--init-- [(super ~class-name self)] {~@(interleave (map HyString class-grouping) (repeat 'True)) "parent" parent}) ~@(map (fn [key] `(.add-classes (. self ~(HySymbol key)) [~@(genexpr (HySymbol (. ut __name__)) [ut (get class-grouping key)])])) class-grouping)))]))))
      
      





上記の例では、リストは宣言されたクラスのalternate_factions



およびarmy-factions



フィールドに入力されます。 Pythonコードでは、これらのフィールドは両方ともアンダースコアを使用することに注意してください。 充填は文字列リストに基づいているため、 python



から文字列変数への変換の結果の構造的な開示HyString



ます。







上記のhy



コードスニペットから、Pythonで次のコードスニペットを生成できます。







 class DetachPatrol_adeptus_ministorum(DetachPatrol): army_name = u'Adeptus Ministorum (Patrol detachment)' faction_base = u'ADEPTUS MINISTORUM' alternate_factions = [] army_id = u'patrol_adeptus_ministorum' army_factions = [u'IMPERIUM', u'ADEPTA SORORITAS', u'<ORDER>', u'ADEPTUS MINISTORUM'] def __init__(self, parent=None): super(DetachPatrol_adeptus_ministorum, self).__init__(*[], **{u'heavy': True, u'troops': True, u'transports': True, u'hq': True, u'fast': True, u'elite': True, u'parent': parent, }) self.heavy.add_classes([Exorcist, Retributors, PenitentEngines]) self.troops.add_classes([BattleSisters]) self.transports.add_classes([ASRhino, Immolator]) self.hq.add_classes([Celestine, Canoness, Jacobus]) self.fast.add_classes([Dominions, Seraphims]) self.elite.add_classes([ArcoFlagellants, Assassins, Celestians, Dialogus, Hospitaller, Imagifier, Mistress, Priest, Repentia, PriestV2, Crusaders]) return None
      
      





また、親クラスのコンストラクターの呼び出しがどのように記述されているかに注意したいと思います。









7.使用した材料



この記事を書くときは、 Hyのドキュメントの資料とロシア語のPractical Common Lispの翻訳を使用しました。








All Articles