私は次の Richard Stallmanによるステートメント に遭遇しました:
'LISPシステムを起動すると、read-eval-printループに入ります。他のほとんどの言語には、readに相当するもの、evalに相当するもの、printに相当するものはありません。なんと欠けている欠陥だ! 」
今、私はLISPでほとんどプログラミングをしていませんが、Python、そして最近はErlangでかなりの量のコードを書いています。これらの言語はread-eval-printも提供しているという印象でしたループ、しかしストールマンは同意しない(少なくともPythonについて):
'Pythonのドキュメントを読み飛ばして、LISPと基本的に似ていると言われました。私の結論は、そうではないということです。LISPを起動すると、「読み取り」、「評価」、そして「印刷」、それらすべてはPythonでは欠けています。
LISPとPythonのread-eval-printループの間に根本的な技術的な違いは本当にありますか? LISP REPLで簡単にでき、Pythonでは難しいことの例を挙げられますか?
ストールマンの立場を支持するために、Pythonは、以下の領域の典型的なLISPシステムと同じことをしません:
LISPのread
関数はS式を読み取ります。S式は、データとして扱うか、コードとして評価できる任意のデータ構造を表します。 Python=の最も近いものは、1つの文字列を読み取ります。これは、何かを意味したい場合は、自分で解析する必要があります。
LISPのeval
関数は、任意のLISPコードを実行できます。 Python=only式を評価するeval
関数は、ステートメントを実行するためにexec
ステートメントを必要とします。 Pythonテキストとして表されたソースコード、そして "評価" a Python AST。
LISPのprint
関数は、read
が受け入れるのとまったく同じ形式でS式を書き出します。 print
in Pythonは、印刷しようとしているデータによって定義されたものを出力します。これは、常に可逆的であるとは限りません。
Pythondoesに正確にeval
およびprint
という名前の関数があるが、それらは何かを行うので、ストールマンのステートメントは少し不誠実です彼が期待するものとは異なる(そして劣っている)。
私の意見では、PythondoesはLISPに似たいくつかの側面を持ち、人々がそれを推奨したかもしれない理由を理解できますストールマンはPythonを調べますが、 Paul GrahamがLISPの相違点について論じている のように、LISPのすべての機能を含むプログラミング言語もbeLISP。
ストールマンのポイントは、明示的な「リーダー」を実装しないと、REPLプロセスから重要なステップが削除されるため、Lispに比べてPythonのREPLは不自由に見えるようになります。リーダーはテキスト入力ストリームをメモリに変換するコンポーネント—言語に組み込まれ、データのソースコードとの両方に使用されるXMLパーサーのようなものを考えてください。これはマクロの記述(理論的にはPythonでast
モジュールを使用して可能)だけでなく、デバッグやイントロスペクションにも役立ちます。
incf
特殊フォームの実装方法に興味があるとします。次のようにテストできます:
[4]> (macroexpand '(incf a))
(SETQ A (+ A 1)) ;
しかし、incf
は、シンボル値をインクリメントするだけではありません。ハッシュテーブルエントリをインクリメントするように求められた場合、正確にはどうなりますか?どれどれ:
[2]> (macroexpand '(incf (gethash htable key)))
(LET* ((#:G3069 HTABLE) (#:G3070 KEY) (#:G3071 (+ (GETHASH #:G3069 #:G3070) 1)))
(SYSTEM::PUTHASH #:G3069 #:G3070 #:G3071)) ;
ここでは、incf
がシステム固有のputhash
関数を呼び出すことを学びます。これは、このCommon LISPシステムの実装の詳細です。 「プリンター」が「リーダー」に知られている機能をどのように利用しているかに注意してください。たとえば、#:
構文で匿名シンボルを導入したり、展開された式のスコープ内で同じシンボルを参照したりします。この種の検査をPythonでエミュレートすると、はるかに冗長になり、アクセスしにくくなります。
REPLでの明らかな使用に加えて、経験豊富なLispersは、XMLまたはjsonに匹敵する、シンプルですぐに利用できるシリアル化ツールとしてコードでprint
およびread
を使用します。 PythonにはLISPのstr
と同等のprint
関数がありますが、read
に相当するものがないため、最も近いものはeval
。eval
はもちろんtwo異なる概念、解析、および評価を統合し、 問題につながりますthis と solutions this のような、Pythonフォーラムの繰り返しのトピックです。これはLISPの問題ではありませんリーダーとエバリュエーターがきれいに分離されているためです。
最後に、リーダー機能の高度な機能により、プログラマーは、マクロでさえ提供できない方法で言語を拡張できます。そのような難しいことを可能にする完璧な例は、Mark Kantrowitzによる infix
パッケージ であり、フル機能のインフィックス構文をリーダーマクロとして実装しています。
LISPベースのシステムでは、通常、プログラムはREPL(read eval print loop)から実行されている間に開発されます。そのため、一連のツールを統合します。補完、編集、コマンドラインインタプリタ、デバッガ、...デフォルトではそれが設定されています。エラーのある式を入力してください-別のREPLデバッグコマンドが有効になっているレベルです。実際に何かをする必要があります。この動作を取り除きます。
REPLの概念には2つの異なる意味があります。
lISP(または他のいくつかの同様の言語)のようなRead Eval Print Loopプログラムとデータを読み取り、結果データを評価して印刷します。 Pythonはこの方法では機能しません。LISPのREPLを使用すると、メタプログラミング方法で直接作業でき、(コード)を生成するコードを記述して、展開を確認できます、実際のコードを変換するなど。LISPは読み取り/評価/印刷をトップループとして持っています。Pythonは読み取りループ/評価/印刷文字列のようなものをトップループとして持っています。
コマンドラインインターフェース。インタラクティブなシェル。 IPython の例を参照してください。一般的なLISPの [〜#〜] slime [〜#〜] と比較してください。
デフォルトモードのデフォルトシェルPythonは、インタラクティブな使用にはそれほど強力ではありません。
Python 2.7.2 (default, Jun 20 2012, 16:23:33)
[GCC 4.2.1 Compatible Apple Clang 4.0 (tags/Apple/clang-418.0.60)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> a+2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
>>>
エラーメッセージが表示されたら、それで終わりです。
それをCLISP REPLと比較してください:
rjmba:~ joswig$ clisp
i i i i i i i ooooo o ooooooo ooooo ooooo
I I I I I I I 8 8 8 8 8 o 8 8
I \ `+' / I 8 8 8 8 8 8
\ `-+-' / 8 8 8 ooooo 8oooo
`-__|__-' 8 8 8 8 8
| 8 o 8 8 o 8 8
------+------ ooooo 8oooooo ooo8ooo ooooo 8
Welcome to GNU CLISP 2.49 (2010-07-07) <http://clisp.cons.org/>
Copyright (c) Bruno Haible, Michael Stoll 1992, 1993
Copyright (c) Bruno Haible, Marcus Daniels 1994-1997
Copyright (c) Bruno Haible, Pierpaolo Bernardi, Sam Steingold 1998
Copyright (c) Bruno Haible, Sam Steingold 1999-2000
Copyright (c) Sam Steingold, Bruno Haible 2001-2010
Type :h and hit Enter for context help.
[1]> (+ a 2)
*** - SYSTEM::READ-EVAL-PRINT: variable A has no value
The following restarts are available:
USE-VALUE :R1 Input a value to be used instead of A.
STORE-VALUE :R2 Input a new value for A.
ABORT :R3 Abort main loop
Break 1 [2]>
CLISPはLISPの条件システムを使用してデバッガREPLに侵入します。それはいくつかの再起動を提示します。エラーコンテキスト内で、新しいREPLは拡張コマンドを提供します。
:R1
再起動:
Break 1 [2]> :r1
Use instead of A> 2
4
[3]>
したがって、プログラムおよび実行の実行のインタラクティブな修復を取得します...
Pythonのインタラクティブモードは、Pythonの「ファイルからコードを読み取る」モードとは、言語のテキスト表現に固有のいくつかの小さな重要な点で異なります。 Pythonも同型ではなく、「read-eval-print loop」ではなく「インタラクティブモード」と呼べるようなものです。それはさておき、それは種類違いよりグレード。
さて、Pythonコードファイルで、何かが「種類の違い」に近づきます。空白行を簡単に挿入できます:
def foo(n):
m = n + 1
return m
同じコードをインタプリタに貼り付けようとすると、関数は「閉じられている」と見なされ、間違ったインデントに裸のreturnステートメントがあると文句を言われます。これは(一般的な)LISPでは発生しません。
さらに、Pythonでは(少なくとも私の知る限り)利用できないCommon LISP(CL)には、かなり便利な便利な変数がいくつかあります。 CLとPythonには、 "最後の式の値"(*
CL内、_
(Pythonの場合)、CLには**
(最後の前の式の値)および***
(その前の値)および+
、++
および+++
(式自体)。また、CLは式とステートメント(本質的にすべてが式です)を区別しません。これらはすべて、より豊かなREPLエクスペリエンスの構築に役立ちます。
最初に言ったように、それは種類の違いよりもグレードの違いです。しかし、ギャップがそれらの間のより広い中間であったならば、それはおそらく同様に種類の違いでしょう。