4

私は、S 式がさまざまな Lisp でどのように評価されるかをよりよく理解しようとしており、それらが興味深い形式の悪い式を処理することを確認したかったのです。Common Lisp と Scheme はまったく異なる言語だと思いますが、動作の違いを説明するセマンティクスに特定の違いがあるのでしょうか。たとえば、Lisp-1 と Lisp-2 には、衛生的なマクロ システムと非衛生的なマクロ システムのように、動作に目に見える違いがあります。

私は、 Scheme と Common Lisp で到達不能な不正なif式を含むプログラムを持っています。

;; foo.scm
(if #t 1 (if))

(display "12")

そして Common Lisp バージョン

;; foo.lisp
(if t 1 (if))

(display "12")

chickenguileどちらも構文エラーを生成します。

鶏:

% chicken foo.scm

Syntax error: (foo.scm:1) in `if' - pair expected

    (if)

    Expansion history:

    <syntax>      (##core#begin (if #t 1 (if)))
    <syntax>      (if #t 1 (if))
    <syntax>      (##core#if #t 1 (if))
    <syntax>      (if)  <--

ガイル:

% guile foo.scm
...
.../foo.scm:1:9: source expression failed to match any pattern in form (if)

sbclどちらも 12をclisp出力し、警告を発しません。

SBCL:

% sbcl --load foo.lisp
This is SBCL 1.3.11, an implementation of ANSI Common Lisp.
...
12
0]^D

CLISP

% clisp foo.lisp

"12"
4

2 に答える 2

8

Common Lisp の実装: インタプリタとコンパイラ

Common Lisp では、コード実行のタイプは、Lisp システムが実装するものと、それをどのように使用するかによって異なります。多くの場合、Common Lisp の実装には、コードを実行する複数の方法があります。インタープリターと 1 つまたは複数のコンパイラーです。単一の実行中の実装でさえそれを持っている可能性があり、ユーザーはそれらを切り替えることができます。

  • インタプリタ: Lisp データ構造から直接実行します。JVM のような仮想マシン コードのインタープリターではありません。実行中にインタープリターが完全なコード ツリー/グラフを検証するとは思わないでしょう。インタプリタは通常、実行する現在の最上位フォームのみを調べます。

  • コンパイラ: Lisp コードを C、一部のバイト コードまたはマシン コードにコンパイルします。コンパイラはコードを実行する前にコードを生成するため、すべてのコードの構文チェックを行います (例外の可能性については、下部のコメントを参照してください)。

  • トップレベルの評価: インタープリター、コンパイラー、または両方の組み合わせを使用できます。

GNU CLISP にはインタープリターとコンパイラーの両方があります

GNU CLISP での例:

通常、テキスト ファイルの LOAD はインタープリターを使用します。

[1]> (load "test.lisp")
;; Loading file test.lisp ...
;; Loaded file test.lisp
T

インタープリターは式全体の構文の正確性をチェックしないため、エラーを検出しません。構文エラーのある else 句は使用されないため、Interpreter はそれを確認しません。

CLISP にはコンパイラもあります。

[2]> (compile-file "test.lisp")
;; Compiling file /tmp/test.lisp ...
** - Continuable Error
in #:|1 1 (IF T 1 ...)-1| in line 1 : Form too short, too few arguments: (IF)
If you continue (by typing 'continue'): Ignore the error and proceed
The following restarts are also available:
ABORT          :R1      Abort main loop

ご覧のとおり、CLISP コンパイラは構文エラーを検出し、明確なエラー メッセージを表示します。

SBCL はコンパイラーを使用しますが、インタープリターも備えています

SBCL はデフォルトで、エラーを検出するコンパイラを使用します。トップレベルのフォームでは、一部のフォームではより単純な評価メカニズムを使用します。通訳に切り替えることもできます。

SBCL で単純な IF フォームを記述した場合、エバリュエーターは完全なコンパイルを使用せず、エラーをキャッチしません。

CL-USER> (if t 1 (if))
1

関数定義内に同じコードを記述すると、関数定義はデフォルトでコンパイルされるため、エラーが検出されます。

CL-USER> (defun foo () (if t 1 (if)))
; in: DEFUN FOO
;     (IF)
; 
; caught ERROR:
;   error while parsing arguments to special operator IF:
;     too few elements in
;       ()
;     to satisfy lambda list
;       (SB-C::TEST SB-C::THEN &OPTIONAL SB-C::ELSE):
;     between 2 and 3 expected, but got 0
; 
; compilation unit finished
;   caught 1 ERROR condition
WARNING: redefining COMMON-LISP-USER::FOO in DEFUN
FOO

完全な SBCL インタープリターに切り替えると、定義時にエラーが検出されません。

CL-USER> (setf *evaluator-mode* :interpret)
:INTERPRET
CL-USER> (defun foo () (if t 1 (if)))
WARNING: redefining COMMON-LISP-USER::FOO in DEFUN
FOO

最適化と構文チェック

一部の最適化コンパイラは、到達不能コードの構文をチェックしない場合があることに注意してください。

概要

ほとんどの Common Lisp 実装では、コンパイラを使用して完全な構文の警告/エラーを取得する必要があります。

于 2016-11-04T05:48:12.017 に答える
3

CLの部分はRainerさんが丁寧に扱っているようです。R5RS は、2 つの実装のように動作する必要はないことを指摘したいだけです。

R5RS は非常に詳細に記述されifておらず、正しく使用された場合にのみ何をすべきかを指定しているため、エラー処理に関する次の文章があります

エラー状況について話すとき、このレポートでは「エラーが通知される」というフレーズを使用して、実装がエラーを検出して報告する必要があることを示します。そのような文言がエラーの議論に現れない場合、実装はエラーを検出または報告する必要はありませんが、そうすることが推奨されます。実装が検出する必要のないエラー状況は、通常、単に「エラー」と呼ばれます。

したがって、R5RS でこれをまとめると、この文字列"banana"は の評価に対する Guile と Chickens の応答と同じくらい正確です(if #t 1 (if))

一方、R6RS では、実装によって例外的な状況が検出されると、例外が発生すると一般的に述べられています。これは、if式が 2 つ未満または 3 つを超えると例外が発生する必要があることを意味しますが、言語が解析および解釈している可能性があるため、コンパイル時に例外が発生する必要があるとは言いません。

于 2016-11-04T15:21:49.850 に答える