Common Lisp

ウィキペディアから、無料の百科事典
ナビゲーションにジャンプ 検索にジャンプ

Common Lisp
パラダイムマルチパラダイム手続き型機能型オブジェクト指向メタリフレクティブジェネリック
家族舌足らずの発音
によって設計されたスコット・ファールマンリチャード・P・ガブリエルデビッド・A・ムーンケント・ピットマンガイ・スティールダン・ウェインレブ
デベロッパーANSIX3J13委員
初登場1984 (38年前)、1994 (28年前) ANSI Common Lisp (1984 (1994
規律の入力ダイナミック強い
範囲字句、オプションで動的
OSクロスプラットフォーム
ファイル名拡張子.lisp、.lsp、.l、.cl、.fasl
Webサイトcommon-lisp .net
主な実装
Allegro CLABCLCLISPClozure CLCMUCLECLGCLLispWorksScieneer CLSBCLSymbolics Common Lisp
方言
CLtL1、CLtL2、ANSI Common Lisp
に影響を受けた
LispLisp Machine LispMaclispSchemeInterlisp
影響を受ける
ClojureDylanEmacs LispEuLispISLISP* LispAutoLispJuliaMooseRSKILLSubL

Common LispCL)は、Lispプログラミング言語の方言であり、 ANSI標準文書ANSI INCITS 226-1994(S20018)[1](以前のX3.226-1994(R1999) )で公開されています。[2]ハイパーリンクされたHTMLバージョンであるCommonLisp HyperSpecは、ANSI CommonLisp標準から派生しています。[3]

Common Lisp言語は、 Maclispの後継として標準化および改良されたものとして開発されました1980年代初頭までに、いくつかのグループがすでにMacLispの後継としてさまざまな作業を行っていました。LispMachineLisp (別名ZetaLisp)、Spice Lisp NIL S -1Lispです。Common Lispは、これらのMacLisp方言の機能を統合、標準化、および拡張しようとしました。Common Lispは実装ではなく、言語仕様です。[4] Freeおよびオープンソースソフトウェアやプロプライエタリ製品など、CommonLisp標準のいくつかの実装が利用可能です。[5] Common Lispは汎用であり、マルチパラダイムプログラミング言語手続き型関数型、およびオブジェクト指向プログラミングパラダイムの組み合わせをサポートします。動的計画法言語として、効率的なランタイムプログラムへの反復コンパイルにより、進化的かつ段階的なソフトウェア開発を容易にします。この段階的な開発は、多くの場合、実行中のアプリケーションを中断することなくインタラクティブに実行されます。

また、オプションの型注釈とキャストもサポートしています。これらは、後のプロファイリングと最適化の段階で必要に応じて追加できるため、コンパイラーはより効率的なコードを生成できます。たとえば、ハードウェアと実装でサポートされている範囲でボックス化fixnumされていない整数を保持できるため、大きな整数や任意精度の型よりも効率的な演算が可能になります。同様に、コンパイラーは、 optimize宣言 を使用して、モジュールごとまたは関数ごとに、どのタイプの安全レベルが必要かを通知できます。

Common Lispには、マルチメソッドとメソッドの組み合わせをサポートするオブジェクトシステムであるCLOSが含まれています。多くの場合、メタオブジェクトプロトコル で実装されます。

Common Lispは、Lispマクロ(コード変換)やリーダーマクロ(文字の入力パーサー)などの標準機能を介して拡張できます。

Common Lispは、 MaclispおよびJohnMcCarthyの元のLispとの部分的な下位互換性を提供しますこれにより、古いLispソフトウェアをCommonLispに移植できます。[6]

歴史

Common Lispの作業は、ARPAマネージャーのBob Engelmoreが単一のコミュニティ標準Lisp方言を開発するイニシアチブをとった後、1981年に開始されました。[7]初期の言語設計の多くは、電子メールを介して行われました。[8] [9] 1982年、Guy L. Steele Jr.は、1982年のLISPと関数型プログラミングに関するACMシンポジウムで、CommonLispの最初の概要を説明しました。[10]

最初の言語のドキュメントは、1984年にCommon Lisp the Language(CLtL1として知られる)の初版として公開されました。1990年に発行された第2版(CLtL2として知られる)は、ANSI Common Lisp標準化プロセス中に行われた言語への多くの変更を取り入れました:拡張LOOP構文、Common Lisp Object System、エラー処理のための条件システム、きれいなプリンターとはるかに。しかし、CLtL2は最終的なANSI Common Lisp標準を記述していないため、ANSI CommonLispのドキュメントではありません。その後、最終的なANSI Common Lisp標準が1994年に公開されました。それ以来、標準の更新は公開されていません。Common Lispのさまざまな拡張機能と改善点(例としては、Unicode、同時実行性、CLOSベースのIO)が、実装とライブラリーによって提供されています。

構文

CommonLispはLispの方言です。S式を使用して、コードとデータ構造の両方を示します。関数呼び出し、マクロフォーム、および特殊フォームは、次の例のように、最初に演算子の名前が付いたリストとして記述されます。

 +  2  2            ; 2と2を加算すると、4になります。関数の名前は「+」です。Lispにはそのような演算子はありません。
 defvar  * x *       ; 変数* x *が存在することを確認し
                   ます; 値を与えずに。アスタリスクは
                   ;の一部です。名前。慣例により、特別な(グローバル)変数を示します。
                   ; シンボル* x *には、次のプロパティも付与され
                   ます。その後のバインディングは、字句ではなく動的です。
 setf  * x *  42.1    ; 変数* x *を浮動小数点値42.1に設定します
 ;; 数値を二乗する関数を定義します:
 defun  square  x 
   *  x  x ))
 ;; 関数を実行します:
 square  3         ; 9を返します
 ;; 'let'構文は、ローカル変数のスコープを作成します。ここ
 ;; 変数 'a'は6にバインドされ、変数 'b'はバインドされます
 ;; 4.「let」の中には「body」があり、最後に計算された値が返されます。
 ;; ここで、aとbを追加した結果は、「let」式から返されます。
 ;; 変数aとbは、記号が
 ;;でない限り、字句スコープを持ちます。特別な変数としてマークされています(たとえば、以前のDEFVARによって)。
 let  ((a  6 
       b  4 ))
   +  a  b ))        ; 10を返します

データ型

CommonLispには多くのデータ型があります。

スカラー型

数値タイプには、整数比率浮動小数点数、および複素数が含まれます。[11] Common Lispは、bignumを使用して、任意のサイズと精度の数値を表します。比率タイプは分数を正確に表します。これは、多くの言語では利用できない機能です。Common Lispは、必要に応じてこれらのタイプ間で数値を自動的に強制します。

CommonLispの文字タイプはASCII文字に限定されません最新の実装のほとんどは、Unicode文字を許可しています。[12]

シンボルタイプはLisp言語に共通ですが、それ以外ではほとんど知られていません。シンボルは、名前、値、関数、プロパティリスト、パッケージなど、いくつかの部分からなる一意の名前付きデータオブジェクトです。これらの中で、バリューセルファンクションセルが最も重要です。Lispのシンボルは、他の言語の識別子と同じように使用されることがよくあります。変数の値を保持するため。ただし、他にも多くの用途があります。通常、シンボルが評価されると、その値が返されます。一部のシンボルはそれ自体に評価されます。たとえば、キーワードパッケージ内のすべてのシンボルは自己評価されます。Common Lispのブール値は、自己評価記号TおよびNILで表されます。Common Lispには、「パッケージ」と呼ばれるシンボルの名前空間があります。

スカラー数値をさまざまな方法で丸めるために、いくつかの関数を使用できます。この関数roundは、引数を最も近い整数に丸め、途中の場合は偶数の整数に丸めます。関数truncate、、、floorおよびはceiling、それぞれゼロ、ダウン、またはアップに向かって丸めます。これらの関数はすべて、破棄された小数部分を2次値として返します。たとえば、(floor -2.5)-3、0.5が得られます。(ceiling -2.5)-2、-0.5を生成します。(round 2.5)収量2、0.5; そして(round 3.5)、4、-0.5をもたらします。

データ構造

Common Lispのシーケンスタイプには、リスト、ベクトル、ビットベクトル、および文字列が含まれます。任意のシーケンスタイプで機能する操作は多数あります。

他のほとんどすべてのLisp方言と同様に、Common Lispのリストは、 consセルまたはペアと呼ばれることもあるconsesで構成されています。短所は、 carcdrと呼ばれる2つのスロットを持つデータ構造です。リストは、リンクされた一連のコンスまたは空のリストです。各短所の車は、リストのメンバー(おそらく別のリスト)を参照します。各短所のcdrは次の短所を参照します。ただし、リストの最後の短所は例外で、そのcdrは値を参照します。Consesは、ツリーやその他の複雑なデータ構造を実装するためにも簡単に使用できます。ただし、通常は代わりに構造体またはクラスインスタンスを使用することをお勧めします。consesを使用して循環データ構造を作成することもできます。 nil

Common Lispは多次元配列をサポートしており、必要に応じて調整可能な配列のサイズを動的に変更できます多次元配列は、行列数学に使用できます。ベクトルは1次元配列です。配列は、任意の型をメンバーとして運ぶことができます(同じ配列内の混合型であっても)、またはビットのベクトルのように、特定の型のメンバーを含むように特殊化することができます。通常、サポートされているタイプはごくわずかです。使用される配列が型に特化している場合、多くの実装で配列関数を最適化できます。2つの型に特化した配列型が標準です。文字列は文字のベクトルであり、ビットベクトルビットのベクトルです。

ハッシュテーブルは、データオブジェクト間の関連付けを格納します。任意のオブジェクトをキーまたは値として使用できます。ハッシュテーブルは、必要に応じて自動的にサイズ変更されます。

パッケージはシンボルのコレクションであり、主にプログラムの一部を名前空間に分離するために使用されます。パッケージはいくつかのシンボルをエクスポートし、それらをパブリックインターフェイスの一部としてマークする場合があります。パッケージは他のパッケージを使用できます。

構造体は、 C構造体やPascalレコードと同様に使用され、任意の数とタイプのフィールド(スロットと呼ばれる)を持つ任意の複雑なデータ構造を表します構造体は単一継承を可能にします。

クラスは構造に似ていますが、より動的な機能と多重継承を提供します。CLOSを参照)。クラスはCommonLispに遅れて追加されており、構造との概念的な重複がいくつかあります。クラスで作成されたオブジェクトは、インスタンスと呼ばれます。特別な場合はジェネリック関数です。ジェネリック関数は、関数とインスタンスの両方です。

関数

CommonLispはファーストクラスの関数をサポートしています。たとえば、他の関数を引数として取る関数や、関数を返す関数を作成することもできます。これにより、非常に一般的な操作を説明できます。

Common Lispライブラリは、このような高階関数に大きく依存しています。たとえば、このsort関数は、関係演算子を引数として取り、key関数をオプションのキーワード引数として取ります。これは、あらゆるタイプのデータをソートするだけでなく、キーに従ってデータ構造をソートするためにも使用できます。

 ;; 関係演算子として>および<関数を使用してリストをソートします。
 ソート リスト 5  2  6  3  1  4  # ' >    ; 戻り値(6 5 4 3 2 1)
 sort  list  5  2  6  3  1  4  # ' <    ; 戻り値(1 2 3 4 5 6)
 ;; 各サブリストの最初の要素に従ってリストを並べ替えます。
 sort  list  ' 9  A  ' 3  B  ' 4  C )) #' <  :key  # ' first    ; 戻り値((3 B)(4 C)(9 A))

関数の評価モデルは非常に単純です。評価者がフォームに遭遇する(f a1 a2...)と、fという名前の記号は次のいずれかであると見なされます。

  1. 特別な演算子(固定リストに対して簡単にチェックできます)
  2. マクロ演算子(事前に定義されている必要があります)
  3. 関数の名前(デフォルト)。シンボル、またはシンボルで始まるサブフォームのいずれかlambdaです。

fが関数の名前である場合、引数a1、a2、...、anは左から右の順序で評価され、関数が検出され、パラメーターとして指定された値を使用して呼び出されます。

関数の定義

マクロdefunは関数を定義します。関数定義は、関数の名前、引数の名前、および関数本体を示します。

 defun  square  x 
   *  x  x ))

関数定義には、宣言と呼ばれるコンパイラ指令が含まれる場合があります。これは、最適化設定または引数のデータ型に関するヒントをコンパイラに提供します。また、Lispシステムがインタラクティブなドキュメントを提供するために使用できるドキュメント文字列(docstrings)が 含まれる場合もあります。

 defun  square  x ) "単精度浮動小数点数x
   2乗を計算します。" 
   declare  single-float  x  optimize  speed  3  debug  0  safety  1 )))
   single-float * x x )))    

匿名関数(関数リテラル)はlambda、式を使用して定義されます。たとえば(lambda (x) (* x x))、引数を2乗する関数の場合です。Lispプログラミングスタイルでは、無名関数を引数として提供すると便利な高階関数が頻繁に使用されます。

fletローカル関数はとで定義できますlabels

 flet  ((square  x 
          *  x  x )))
   square  3 ))

関数の定義と操作に関連する他のいくつかの演算子があります。たとえば、関数はcompile演算子を使用してコンパイルできます。(一部のLispシステムは、コンパイルするように指示されない限り、デフォルトでインタプリタを使用して関数を実行します。他のシステムはすべての関数をコンパイルします)。

ジェネリック関数とメソッドの定義

マクロdefgenericジェネリック関数を定義します。ジェネリック関数はメソッドのコレクションです。マクロdefmethodはメソッドを定義します。

メソッドは、CLOS標準クラスシステムクラス構造体クラス、または個々のオブジェクトにパラメータを特化できます。多くのタイプには、対応するシステムクラスがあります。

ジェネリック関数が呼び出されると、多重ディスパッチが使用する効果的な方法を決定します。

 defgeneric  add  a  b ))
 defmethod  add  ((a  number  b  number ))
   +  a  b ))
 defmethod  add  ((a vector   b number  
   map'vector   lambda n + n b ))a ))     
 defmethod  add  ((a  vector  b  vector ))
   map'vector  # ' +  a b   
defmethod  add  ((a  string  b  string ))
  concatenate'string  a b   
 2 3を追加 ; 5を返します#(1 2 3 4 7を追加; 戻り値#(8 9 10 11)add #(1 2 3 4 #(4 3 2 1 )); #(5 5 5 5)を返します(" COMMON" "LISP"を追加; 「COMMONLISP」を返します                    
                
          
         

ジェネリック関数もファーストクラスのデータ型です。ジェネリック関数とメソッドには、上記よりも多くの機能があります。

関数名前空間

関数名の名前空間は、データ変数の名前空間とは別のものです。これは、CommonLispとSchemeの主な違いです。Common Lispの場合、関数名前空間で名前を定義する演算子には、、、、、および含まdefunれますfletlabelsdefmethoddefgeneric

関数を引数として別の関数に名前で渡すには、function特殊な演算子(通常は。と略記)を使用する必要があります#'上記の最初の例は、関数名前空間sortのシンボルによって名前が付けられた関数をコードで参照しています逆に、このように渡された関数を呼び出すには、引数に演算子を使用します。 >#'>funcall

Schemeの評価モデルはより単純です。名前空間は1つだけであり、フォーム内のすべての位置が(任意の順序で)評価されます。引数だけではありません。したがって、一方の方言で書かれたコードは、もう一方の方言の経験が豊富なプログラマーにとっては混乱を招くことがあります。たとえば、多くのCommon Lispプログラマーは、関数名をローカルでシャドウイングするため、Schemeで問題を引き起こす可能性のある リスト文字列などの記述変数名を使用することを好みます。

関数用の個別の名前空間が利点であるかどうかは、Lispコミュニティでの論争の原因です。これは通常、Lisp-1対Lisp-2の討論と呼ばれます。Lisp-1はSchemeのモデルを指し、Lisp-2はCommonLispのモデルを指します。これらの名前は、リチャードP.ガブリエルケントピットマンによる1988年の論文で造られました。この論文では、2つのアプローチを広範囲に比較しています。[13]

複数の戻り値

Common Lispは、複数の値概念をサポートします[14]。ここで、式は常に単一のプライマリ値を持ちますが、関心のある呼び出し元によって受信および検査される可能性のあるセカンダリ値をいくつでも持つ可能性があります。二次値は完全にオプションであり、専用のサイドチャネルを介して渡されるため、この概念はリスト値を返すこととは異なります。これは、発信者が二次値を必要としない場合、そこにあることに完全に気付かない可能性があることを意味し、情報を伝達するためのメカニズムを使用すると便利です。例えば、

  • TRUNCATE関数[15]は、指定された数値をゼロに向かって整数に丸めます。ただし、余りを2次値として返すため、切り捨てられた値を簡単に判別できます。また、オプションの除数パラメーターをサポートします。これを使用して、除法の原理を簡単に実行できます。
let  ((x  1266778 
      y  458 ))
  複数値バインド  の剰余
      切り捨て x  y 
    format  nil  "〜Aを〜Aで割ったものは〜Aの剰余〜A"  x  yの  の剰余)) )。

;;;; =>「1266778を458で割ると2765の余り408になります」
  • GETHASH[16]は、連想マップのキーの値、またはそれ以外の場合はデフォルト値、および値が見つかったかどうかを示す2次ブール値を返します。したがって、値が見つかったかデフォルトとして提供されたかを気にしないコードは、そのまま使用できますが、そのような区別が重要な場合は、2次ブール値を検査して適切に反応する可能性があります。どちらのユースケースも同じ呼び出しでサポートされており、どちらも他方によって不必要に負担や制約を受けることはありません。この機能を言語レベルで使用すると、他の言語で行われるように、キーの存在を確認したり、キーをnullと比較したりする必要がなくなります。
defun  get-answer  library 
  gethash'answer  library 42   

defun  the-answer-1  library 
  format  nil  "The answer is〜A"  get-answer  library )))
;;;; ANSWERがLIBRARYに存在しない場合、「答えは42です」を返します

defun  the-answer-2  library 
  multiple-value-bind  answer  sure-p 
      get-answer  library 
    if  not  sure-p 
        "I do n't know" 
     format  nil  "The answer is 〜A " 回答))))
;;;; ANSWERがLIBRARYに存在しない場合は、「わからない」を返します

複数の値は、いくつかの標準形式でサポートされています。その最も一般的な形式は、MULTIPLE-VALUE-BIND2次値にアクセスしVALUES、複数の値を返す ための特別な形式です。

defun  magic-eight-ball  ()
  "確率を2次値として、見通しの予測を返します" 
   "Outlook good"  ランダム 1.0 )))

;;;; =>「見通しは良い」
;;;; => 0.3187

その他の種類

CommonLispの他のデータ型は次のとおりです。

  • パス名は、ファイルシステム内のファイルとディレクトリを表しますCommon Lispパス名機能は、ほとんどのオペレーティングシステムのファイル命名規則よりも一般的であり、Lispプログラムがさまざまなシステム間でファイルに広く移植できるようにします。
  • 入力ストリームと出力ストリームは、ターミナルファイルや開いているファイルなどのバイナリデータまたはテキストデータのソースとシンクを表します。
  • Common Lispには、疑似乱数ジェネレーター(PRNG)が組み込まれています。ランダム状態オブジェクトは、疑似乱数の再利用可能なソースを表し、ユーザーがPRNGをシードしたり、シーケンスを再生したりできるようにします。
  • 条件は、プログラムが応答する可能性のあるエラー、例外、およびその他の「興味深い」イベントを表すために使用されるタイプです。
  • クラスファーストクラスのオブジェクトであり、それ自体がメタオブジェクトクラス略してメタクラス)と呼ばれるクラスのインスタンスです。
  • 読み取りテーブルは、 CommonLispのリーダーがソースコードのテキストを解析する方法を制御するオブジェクトの一種です。コードを読み込むときに使用する読み取りテーブルを制御することにより、プログラマーは言語の構文を変更または拡張できます。

スコープ

他の多くのプログラミング言語のプログラムと同様に、Common Lispプログラムは、変数、関数、および他の多くの種類のエンティティを参照するために名前を使用します。名前付き参照はスコープの対象となります。

名前とその名前が参照するエンティティとの関連付けは、バインディングと呼ばれます。

スコープとは、名前に特定のバインディングがあると判断される一連の状況を指します。

スコープの限定詞

CommonLispのスコープを決定する状況は次のとおりです。

  • 式内の参照の場所。化合物の左端の位置の場合は、特殊な演算子、マクロまたは関数のバインディングを指します。それ以外の場合は、変数のバインディングなどを指します。
  • 参照が行われる表現の種類。たとえば、(go x)ラベルへの制御の転送を意味しますが、変数を参照しますタグボディラベルは変数名とは別の名前空間にあるため、の両方のスコープをプログラムテキストの同じ領域でアクティブにすることができます。特殊形式またはマクロ形式は、構文内のすべての記号の意味を完全に制御できます。たとえば、クラス定義では、は基本クラスのリストであるため、これらの名前はクラス名のスペースで検索され、既存のバインディングへの参照ではなく、派生する新しいクラスの名前です。からと_ これらの事実は、純粋にx(print x)xx(defclass x (a b) ())(a b)xabdefclassこの式に関する唯一の一般的な事実はdefclass、マクロバインディングを参照していることです。他のすべてはまでdefclassです。
  • プログラムテキスト内の参照の場所。たとえば、変数への参照が、のバインディングを定義するaxなどのバインディング構造で囲まれている場合、その参照はそのバインディングによって作成されたスコープ内にあります。letx
  • 変数参照の場合、変数シンボルがローカルまたはグローバルに特別に宣言されているかどうか。これにより、参照が字句環境内で解決されるか、動的環境内で解決されるかが決まります。
  • 参照が解決される環境の特定のインスタンス。環境は、シンボルをバインディングにマップするランタイムディクショナリです。各種類の参照は、独自の種類の環境を使用します。字句変数への参照は、字句環境などで解決されます。複数の環境を同じ参照に関連付けることができます。たとえば、再帰または複数のスレッドの使用のおかげで、同じ機能の複数のアクティブ化が同時に存在する可能性があります。これらのアクティベーションは同じプログラムテキストを共有しますが、それぞれに独自の字句環境インスタンスがあります。

シンボルが何を指しているのかを理解するには、Common Lispプログラマーは、表現されている参照の種類、変数参照の場合に使用するスコープの種類(動的スコープと字句スコープ)、および実行時の状況を知る必要があります。参照が解決された環境、環境に導入されたバインディングなどはどこにありますか。

環境の種類

グローバル

Lispのいくつかの環境は世界的に普及しています。たとえば、新しいタイプが定義された場合、それ以降はどこでも認識されます。そのタイプへの参照は、このグローバル環境で検索します。

動的

Common Lispの環境の1つのタイプは、動的環境です。この環境で確立されたバインディングには動的な範囲があります。つまり、バインディングはletブロックなどの一部のコンストラクトの実行の開始時に確立され、そのコンストラクトの実行が終了すると消えます。その存続期間は、ブロック。ただし、動的バインディングはそのブロック内に表示されるだけではありません。また、そのブロックから呼び出されたすべての関数にも表示されます。このタイプの可視性は、不定スコープとして知られています。動的範囲(ブロックのアクティブ化と非アクティブ化に関連付けられた存続期間)と不定スコープ(そのブロックから呼び出されるすべての関数に表示される)を示すバインディングは、動的スコープを持つと言われます。

Common Lispは、動的スコープ変数をサポートしています。これは、特殊変数とも呼ばれます。リスタートやキャッチタグなど、他の特定の種類のバインディングも必然的に動的にスコープされます。関数バインディングは、(字句スコープの関数バインディングのみを提供する)を使用して動的にスコープすることはできませんがflet、関数オブジェクト(Common Lispの第1レベルのオブジェクト)を動的スコープの変数に割り当て、動的スコープで使用してバインドし、またはletを使用して呼び出すことができます。 funcallAPPLY

動的スコープは、グローバル変数に参照の明確さと規律を追加するため、非常に便利ですグローバル変数は、コンピュータサイエンスでは、潜在的なエラーの原因として嫌われています。これは、モジュール間でアドホックな秘密の通信チャネルが発生し、望ましくない驚くべき相互作用が発生する可能性があるためです。

Common Lispでは、トップレベルのバインディングのみを持つ特別な変数は、他のプログラミング言語のグローバル変数と同じように動作します。新しい値を格納することができ、その値は単に最上位のバインディングにあるものを置き換えます。グローバル変数の値を不注意に置き換えることは、グローバル変数の使用によって引き起こされるバグの中心です。ただし、特別な変数を操作する別の方法は、式内で新しいローカルバインディングを変数に与えることです。これは、変数の「再バインド」と呼ばれることもあります。動的スコープの変数をバインドすると、その変数の新しいメモリ位置が一時的に作成され、名前がその位置に関連付けられます。そのバインディングが有効である間、その変数へのすべての参照は新しいバインディングを参照します。以前のバインディングは非表示になっています。バインディング式の実行が終了すると、一時メモリの場所がなくなり、元の値がそのままの状態で古いバインディングが表示されます。もちろん、同じ変数の複数の動的バインディングをネストすることもできます。

マルチスレッドをサポートするCommonLisp実装では、動的スコープは実行の各スレッドに固有です。したがって、特別な変数はスレッドローカルストレージの抽象化として機能します。1つのスレッドが特別な変数を再バインドする場合、この再バインドは他のスレッドのその変数に影響を与えません。バインディングに格納されている値は、そのバインディングを作成したスレッドによってのみ取得できます。各スレッドが特別な変数をバインドする場合、スレッドローカルストレージのよう*x*に動作します。*x*再バインドしないスレッドの中で*x*、それは通常のグローバルのように動作します。これらのスレッドはすべて、の同じトップレベルのバインディングを参照し*x*ます。

動的変数を使用すると、追加のコンテキスト情報を使用して実行コンテキストを拡張できます。このコンテキスト情報は、追加の関数パラメーターとして表示されることなく、関数から関数に暗黙的に渡されます。これは、制御転送が無関係のコードのレイヤーを通過する必要がある場合に特に役立ちます。このコードは、追加のデータを渡すために追加のパラメーターで拡張することはできません。このような状況では、通常、グローバル変数が必要になります。スキームが再帰的に壊れないように、そのグローバル変数を保存して復元する必要があります。動的変数の再バインドがこれを処理します。そして、その変数はスレッドローカルにする必要があります(または、大きなミューテックスを使用する必要があります)。これにより、スキームがスレッドの下で壊れることがなくなります。動的スコープの実装でもこれを処理できます。

Common Lispライブラリには、多くの標準的な特殊変数があります。たとえば、すべての標準I / Oストリームは、よく知られている特殊変数の最上位のバインディングに格納されます。標準出力ストリームは* standard-output *に保存されます。

関数fooが標準出力に書き込むとします。

  defun  foo  ()
    format  t  "Hello、world" ))

その出力を文字列に取り込むために、* standard-output *を文字列ストリームにバインドして、次のように呼び出すことができます。

  with-output-to-string  * standard-output * 
    foo ))
-> "Hello、world"; 収集された出力は文字列として返されます

字句

CommonLispは字句環境をサポートします。正式には、字句環境のバインディングには字句スコープがあり、名前空間のタイプに応じて、不定の範囲または動的な範囲のいずれかを持つ場合があります。字句スコープとは、バインディングが確立されているブロックに可視性が物理的に制限されることを意味します。そのブロックにテキストで(つまり字句的に)埋め込まれていない参照は、単にそのバインディングを認識しません。

TAGBODYのタグには、字句スコープがあります。式(GO X)は、ラベルXを含むTAGBODYに埋め込まれていない場合は誤りです。ただし、ラベルバインディングは動的な範囲を持っているため、実行を終了するとラベルバインディングが消えます。字句クロージャの呼び出しによってそのコードのブロックが再入力された場合、そのクロージャの本体がGOを介してタグに制御を移そうとすることは無効です。

  defvar  * stashed *  ;; 機能を保持します

  tagbody 
    setf  * stashed *  lambda  () go  some-label )))
    go  end-label  ;;スキップ(print "Hello")
   some-label 
    print  "Hello" 
   end-label 
  ->  NIL

TAGBODYが実行されると、最初に、関数を特殊変数* stashed *に格納するsetfフォームが評価されます。次に、(go end-label)は、コードをスキップして、制御をend-labelに移します(「Hello」を出力)。end-labelはタグ本体の最後にあるため、タグ本体は終了し、NILを生成します。以前に記憶されていた関数が現在呼び出されているとします。

  funcall  * stashed *  ;; エラー!

この状況は誤りです。実装の応答の1つは、「GO:タグSOME-LABELのタグボディがすでに残っています」というメッセージを含むエラー条件です。関数は、タグ本体に字句的に埋め込まれている評価(go some-label)を試み、ラベルに解決します。ただし、タグボディが実行されていない(エクステントが終了している)ため、制御の転送を実行できません。

Lispのローカル関数バインディングには字句スコープがあり、変数バインディングにもデフォルトで字句スコープがあります。GOラベルとは対照的に、これらは両方とも無限の範囲を持っています。字句関数または変数バインディングが確立されると、そのバインディングを確立した構成が終了した後でも、そのバインディングは、それへの参照が可能な限り存在し続けます。字句クロージャのおかげで、確立構造の終了後の字句変数および関数への参照が可能です。

字句バインディングは、CommonLisp変数のデフォルトのバインディングモードです。個々のシンボルの場合、ローカル宣言またはグローバル宣言のいずれかによって、動的スコープに切り替えることができます。後者は、DEFVARやDEFPARAMETERなどの構造を使用することで暗黙的に発生する可能性があります。Common Lispプログラミングの重要な規則は、特別な(つまり動的にスコープされた)変数の名前が、いわゆる「イヤーマフ規則」のアスタリスク記号で始まり、終わる名前を持つことです。 [17]準拠している場合、この規則は、特殊変数用に別個の名前空間を効果的に作成するため、字句を意図した変数が誤って特殊化されることはありません。 *

字句スコープはいくつかの理由で役立ちます。

まず、ランタイム環境の構造が比較的単純であるため、変数と関数への参照を効率的なマシンコードにコンパイルできます。多くの場合、ストレージをスタックするように最適化できるため、字句スコープの開閉によるオーバーヘッドは最小限に抑えられます。完全なクロージャを生成する必要がある場合でも、クロージャの環境へのアクセスは依然として効率的です。通常、各変数はバインディングのベクトルへのオフセットになるため、変数参照は、ベースプラスオフセットアドレッシングモードを使用した単純なロードまたはストア命令になります。

第二に、字句スコープ(無限の範囲と組み合わせて)は字句クロージャを生じさせます。これにより、関数型プログラミングのルートであるファーストクラスオブジェクトである関数の使用を中心としたプログラミングのパラダイム全体が作成されます。

第三に、おそらく最も重要なことは、字句クロージャが利用されていない場合でも、字句スコープを使用すると、プログラムモジュールが不要な相互作用から分離されます。可視性が制限されているため、字句変数はプライベートです。あるモジュールAが字句変数Xをバインドし、別のモジュールBを呼び出す場合、B内のXへの参照が誤ってA内にバインドされたXに解決されることはありません。Bは単にXにアクセスできません。望ましいのは、CommonLispが特別な変数を提供することです。特別な変数を使用すると、モジュールAは、Aから呼び出された別のモジュールBに表示される変数Xのバインディングを設定できます。これを実行できることは利点であり、発生を防ぐことができることも利点です。その結果、CommonLispは字句スコープと動的スコープの両方をサポートします。

マクロ

Lispのマクロは、表面的には使用法の関数に似ています。ただし、評価される式を表すのではなく、プログラムのソースコードの変換を表します。マクロは、周囲のソースを引数として取得し、それらをパラメーターにバインドして、新しいソースフォームを計算します。この新しいフォームでは、マクロも使用できます。新しいソースフォームがマクロを使用しなくなるまで、マクロ展開が繰り返されます。最終的に計算される形式は、実行時に実行されるソースコードです。

Lispでのマクロの典型的な使用法:

  • 新しい制御構造(例:ループ構造、分岐構造)
  • スコーピングおよびバインディングコンストラクト
  • 複雑で繰り返されるソースコードの簡略化された構文
  • コンパイル時の副作用を伴うトップレベルの定義フォーム
  • データ駆動型プログラミング
  • 組み込みドメイン固有言語(例:SQLHTMLProlog
  • 暗黙のファイナライズフォーム

次のようなさまざまな標準のCommonLisp機能もマクロとして実装する必要があります。

  • setf割り当て/アクセス演算子のカスタムコンパイル時拡張を可能にする標準の抽象化
  • with-accessors、、およびその他with-slotswith-open-file同様のWITHマクロ
  • 実装に応じて、ifまたはcond他の特別な演算子に基づいて構築されたマクロです。マクロwhenで構成されていますunless
  • 強力なloopドメイン固有言語

マクロはdefmacroマクロによって定義されます特別な演算子マクロレットを使用すると、ローカル(字句スコープ)マクロを定義できます。define-symbol-macroおよびsymbol-macroletを使用して、シンボルのマクロを定義することもできます

PaulGrahamの著書OnLispは、CommonLispでのマクロの使用について詳しく説明しています。 DougHoyteの著書LetOver Lambdaは、マクロに関する議論を拡張し、「マクロは、プログラミング言語としてのlispの最大の利点であり、プログラミング言語の最大の利点である」と述べています。Hoyteは、マクロの反復型開発のいくつかの例を提供しています。

マクロを使用して新しい制御構造を定義する例

マクロを使用すると、Lispプログラマはその言語で新しい構文形式を作成できます。典型的な使用法の1つは、新しい制御構造を作成することです。サンプルマクロは、untilループ構造を提供します。構文は次のとおりです。

(テストフォームまで*)

untilのマクロ定義

defmacro  until  test  &body  body 
  let  ((start-tag  gensym  "START" ))
        end-tag    gensym  "END" )))
    ` tagbody  start-tag 
              when  test  go  end- tag ))
              progn  、@ body 
              go  start-tag 
              end-tag )))

tagbodyは、タグに名前を付け、 goフォームを使用してそれらのタグにジャンプする機能を提供する基本的なCommonLisp特殊演算子です。バッククォート`は、コードテンプレートを提供する表記法を提供します。ここで、コンマが前に付いたフォームの値が入力されます。コンマとアットマークが前に付いたフォームがスプライスされます。tagbodyフォームは終了条件をテストします。条件が真の場合、終了タグにジャンプします。それ以外の場合は、提供された本文コードが実行されてから、開始タグにジャンプします。

上記のuntilマクロ の使用例:

until  =  random  10  0 
  write-line  "Hello" ))

コードは、関数macroexpand-1を使用して展開できます。上記の例の展開は次のようになります。

TAGBODY 
 #:START1136 
 WHEN  ZEROP  RANDOM  10 ))
   GO  #:END1137 ))
 PROGN  WRITE-LINE  "hello" ))
 GO  #:START1136 
 #:END1137 

マクロ展開中、変数testの値は(=(random 10)0)であり、変数本体の値は((write-line "Hello"))です。本文はフォームのリストです。

記号は通常、自動的に大文字になります。拡張では、2つのラベルを持つTAGBODYを使用します。これらのラベルのシンボルはGENSYMによって計算され、どのパッケージにも組み込まれていません。2つのgoフォームは、これらのタグを使用してジャンプします。tagbodyはCommonLispのプリミティブ演算子(マクロではない)であるため、他の何かに展開されることはありません。展開されたフォームは、whenマクロを使用します。これも展開されます。ソースフォームを完全に拡張することをコードウォーキングと呼びます。

完全に拡張された(ウォークされた)フォームでは、whenフォームがプリミティブに置き換えられます

TAGBODY 
 #:START1136 
 IF  ZEROP  RANDOM  10 ))
     PROGN  GO  #:END1137 ))
   NIL 
 PROGN  WRITE-LINE  "hello" ))
 GO  #:START1136 ))
 #:END1137 

マクロを含むソースコードを正常に評価またはコンパイルする前に、すべてのマクロを展開する必要があります。マクロは、 S式を受け入れて返す関数と見なすことができます。抽象構文木に似ていますが、これらに限定されません。これらの関数は、最終的なソースコードを生成するために、エバリュエーターまたはコンパイラーの前に呼び出されます。マクロは通常のCommonLispで記述されており、利用可能な任意のCommon Lisp(またはサードパーティ)演算子を使用できます。

変数のキャプチャとシャドウイング

Common Lispマクロは、一般に変数キャプチャと呼ばれる機能を備えており、マクロ拡張本体のシンボルが呼び出し元のコンテキストのシンボルと一致するため、プログラマーはさまざまなシンボルが特別な意味を持つマクロを作成できます。変数キャプチャという用語は、演算子と関数の名前空間、タグボディラベルの名前空間、キャッチタグ、条件ハンドラー、再起動の名前空間など、すべての名前空間が不要なキャプチャに対して脆弱であるため、多少誤解を招く可能性があります。

変数キャプチャは、ソフトウェアの欠陥を引き起こす可能性があります。これは、次の2つの方法のいずれかで発生します。

  • 最初の方法では、マクロ展開は、マクロ作成者がグローバル名前空間で解決すると想定したシンボリック参照を誤って作成する可能性がありますが、マクロが展開されるコードは、その参照を盗むローカルのシャドウ定義を提供します。これをタイプ1キャプチャと呼びます。
  • 2番目の方法であるタイプ2キャプチャは、正反対です。マクロの引数の一部は、マクロ呼び出し元によって提供されるコードの一部であり、それらのコードは、周囲のバインディングを参照するように記述されています。ただし、マクロはこれらのコードを拡張に挿入し、これらの参照の一部を誤ってキャプチャする独自のバインディングを定義します。

LispのScheme方言は、両方のタイプのキャプチャ問題を排除する参照透過性を提供するマクロ書き込みシステムを提供します。このタイプのマクロシステムは、特にその支持者(この問題を自動的に解決しないマクロシステムを非衛生的と見なす)によって「衛生的」と呼ばれることがあります。[要出典]

Common Lispでは、マクロ衛生は2つの異なる方法のいずれかで保証されます。

1つのアプローチは、gensymsを使用することです。これは、キャプチャの脅威なしにマクロ拡張で使用できる、保証された一意のシンボルです。マクロ定義でのgensymの使用は手動の雑用ですが、gensymのインスタンス化と使用を簡素化するマクロを作成できます。Gensymsはタイプ2キャプチャを簡単に解決しますが、マクロ展開では参照をキャプチャする周囲のコードの干渉シンボルの名前を変更できないため、同じ方法でタイプ1キャプチャに適用することはできません。Gensymsを使用して、マクロ展開に必要なグローバルシンボルの安定したエイリアスを提供できます。マクロ展開では、既知の名前ではなくこれらの秘密のエイリアスが使用されるため、既知の名前を再定義してもマクロに悪影響はありません。

別のアプローチは、パッケージを使用することです。独自のパッケージで定義されたマクロは、展開時にそのパッケージの内部シンボルを使用するだけです。パッケージの使用は、タイプ1およびタイプ2のキャプチャを扱います。

ただし、パッケージは、標準のCommonLisp関数および演算子への参照のタイプ1キャプチャを解決しません。その理由は、キャプチャの問題を解決するためのパッケージの使用は、プライベートシンボル(1つのパッケージ内のシンボルであり、他のパッケージにインポートされていないか、他のパッケージで表示されていない)の使用を中心に展開しているためです。Common Lispライブラリシンボルは外部にあり、ユーザー定義パッケージに頻繁にインポートされたり、表示されたりします。

以下は、マクロの展開で発生する、演算子の名前空間での不要なキャプチャの例です。

 ;; UNTILの拡張では、DOを自由に使用します
 defmacro  until  expression  &body  body 
   ` do  () expression  、@ body ))

 ;; macroletは、DOの字句演算子バインディングを確立します
 macrolet  ((do  ...  ... 何か 他の ... ))
   until  =  random  10  0  write-line  "Hello" )))

マクロは、標準のCommonLispマクロを参照することを目的としuntilた呼び出し形式に展開されます。ただし、このコンテキストでは、まったく異なる意味を持つ可能性があるため、正しく機能しない可能性があります。 dododountil

Common Lispは、標準の演算子と関数の再定義を禁止することにより、それらのシャドウイングの問題を解決します。これは標準演算子を再定義するためdo、上記は実際には不適合のCommon Lispのフラグメントであり、実装がそれを診断して拒否できるようにします。

条件システム

条件システムは、 CommonLispでの例外処理を担当します[18]条件ハンドラー、および再起動を提供します条件は、例外的な状況(エラーなど)を説明するオブジェクトです。条件が通知されると、Common Lispシステムはこの条件タイプのハンドラーを検索し、ハンドラーを呼び出します。ハンドラー_これで、再起動を検索し、これらの再起動の1つを使用して、条件タイプや条件オブジェクトの一部として提供される関連情報などの情報を使用して現在の問題を自動的に修復し、適切な再起動関数を呼び出すことができます。

これらの再起動は、コードで処理されない場合、ユーザーに表示でき(たとえば、デバッガーのユーザーインターフェイスの一部として)、ユーザーは使用可能な再起動の1つを選択して呼び出すことができます。条件ハンドラーは(スタックを巻き戻さずに)エラーのコンテキストで呼び出されるため、他の例外処理システムが現在のルーチンを既に終了している場合、多くの場合、完全なエラー回復が可能です。デバッガー自体も、*debugger-hook*動的変数を使用してカスタマイズまたは置換できます。ファイナライザーなど のunwind-protectフォーム内にあるコードも、例外にもかかわらず、必要に応じて実行されます。

次の例(Symbolics Generaを使用)では、ファイルが存在しない場合、ユーザーはRead-Eval-Print-LOOP(REPL )から呼び出されたLisp関数テストでファイルを開こうとします。Lispシステムは4回の再起動を示します。ユーザーは、別のパス名の再起動を使用してOPENの再試行を選択し、別のパス名(lispm-int.lispの代わりにlispm-init.lisp)を入力します。ユーザーコードには、エラー処理コードは含まれていません。エラー処理と再起動のコード全体はLispシステムによって提供され、ユーザーコードを終了することなくエラーを処理および修復できます。

コマンド:(test "> zippy> lispm-int.lisp")

エラー:ファイルが見つかりませんでした。
       lispmの場合:> zippy> lispm-int.lisp.newest

LMFS:OPEN-LOCAL-LMFS-1
   引数0:#P "lispm:> zippy> lispm-int.lisp.newest"

sA、<Resume>:lispmのOPENを再試行します:> zippy> lispm-int.lisp.newest
sB:別のパス名を使用してOPENを再試行してください
sC、<中止>:TELNETサーバーのLispトップレベルに戻る
sD:プロセスTELNET端末を再起動します

->別のパス名を使用してOPENを再試行してください
代わりにどのパス名を使用してください[デフォルトのlispm:> zippy> lispm-int.lisp.newest]:
   lispm:> zippy> lispm-init.lisp.newest

...プログラムは継続します

Common Lisp Object System(CLOS)

Common Lispには、オブジェクト指向プログラミング用のツールキットであるCommon Lisp Object SystemまたはCLOSが含まれています。これは、あらゆる言語で利用できる最も強力なオブジェクトシステムの1つです。たとえば、Peter Norvigは、CLOSの機能(多重継承、ミックスイン、マルチメソッド、メタクラス、メソッドの組み合わせなど)を使用して、動的言語で実装するのが簡単なデザインパターンの数を説明しています。[19] オブジェクト指向プログラミングのためのCommonLispのいくつかの拡張が、ANSI Common Lisp標準に含まれることが提案されていますが、最終的にCLOSがCommonLispの標準オブジェクトシステムとして採用されました。CLOSは、多重ディスパッチを備えた動的オブジェクトシステムです。多重継承であり、 C ++Javaなどの静的言語に見られるOOP機能とは根本的に異なります動的オブジェクトシステムとして、CLOSは実行時にジェネリック関数とクラスへの変更を許可します。メソッドを追加および削除したり、クラスを追加および再定義したり、オブジェクトを更新してクラスを変更したり、オブジェクトのクラスを変更したりできます。

CLOSはANSICommonLispに統合されました。ジェネリック関数は通常の関数と同じように使用でき、ファーストクラスのデータ型です。すべてのCLOSクラスは、CommonLisp型システムに統合されています。多くのCommonLispタイプには、対応するクラスがあります。CommonLispにはCLOSの使用の可能性があります。仕様には、条件がCLOSで実装されているかどうかは記載されていません。パス名とストリームはCLOSで実装できます。ANSI Common Lisp用のCLOSのこれらのさらなる使用の可能性は、標準の一部ではありません。実際のCommonLisp実装は、パス名、ストリーム、入出力、条件、CLOS自体の実装などにCLOSを使用します。

コンパイラとインタプリタ

Lispインタプリタは、S式から読み取られたLispオブジェクト(リスト、シンボル、数値など)として提供されるLispソースコードを直接実行します。Lispコンパイラは、Lispソースコードからバイトコードまたはマシンコードを生成します。Common Lispを使用すると、個々のLisp関数をメモリにコンパイルすることも、ファイル全体を外部に保存されたコンパイル済みコード(faslファイル)にコンパイルすることもできます。

以前のLisp方言のいくつかの実装は、インタプリタとコンパイラの両方を提供していました。残念ながら、多くの場合、セマンティクスは異なっていました。これらの初期のLispは、コンパイラーで字句スコープを実装し、インタープリターで動的スコープを実装しました。Common Lispでは、インタプリタとコンパイラの両方がデフォルトで字句スコープを使用する必要があります。Common Lisp標準は、インタプリタとコンパイラの両方のセマンティクスを記述しています。コンパイラーは、個々の関数には関数compileを使用し、ファイルには関数compile -fileを使用して呼び出すことができます。Common Lispは型宣言を可能にし、コンパイラコード生成ポリシーに影響を与える方法を提供します。後者の場合、さまざまな最適化品質に0(重要ではない)から3(最も重要)までの値を指定できます。速度スペース安全性デバッグコンパイル速度

Lispコードを評価する関数もあります:evalevalコードを事前に解析されたS式として受け取り、他の言語のようにテキスト文字列としては受け取りません。このようにして、リストとシンボルを構築するための通常のLisp関数を使用してコードを構築し、このコードを関数で評価することができますevalいくつかのCommonLisp実装(Clozure CLやSBCLなど)はeval、コンパイラーを使用して実装しています。このようにして、関数を使用して評価されたとしても、コードはコンパイルされますeval

ファイルコンパイラは、関数compile-fileを使用して呼び出されます。コンパイルされたコードで生成されたファイルは、fasl高速ロードから)ファイルと呼ばれます。これらのfaslファイルとソースコードファイルは、関数loadを使用して実行中のCommonLispシステムにロードできます。実装に応じて、ファイルコンパイラはバイトコード(たとえば、Java仮想マシン用)、C言語コード(Cコンパイラでコンパイルされる)、または直接ネイティブコードを生成します。

コードが完全にコンパイルされていても、CommonLispの実装はインタラクティブに使用できます。したがって、インタプリタ言語の考え方は、インタラクティブなCommonLispには適用されません。

この言語は、読み取り時、コンパイル時、ロード時、および実行時を区別し、ユーザーコードがこの区別を行って、必要なステップで必要なタイプの処理を実行できるようにします。

インタラクティブな開発に特に適した特別な演算子がいくつか用意されています。たとえば、defvar値がまだバインドされていない場合にのみ、提供された変数に値を割り当てますが、defparameter常に割り当てを実行します。この区別は、ライブ画像のコードをインタラクティブに評価、コンパイル、およびロードする場合に役立ちます。

コンパイラーとインタープリターの作成を支援するために、いくつかの機能も提供されています。シンボルは第1レベルのオブジェクトで構成され、ユーザーコードによって直接操作できます。特別な演算子を使用すると、progvプログラムで字句バインディングを作成できますが、パッケージも操作できます。Lispコンパイラは、実行時にファイルまたは個々の関数をコンパイルするために使用できます。これらにより、Lispを別の言語の中間コンパイラまたはインタプリタとして簡単に使用できます。

コード例

誕生日のパラドックス

次のプログラムは、ユニークな誕生日の確率が50%未満の部屋の最小人数を計算します(誕生日のパラドックス、1人の場合は明らかに100%、2人の場合は364/365など。 )。答えは23です。

慣例により、CommonLispの定数は+文字で囲まれています。

defconstant  + year-size +  365 

defun Birthday  -paradox  probability  number-of-people 
  let  ((new-probability  *  /  -  + year-size +  number-of-people 
                               + year-size + 
                            probability )))
    if  <  new -確率 0.5 
        1+ 人数
        誕生日のパラドックス 新しい確率 1+ 人数)))))

REPL(Read Eval Print Loop)を 使用してサンプル関数を呼び出す:

CL-USER>(誕生日-パラドックス1.0 1)
23

人物オブジェクトのリストの並べ替え

person人の名前と年齢を表示するためのクラスとメソッドを定義します。次に、人のグループをオブジェクトのリストとして定義しpersonます。次に、並べ替えられたリストを繰り返し処理します。

defclass  person  ()
  ((name  :initarg  :name  :accessor  person-name 
   age   :initarg  :age   :accessor  person-age ))
  :documentation  "クラスPERSONとスロットNAMEおよびAGE。" ))

defmethod  display  ((object  person  stream 
  "PERSONオブジェクトを出力ストリームに表示します。" 
  with-slots  name  age  object 
    format  stream  "〜a(〜a)"  name  age )))

defparameter  * group * 
  list  make-instance'person  :name "Bob" :age 33 make-instance 'person  name "Chris" :age 16 make-instance'person :name " Ash" :age 23 ))"PERSONオブジェクトのリスト。"      
             
               
  

dolist  person  sort  copy-list  * group * 
                      # ' > 
                      :key  #' person-age ))
  display  person  * standard-output * 
  terpri ))

3つの名前を年齢の降順で印刷します。

ボブ(33)
アッシュ(23)
クリス(16)

二乗による指数化

LOOPマクロの使用方法を示します。

defun  power  x  n 
  loop  with  result  =  1 
        while  plusp  n 
        when  oddp  n  do  setf  result  *  result  x ))
        do  setf  x  *  x  x 
                 n  truncate  n  2 ))
        finally  結果を返す )))

使用例:

CL-USER>(power 2 200)
1606938044258990275541962092341162602522202993782792835301376

組み込みのべき乗と比較してください。

CL-USER>(=(expt 2 200)(power 2 200))
T

利用可能なシェルのリストを見つける

WITH-OPEN-FILEは、ファイルを開いてストリームを提供するマクロです。フォームが返されると、ファイルは自動的に閉じられます。FUNCALLは関数オブジェクトを呼び出します。LOOPは、述部に一致するすべての行を収集します。

defun  list-matching-lines  file  predicate 
  "ファイル内の行のリストを返します。その行に適用された述語
はTを返します。" 
  with-open-file  stream  file 
    loop  for  line  =  read- line  stream  nil  nil 
          while  line 
          when  funcall  predicate  line 
          collect  it )))

関数AVAILABLE-SHELLSは、パス名と無名関数を述語として、上記の関数LIST-MAT​​CHING-LINESを呼び出します。述語は、シェルまたはNILのパス名を返します(文字列がシェルのファイル名でない場合)。

defun  available-shells  &optional  file  #p "/ etc / shells" ))
  list-matching-lines 
   file 
   lambda  line 
     and  plusp  length  line ))
          char =  char  line  0  #\ / 
          pathname 
           string-right-trim  ' #\ space  #\ tab  line ))))))

結果の例(Mac OS X 10.6の場合):

CL-USER  >  使用可能シェル
#P "/ bin / bash"  #P "/ bin / csh"  #P "/ bin / ksh"  #P "/ bin / sh"  #P "/ bin / tcsh"  #P "/ bin / zsh" 

他のLispとの比較

Common Lispは、 Schemeと最も頻繁に比較され、対照的です—2つの最も人気のあるLisp方言であるという理由だけで。SchemeはCLよりも前からあり、同じLispの伝統だけでなく、同じエンジニアの何人かから来ています。GeraldJaySussmanがSchemeを設計したGuyL。Steeleは、CommonLispの標準化委員会の議長を務めました。

Common Lispは、特定の製品(それぞれ、GNU EmacsとAutoCAD)に組み込まれている拡張言語であるEmacs LispAutoLISPなどのLispバリアントとは対照的に、汎用プログラミング言語です。以前の多くのLispとは異なり、Common Lisp(Schemeなど)は、解釈されたコードとコンパイルされたコードの両方にデフォルトで 字句変数スコープを使用します。

ZetaLispやFranzLispなどの設計がCommonLispに貢献したほとんどのLispシステムは、インタプリタで動的スコープの変数を使用し、コンパイラで字句スコープの変数を使用していました。スキームは、字句スコープの変数の唯一の使用をLispに導入しました。ALGOL68からのインスピレーションCLは動的スコープの変数もサポートしますが、それらは「特殊」として明示的に宣言する必要があります。ANSI CLインタープリターとコンパイラーの間で、スコープに違いはありません。

Common Lispは、 Lisp-2およびScheme a Lisp-1と呼ばれることもあり、CLが関数と変数に別々の名前空間を使用することを指します。(実際、CLには、goタグ、ブロック名、キーワードなど、多くの名前空間があります)。loopCLとSchemeの支持者の間には、複数の名前空間に関係するトレードオフについて長年の論争があります。スキームでは、関数と衝突する変数名を付けないようにする必要があります。スキーム関数には、システム関数と競合しないように、、、lisまたはlstという名前の引数が含まれることがよくあります。lystlistただし、CLでは、関数を引数として渡すときに関数の名前空間を明示的に参照する必要があります。これは、sort上記の例のように、よくあることです。

CLは、ブール値の処理においてもSchemeとは異なります。スキームは、特別な値#tと#fを使用して、真実と偽りを表します。CLは、記号TとNILを使用するという古いLisp規則に従い、NILは空のリストも表します。CLでは、 NIL以外の値は、などの条件によってtrueとして扱われますがif、Schemeでは、#f以外のすべての値はtrueとして扱われます。これらの規則により、両方の言語の一部の演算子は、述語(ブール値の質問に答える)と、さらに計算するための有用な値の両方として機能できますが、Schemeでは、CommonLispのNILと同等の値 '()はtrueと評価されますブール式で。

最後に、Scheme標準ドキュメントには末尾呼び出しの最適化が必要ですが、CL標準には必要ありません。ほとんどのCL実装は末尾呼び出しの最適化を提供しますが、多くの場合、プログラマーが最適化ディレクティブを使用する場合に限ります。それにもかかわらず、一般的なCLコーディングスタイルは、Schemeスタイルが好む再帰のユビキタスな使用を支持しません。Schemeプログラマーが末尾再帰で表現するもの、CLユーザーは通常、、、、または(最近では)の反復式doで表現します。パッケージ。 dolistloopiterate

実装

カテゴリCommonLispの実装を参照してください。

Common Lispは、1つの実装( Perlなど)ではなく、仕様( AdaCなど)によって定義されます。多くの実装があり、それらが有効に異なる可能性のある標準の詳細領域があります。

さらに、実装には拡張機能が付属する傾向があり、標準でカバーされていない機能を提供します。

  • インタラクティブトップレベル(REPL)
  • デバッガー、ステッパー、インスペクター
  • 弱いデータ構造(ハッシュテーブル)
  • 拡張可能なシーケンス
  • 拡張可能なループ
  • 環境へのアクセス
  • CLOSメタオブジェクトプロトコル
  • CLOSベースの拡張可能なストリーム
  • CLOSベースの条件システム
  • ネットワークストリーム
  • 永続的なCLOS
  • Unicodeサポート
  • 外国語インターフェース(多くの場合C)
  • オペレーティングシステムインターフェイス
  • Javaインターフェイス
  • スレッドとマルチプロセッシング
  • アプリケーション配信(アプリケーション、ダイナミックライブラリ)
  • 画像の保存

無料のオープンソースソフトウェアライブラリは、移植可能な方法でCommon Lispの拡張機能をサポートするために作成されており、Common-Lisp.net [20]およびCLOCC(Common Lisp Open Code Collection)[21 ]プロジェクト。

Common Lispの実装では、ネイティブコードのコンパイル、バイトコードのコンパイル、または解釈を任意に組み合わせて使用​​できます。Common Lispは、インクリメンタルコンパイラ、ファイルコンパイラ、ブロックコンパイラをサポートするように設計されています。言語仕様では、コンパイルを最適化するための標準宣言(関数のインライン化や型の特殊化など)が提案されています。最も一般的なLisp実装は、ソースコードをネイティブマシンコードにコンパイルします。一部の実装では、(最適化された)スタンドアロンアプリケーションを作成できます。その他は、解釈されたバイトコードにコンパイルされます、これはネイティブコードよりも効率的ではありませんが、バイナリコードの移植性を容易にします。一部のコンパイラは、CommonLispコードをCコードにコンパイルします。Lispが純粋に解釈された言語であるという誤解は、Lisp環境がインタラクティブなプロンプトを提供し、そのコードが1つずつ段階的にコンパイルされるためである可能性が最も高いです。Common Lispでは、インクリメンタルコンパイルが広く使用されています。

一部のUnixベースの実装(CLISPSBCL)は、スクリプト言語として使用できます。つまり、PerlまたはUnixシェルインタープリターと同じようにシステムによって透過的に呼び出されます。[22]

実装のリスト

商用実装

Allegro Common Lisp
Microsoft Windows、FreeBSD、Linux、Apple macOS、およびさまざまなUNIXバリアント用。Allegro CLは、統合開発環境(IDE)(WindowsおよびLinux用)とアプリケーション配信のための広範な機能を提供します。
Liquid Common Lisp
以前はLucidCommonLispと呼ばれていました。メンテナンスのみで、新しいリリースはありません。
LispWorks
Microsoft Windows、FreeBSD、Linux、Apple macOS、iOS、Android、およびさまざまなUNIXバリアント用。LispWorksは、統合開発環境(IDE)(ほとんどのプラットフォームで利用可能ですが、iOSとAndroidでは利用できません)とアプリケーション配信のための広範な機能を提供します。
mocl
iOS、Android、macOS用。
オープンジェネラ
DECAlphaの場合。
Scieneer Common Lisp
これは、高性能の科学計算用に設計されています。

自由に再配布可能な実装

Armed Bear Common Lisp(ABCL)
Java仮想マシンで実行されるCL実装[23] Javaバイトコードへのコンパイラが含まれており、CLからJavaライブラリにアクセスできます。以前は、Armed Bear JEditorの単なるコンポーネントでした。
CLISP
バイトコードコンパイルの実装で、移植可能で、いくつかのUnixおよびUnixライクなシステム(macOSを含む)、およびMicrosoftWindowsやその他のいくつかのシステムで実行されます。
Clozure CL(CCL)
もともとはMacintoshCommonLispの無料のオープンソースフォークです。その歴史が示すように、CCLはMacintosh用に作成されましたが、Clozure CLは現在、macOSFreeBSDLinuxSolaris、およびWindowsで動作します。32ビットおよび64ビットのx86ポートが各プラットフォームでサポートされています。さらに、MacOSおよびLinux用のPowerPCポートがあります。CCLは以前はOpenMCLと呼ばれていましたが、Macintosh Common Lispのオープンソースバージョンとの混同を避けるために、その名前は使用されなくなりました。
CMUCL
カーネギーメロン大学出身で、現在はボランティアのグループによって無料のオープンソースソフトウェアとして維持されています。CMUCLは高速のネイティブコードコンパイラを使用します。LinuxおよびBSDfor Intelx86で利用できますLinux for Alpha; Intelx86およびPowerPC用のmacOS 。ネイティブプラットフォーム上のSolaris、IRIX、およびHP-UX。
Corman Common Lisp
MicrosoftWindowsの場合。2015年1月、CormanLispはMITライセンスの下で公開されました。[24]
埋め込み型CommonLisp(ECL)
ECLには、バイトコードインタープリターとコンパイラーが含まれています。また、Cコンパイラを介してLispコードをマシンコードにコンパイルすることもできます。次に、ECLはLispコードをCにコンパイルし、CコンパイラでCコードをコンパイルして、結果のマシンコードをロードできます。ECLをCプログラムに埋め込み、CコードをCommonLispプログラムに埋め込むこともできます。
GNU Common Lisp(GCL)
GNUプロジェクトのLispコンパイラ。まだ完全にANSIに準拠していませんが、GCLは、数学ツールMaximaAXIOM、および(歴史的に)ACL2を含むいくつかの大規模プロジェクトに最適な実装です。GCLは、 11の異なるアーキテクチャーの下でLinux上で実行され、Windows、Solaris、およびFreeBSDの下でも実行されます。
Macintosh Common Lisp(MCL)
Mac OSXを実行しているPowerPCプロセッサを搭載したAppleMacintoshコンピュータ用のバージョン5.2はオープンソースです。RMCL(MCL 5.2に基づく)は、AppleのRosettaバイナリトランスレータを使用するIntelベースのAppleMacintoshコンピュータで実行されます。
ManKai Common Lisp(MKCL)
ECLのブランチMKCLは、大幅に作り直された、ネイティブにマルチスレッド化されたランタイムシステムを通じて、信頼性、安定性、および全体的なコード品質を強調しています。Linuxでは、MKCLは完全にPOSIXに準拠したランタイムシステムを備えています。
Movitz
基盤となるOSに依存せずに、 x86コンピューター用のLisp環境を実装します。
ポップログ
Poplogは、POP-11、オプションでProlog、およびStandard ML(SML)を備えたバージョンのCLを実装し、混合言語プログラミングを可能にします。結局のところ、実装言語はPOP-11であり、インクリメンタルにコンパイルされます。また、コンパイラと通信するEmacsのようなエディタが統合されています。
Steel Bank Common Lisp(SBCL)
CMUCLからのブランチ「大まかに言えば、SBCLは保守性をより重視することでCMUCLと区別されます。」[25] SBCLは、HP / UXを除くCMUCLが実行するプラットフォームで実行されます。さらに、Linux for AMD64、PowerPC、SPARC、MIPS、Windows x86 [26]で実行され、WindowsAMD64での実行を実験的にサポートしています。SBCLはデフォルトではインタプリタを使用しません。ユーザーがインタープリターをオンにしない限り、すべての式はネイティブコードにコンパイルされます。SBCLコンパイラは、以前のバージョンのThe Computer Language BenchmarksGameに従って高速ネイティブコードを生成します[27]
Ufasoft Common Lisp
C ++で記述されたコアを備えたWindowsプラットフォーム用のCLISPのポート。

その他の実装

オースティン京都CommonLisp
ビル・シェルターによるKyoto CommonLispの進化
バタフライコモンリスプ
BBNButterflyマルチプロセッサコンピュータ用のSchemeで記述された実装[28] [29]
CLICC
CommonLispからCへのコンパイラ[30]
CLOE
SymbolicsによるPC用のCommonLisp
Codemist Common Lisp
数式処理システムAxiomの商用バージョンに使用されます[31] [32]
ExperCommon Lisp
ExperTelligenceによるAppleMacintoshの初期の実装
ゴールデンコモンリスプ
GoldHillInc。によるPCの実装[33] [34]
Ibuki Common Lisp
京都コモンリスプの商品化バージョン
京都コモンリスプ
Cをターゲット言語として使用した最初のCommonLispコンパイラ。GCL、ECL、およびMKCLは、このCommonLisp実装に由来します。
L
IS Robotics、現在はiRobotによって開発された組み込みシステム用のCommonLispの小さなバージョン[35]
LispマシンSymbolics、TI [36] [37]およびXerox [38]から)
ネイティブのLisp方言(Lisp Machine LispまたはInterlisp)に加えて、CommonLispの実装を提供しました。CLOSも利用可能でした。Symbolicsは、拡張バージョンのCommonLispを提供します。[39] [40] [41]
Procyon Common Lisp
フランツがAllegroCLのWindowsポートに使用するWindowsおよびMacOSの実装
スターサファイアCommonLISP
PCの実装
SubL
Cyc知識ベースシステムの実装に使用されるCommonLispの変種[42]
トップレベルのCommonLisp
同時実行の初期の実装[43]
WCL
共有ライブラリの実装[44] [45]
VAX Common Lisp
VMSまたはULTRIXを実行するVAXシステムで実行されたDigitalEquipmentCorporationの実装
XLISP
DavidBetzによって書かれた実装[46]

アプリケーション

Common Lispは、研究アプリケーション(多くの場合、人工知能)の開発、プロトタイプの迅速な開発、または展開されたアプリケーションに使用されます。

Common Lispは、 Yahoo!を含む多くの商用アプリケーションで使用されています。元々はPaulGrahamが関与し、後にC ++Perlで書き直されたWebコマースサイトを保存します。[47]その他の注目すべき例は次のとおりです。

CommonLispで書かれた次のようなオープンソースアプリケーションもあります。

も参照してください

参考文献

  1. ^ 「ANSI規格アクション-2018年12月28日」 (PDF)ansi.org
  2. ^ 引用された標準の表紙から引用。ANSI INCITS 226-1994 [S2008]、標準のドキュメントページ で販売されています。 2020年9月27日、 WaybackMachineでアーカイブされました。
  3. ^ 「CLHS:Common Lisp HyperSpec(TM)について」lispworks.com
  4. ^ 「CLHS:セクション1.1.2」lispworks.com
  5. ^ 「CommonLispの実装:調査」2012年4月21日にオリジナルからアーカイブされました2007年12月22日取得
  6. ^ 「古いLISPプログラムはまだCommonLispで実行されています」2015年5月13日取得
  7. ^ 「「Yu-ShiangLisp」のルーツ、Jon L Whiteからのメール、1982年」cmu.edu
  8. ^ 「メールインデックス」cl-su-ai.lisp.se
  9. ^ Knee-jerk Anti-LOOPismおよびその他の電子メール現象:コンピューターを介したコミュニケーションにおける口頭、書面、および電子パターン、JoAnneYatesおよびWandaJ。Orlikowski。、1993年 2012年8月8日、 WaybackMachineでアーカイブ
  10. ^ Jr、スティール; L、ガイ(1982年8月15日)。COMMONLISPの概要Lfp'82。ACM。pp。98–107。土井10.1145 /800068.802140ISBN 9780897910828S2CID14517358 _
  11. ^ Reddy、Abhishek(2008年8月22日)。「CommonLispの機能」
  12. ^ 「Unicodeサポート」Common LispWiki 2008年8月21日取得
  13. ^ リチャードP.ガブリエル; ケントM.ピットマン(1988年6月)。「機能セルと値セルの分離の技術的問題」Lispおよび記号計算1(1):81–101。土井10.1007 / bf01806178S2CID26716515_ 
  14. ^ 「CommonLispHyperspec:セクション3.1.7」
  15. ^ 「CommonLispHyperspec:FunctionFLOOR」
  16. ^ 「CommonLispHyperspec:AccessorGETHASH」
  17. ^ 「LetOverLambda」letoverlambda.com
  18. ^ Peter Seibel(2005年4月7日)。実用的なCommonLisp押してください。ISBN 978-1-59059-239-7
  19. ^ 「動的計画法のデザインパターン」norvig.com
  20. ^ 「Common-Lisp.net」
  21. ^ 「CommonLispオープンコードコレクション」
  22. ^ 「32.6。CLISPによるクイックスタート配信」clisp.cons.org
  23. ^ 「武装したクマのCommonLisp」
  24. ^ 「CormanLispソースが利用可能になりました」
  25. ^ 「歴史と著作権」Steel Bank CommonLisp
  26. ^ 「プラットフォームテーブル」Steel Bank CommonLisp
  27. ^ 「どのプログラムが最速ですか?–コンピュータ言語ベンチマークゲーム」2013年5月20日。2013年5月20日のオリジナルからアーカイブ
  28. ^ 「パッケージ:lang / lisp / impl / bbn /」cs.cmu.edu
  29. ^ 「ButterflyLispの最近の開発、1987年、AAAI議事録」(PDF)aaai.org
  30. ^ Burkart、O。; Goerigk、W。; Knutzen、H。(1992年6月22日)。「CLICC:CへのCommonLispプログラムのコンパイルへの新しいアプローチ」。CiteSeerX10.1.1.38.1282_  {{cite journal}}引用ジャーナルには|journal=ヘルプ)が必要です
  31. ^ "codemist.co.uk"lisp.codemist.co.uk
  32. ^ 「公理、30年の地平線、43ページ」(PDF)
  33. ^ 「GoldenCommonLisp開発者」goldhill-inc.com
  34. ^ Golden Common LISP:A Hands-On Approach、David J. Steele、2000年6月、Addison Wesley Publishing Company
  35. ^ ブルックス、ロドニーA。; al。、et(1995年6月22日)。「L–組み込みシステムのCommonLisp」。CiteSeerX10.1.1.2.1953_  {{cite journal}}引用ジャーナルには|journal=ヘルプ)が必要です
  36. ^ 「TIExplorerプログラミングの概念」(PDF)
  37. ^ 「TIExplorerLispリファレンス」(PDF)
  38. ^ 「MedleyLispリリースノート」(PDF)
  39. ^ 「SymbolicsCommonLisp辞書」(PDF)trailing-edge.com
  40. ^ 「シンボリックスの一般的なLisp言語の概念」(PDF)trailing-edge.com
  41. ^ 「SymbolicsCommonLispプログラミング構造」(PDF)trailing-edge.com
  42. ^ 「SubLリファレンス–Cycorp」cyc.com
  43. ^ 「トップレベル株式会社–ソフトウェア保存グループ」softwarepreservation.org
  44. ^ WCL:Unixでの効率的なCommon Lispアプリケーションの提供、LISPと関数型プログラミングに関する1992年のACM会議の議事録、 260〜269ページ
  45. ^ "commonlisp.net :: WCL"pgc.com2016年4月5日にオリジナルからアーカイブされました2016年3月25日取得
  46. ^ 「パッケージ:lang / lisp / impl / xlisp /」cs.cmu.edu
  47. ^ 「平均を破る」paulgraham.com
  48. ^ 「承認者のアシスタント」(PDF)aaai.org
  49. ^ 2009年12月12日、 WaybackMachineでアーカイブされたAmericanExpress Authorizer's Assistant
  50. ^ 2016年8月2日にWaybackMachineでアーカイブされたリアルタイムアプリケーション開発 Gensym。2016年8月16日取得。
  51. ^ 「GenworksGDL」
  52. ^ 「Opusmodus–ホーム」
  53. ^ PWGL –ホーム、2013年7月17日取得。
  54. ^ a b "Aerospace – CommonLisp"lisp-lang.org
  55. ^ 「ピアノユーザー、メーカーページから取得」
  56. ^ 「Grammarly.com、本番環境でのLispの実行」
  57. ^ 「リモートエージェント」ti.arc.nasa.gov
  58. ^ 「JPLでのリスピング」
  59. ^ 「FranzIncカスタマーアプリケーション:NASA」franz.com
  60. ^ スパイク計画およびスケジューリングシステムStsci.edu。2013年7月17日取得。
  61. ^ 「FranzIncの顧客アプリケーション:宇宙望遠鏡研究所」franz.com
  62. ^ 「すべての始まり…別名CLRの誕生」microsoft.com
  63. ^ ハフマン、スティーブ。「lispで」賛成2018年5月17日にオリジナルからアーカイブされました2019年5月11日取得
  64. ^ 「Pgloader」
  65. ^ 「なぜpgloaderがこれほど高速なのか」

参考文献

Common Lisp(言語)またはCommon Lispを使用したプログラミング(特にAIプログラミング)について出版された(または出版されようとしている)本の年代順のリスト。

外部リンク