1. Hyとは
Hyは、Pythonに組み込まれているLisp方言です。
HyのLispのようなコードをPythonの抽象構文ツリー(AST)に変換したおかげで、Hyを使用して、Pythonの美しい世界全体が指先でLispの形になりました。
2. Hy構文について、非常に簡単に
Hyは、それぞれの親に似た一種の言語です(もちろん、Lispにも似ています)。 Lisp構文に詳しくない人のために、この場合に要約できます。
- インデントは重要ではありません。 代わりに、括弧で囲まれた式のネストレベル。
- すべての関数呼び出しで、関数名は最初の場所に引数のリストを含む括弧に分類されます。 引数リストではコンマは使用されません。
- すべてのステートメントは、関数であるかのように書かれています。
- コロンは使用されません。
- 文字列と辞書のリテラルは以前と同様に機能します。 文字列は二重引用符で囲まれ、タプルは「、」関数の呼び出しのように見えます。
最初はこれは普通ではないように見えますが、実際にはこの構文が単純であるため(使用する特殊文字の数を減らすことにより)、すぐに慣れることができます。
3.用語の説明
使用される用語は個別に指定する必要があります。 英語の主な用語は、クォート、アンクォート、クワジクォート、スプライシング、マクロ展開です。 Practical Common Lispをロシア語に翻訳する際、彼らは「引用」、「引用」、「準引用」という言葉を使います-そして最後の言葉は「マクロを開示する」です。 この翻訳オプションが便利だとは思いません。
この資料では、引用用の「隠蔽」、引用解除用の「開示」、クォージ引用用の「準開示」、スプライシング用の「構造開示」、マクロ展開用の「マクロ展開」を翻訳として使用します。
次のコード例では、これらの操作の構文を見ることができます。
-
'
::非表示; Hyの後続の形式に適用されます。 実行する代わりに、データとして処理されます。 -
`
::準開示; より複雑な形式の隠蔽により、より複雑な構文構造を構築できます。 -
~
::開示; タプルのコンストラクターでPythonで忙しいため、使用される文字はLispの従来のコンマとは異なります。 準発見された形式で使用され、それに続く次の形式の結果を入れます。 -
~@
::構造的開示; 前の操作と同様に機能しますが、次の違いがあります。フォーム評価の結果はリストである必要があり、その要素は、隠された準非表示フォームに配置されます。
実行とは、フォームがリストの場合は関数呼び出し、それ以外の場合は文字の値へのアクセスを意味します。 リテラルは、実行されるとそのまま残ります。
4.メソッドの本質
操作可能なオブジェクトとしてhy
から構築を取得するには、非表示を使用できます。 マクロだけを拡張しても効果はありません-マクロ拡張コードはすぐに実行されるためです。 拡張機能を検査するだけでも、次のように非表示にすることはできません。
(macroexpand '(my-macro param1 param2 (do (print "hello!"))))
したがって、構造の取得にすぐに集中することができます-たとえば、いくつかの入力データで関数を生成します。
忘れてはならないいくつかの困難があります。
- 隠されたコンストラクト自体は、
hy
自体に対して構文的に正しい必要はありません。 私たちの場合、正確さが必要です。 - すべての有効な
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のリストで[ ]
なく、HyList
です。 -
{ }
はPython辞書を開きませんが、HyDict
、および内部モデルではhy
がリストとして表示されます。 -
""
単なる文字列変数ではなく、HyStringです。
などなど。 これから得られる主な結論は、リストされている(および他の)構成要素は、非表示になっているときに、分解時に対応する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
また、親クラスのコンストラクターの呼び出しがどのように記述されているかに注意したいと思います。
- クラス(
.
で始まる)の関数の場合、apply
は、指定された最初の位置引数(リストの最初の要素、2番目のパラメーター)を、メソッドが呼び出されるオブジェクトとして扱います。 - 構造展開を使用して、名前付き引数の辞書を作成できます。
- 各キー(
HyString
変換された文字列)を値にHyString
するには、interleave
が使用されます。interleave
は、2つのリストを反復し、要素をインターリーブします。 -
python
コードで非表示になっているTrue
文字は、それ自体に変換されます。 - 非表示の構成では、どこでも宣言されていない(無料の)文字を使用できます。これは、同じ名前の変数に変換されます。 注;
parent
シンボルがクラスメソッドのパラメーターとして非表示構造で宣言されていても、非表示コード構造を返す関数の実行中に、そのようなシンボルは存在しません。 - リストから同じタイプの一連の操作を生成し、非表示の構成
hy
構造拡張を実行することができます(元のリストからの変換によって取得されます)。
7.使用した材料
この記事を書くときは、 Hyのドキュメントの資料とロシア語のPractical Common Lispの翻訳を使用しました。