Python に Lisp/Scheme ( MetaPythonのようなもの) に似たマクロ機能があるとしたら、それをどのように使用しますか?
あなたが Lisp/Scheme プログラマーである場合、(while ループなどの Python で明確な構文上の類似性があるものを除いて) どんな種類のマクロを使用しますか?
Python に Lisp/Scheme ( MetaPythonのようなもの) に似たマクロ機能があるとしたら、それをどのように使用しますか?
あなたが Lisp/Scheme プログラマーである場合、(while ループなどの Python で明確な構文上の類似性があるものを除いて) どんな種類のマクロを使用しますか?
マクロは Python の文化に反すると思います。Lisp のマクロは、大きな泥の塊のアプローチを可能にします。言語を再定義して、問題のドメインにより適したものにすることができます。逆に、Pythonic コードは、別の言語でより自然な方法で問題を解決するのではなく、Python の最も自然な組み込み機能を使用して問題を解決します。
マクロは本質的に非 Pythonic です。
lispマクロのいくつかの例:
メーリング リストへの投稿( archive.org mirror ) で、これをかなりよく説明しています。この投稿は Perl に関するものですが、Python にも同様に当てはまります。
Lisp では、マクロはアイデアを抽象化するもう 1 つの方法です。
これは、clojure で書かれた不完全なレイ トレーサーの例です。
(defmacro per-pixel
"Macro.
Excecutes body for every pixel. Binds i and j to the current pixel coord."
[i j & body]
`(dotimes [~i @width]
(dotimes [~j @height]
~@body)))
座標 (i,j) を持つすべてのピクセルに対して何かを行いたい場合、たとえば、i が偶数の場合に黒いピクセルを描画する場合は、次のように記述します。
(per-pixel i,j
(if (even? i)
(draw-black i,j)))
@body は内部のすべてを意味する可能性があるため、これはマクロなしでは実行できません (ピクセルごとの ij @body)
このようなことは、Pythonでも可能です。デコレータを使用する必要があります。Lisp マクロでできることすべてを行うことはできませんが、非常に強力です
このデコレーターのチュートリアルをチェックしてください: http://www.artima.com/weblogs/viewpost.jsp?thread=240808
これは私が遭遇した実世界の例で、マクロまたは実際のメタプログラミングのサポートでは些細なことですが、Python には両方がないため、CPython バイトコード操作で実行する必要があります。
http://www.aminus.net/dejavu/chrome/common/doc/2.0a/html/intro.html#cpython
Common Lisp では、通常のマクロと read-macros を組み合わせて構文を拡張して問題を解決する方法を次に示します (後者がなくても実行できましたが、前者ではできませんでした)。
http://clsql.b9.com/manual/csql-find.html
クロージャーとメタプログラミングを使用して Smalltalk で同じ問題を解決しました (Smalltalk は、実際にメッセージ パッシングを正しく行う数少ないシングル ディスパッチ OO 言語の 1 つです)。
http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg02096.html
ここでは、Common Lisp で Smalltalk アプローチを実装しようとしました。これは、Common Lisp でのメタプログラミングのサポートが不十分であることを示す良い例です。
http://carcaddar.blogspot.com/2009/04/closure-directional-metaprogramming-via.html
Python にマクロは必要ないと思います。マクロは次の 2 つの用途に役立つからです。
何かのために DSL またはより雄弁な構文を作成する (Lisp LOOP マクロは良い例です)。この場合、Python の哲学は、意図的に反対することを決定しました。不足している明示的な表記がある場合は、いつでも PEP を要求できます。
コンパイル時に物事を事前計算することにより、物事を高速化します。Python は速度重視ではないため、代わりにいつでも関数を使用できます。
マクロが間違っていると言っているのではなく、マクロが Python の哲学に合わないというだけです。ダックタイピングと演算子のオーバーロードがあるため、多くのコード重複なしでいつでもそれらなしで行うことができます。
余談ですが、マクロよりも Lisp が Python で再起動されるのを見たいと思っています。
ハイ、私自身の使用のために、引数、ループ、および条件付きコード生成を含むマクロ定義を可能にする Python モジュール (Espy) を作成しました。source.espy ファイルを作成し、適切な関数を起動すると、source.py が生成されます。
次のような構文を使用できます。
macro repeat(arg1):
for i in range(%arg1%):
socket
print "stop"
...
repeat(5):
print "Hi everybody"
print "See you soon"
次と同等です。
...
for i in range(5):
print "Hi everybody"
print "See you soon"
print "stop"
その他の構文:
macro doit(arg1):
for i in %arg1%:
socket suit(arg2):
socket
print %arg2%
socket check(arg3):
if %arg2%==%arg3%:
socket
...
#use
doit(range(10)):
suit(result):
result=i*i
check(result,25):
print "I knew that 5*5 == 25"
次と同等です。
for i in range(10):
result=i*i
print result
if result==25:
print "I knew that 5*5 == 25"
さらに、Espy には「macro for」と「macro if」の 2 つの機能があります。例:
macro for v in [6,10,12,20,23]:
macro if 7<%v%<22:
True:
print "At %v%, I'm awake."
False:
print "At %v%, I'm sleeping."
は Espy によって次のように翻訳されています。
print "At 6, I'm sleeping."
print "At 10, I'm awake."
print "At 12, I'm awake."
print "At 20, I'm awake."
print "At 23, I'm sleeping."
完全なドキュメントと無料のダウンロードは、http: //elp.chronocv.frから入手できます。
多くの場合、このモジュールを使用します。これにより、より構造化された短いコードが可能になります。それを使用して、新しいチェス エンジン プロジェクト (まだ進行中) の 1000 行の espy コードから 65000 行の明確で効率的な Python コードを生成しました。
Python が将来のリリースでマクロを含めることができれば、より印象的なものになるでしょう。
これを使用してラップyield
し、より強力なジェネレーター パイプラインを構築できるようにします。
「The Lambda Papers」を読んで、なぜマクロを利用するのかを一般的に理解してください。
「AIM-353 Lambda: The Ultimate Imperative」から始めて、「AIM-443 Lambda: The Ultimate GOTO」に続いてください。両方ともここにあります:
現在、言語の Python に機能を追加できる唯一の方法は、PEP (Python Enhancement Proposal) を使用することです。これは遅くなる可能性があり、ユースケースにのみ役立つ機能を言語に追加したい場合には役に立ちません。
たとえば、do-while ループを追加する PEP があります。これはおそらく Python に追加される予定ですが、PEP は 2003 年に作成されました。今日はループを書きたいと思いdo-while
ます。Python にマクロがあれば、それが可能です。
同様に、ラベル付きの中断と継続を追加する PEPがありましたが、これは拒否されました。ラベル付きの break ステートメントを使用してコードをより明確にすることができれば、それらをマクロで実装できます。
PEP はさておき、unless
マクロも欲しいです。書くのではなく:
if not is_superman():
dodge_bullet()
私は書くことができます:
unless is_superman():
dodge_bullet()
case
マクロが欲しいです (Lisp でよく呼び出されcond
ます)。書くのではなく:
if x == FOO:
do_stuff_with_foos()
elif x == BAR:
do_stuff_with_bars()
elif x == BAZ:
do_stuff_with_bazs()
私は書くことができます:
switch x:
case FOO:
do_stuff_with_foos()
case BAR:
do_stuff_with_bars()
case BAZ:
do_stuff_with_bazs()
これらは、マクロとして簡単に実装できます。より複雑で便利なマクロには、次のものがあります。
"hello there {user}"
(おそらくリーダー マクロとして実装するのが最適)現在、これらは他の言語の機能のみです。マクロを使えば、それらを Python に追加できます。実装例を含む PEP を書くことさえできました。(一部の PEP は既にこれを行っていますが、インタープリター自体の C ソースを変更することを余儀なくされています。)
代わりに
print >> sys.stderr, "abc"
書く
err "abc"
多くのデバッグ出力ステートメントを持ついくつかのスクリプトで。
できます
import sys
err = sys.stderr
その後
print >> err, "abc"
これは短くなりますが、それでも行に多くの文字が必要です。
C の世界から来た私は、リッチ メッセージを効率的にログに記録できることを本当に感謝しています。書く代わりに
if logger.level > logger.DEBUG:
logger.log('%s' % (an_expensive_call_that_gives_useful_info(),))
マクロを使用すると、代わりに次のようなことができます
DEBUG('%s' % (an_expensive_call_that_gives_useful_info(),))
おそらく、デバッグなどの実行時にソースコードが必要な場合(たとえば、式の値をその名前でprintfデバッグすると、2回書き込む必要がなくなります)。