0
(defun prefix (a y) (cond ((null y) nil)
    (t (cons (cons a (car y)) (prefix a (cdr y))))))
(setq result prefix(a (cons 1 2)))
(print result)

この関数 cdr は、リスト y を調べて (a (car y)) を再帰的に出力します。P が最初のパラメーターで、y がリスト (1 2 3) の場合、((P1) (P2) (P3)) を返す必要があります。ただし、パラメーターを指定して実行しようとすると、関数を機能させることができません。ここで何が間違っていますか?

4

3 に答える 3

3

この関数cdrは、リストyを調べ、(a(car y))を再帰的に出力します。

どうすれば何でも印刷できますか?関数には単一のprintステートメントはありません。

Pが最初のパラメーターで、yがリスト(1 2 3)の場合、((P1)(P2)(P3))を返す必要があります。

コードを見ると、返される((P . 1) (P . 2) (P . 3))のではなく返されます((P1) (P2) (P3))

ただし、パラメーターを指定して実行しようとすると、関数を機能させることができません。ここで何が間違っていますか?

あなたの電話は間違っているようです。prefix( ... )何かがLispのようには見えません。Lispでは、関数を呼び出すための構文はです(prefix ... )。関数名は、開き括弧の後に続きます。

(defun prefix (a y)
  (cond ((null y) nil)
        (t (cons (cons a (car y))
                 (prefix a (cdr y))))))

(setq result prefix(a (cons 1 2)))  ; this line has problems

(print result)
于 2012-12-08T09:08:09.480 に答える
1

他のことをする前に、入手可能なLispの入門テキストを見てください。

[dons code-review 帽子]

(defun prefix (a y) (cond ((null y) nil)
    (t (cons (cons a (car y)) (prefix a (cdr y))))))

(setq result prefix(a (cons 1 2)))

(print result)

第一に、これらの定義は、散文の説明がそうすべきだと言っていることを実行しません。再帰関数の一部として印刷する必要はありません。を印刷しますresultが、それはprefixed リスト全体がまとめられた後です。関数prefixも新しいシンボルを返すのではなく、ペアを返します。


それらを印刷するために中間値を設定する必要はありません。Lisp関数は暗黙的に戻り、なしで構成できますsetq

(defun prefix (a y) 
  (cond ((null y) nil)
         (t (cons (cons a (car y)) 
              (prefix a (cdr y))))))

(print prefix(a (cons 1 2)))

Lisp は完全にプレフィックス表記であり、関数名は呼び出し括弧内に表示されます。

...

(print (prefix a (cons 1 2)))

aあなたのprefix呼び出しでは、バインドされていない変数です。a「記号」'aまたは「キーワードa」を使用するつもりだったと思います:a。単に を渡すaと、Lisp はその名前を変数として評価しようとし、値が割り当てられていない場合は失敗します。

...

(print (prefix 'a (cons 1 2)))

の 2 番目の引数prefixは適切なリストではありません。それはペアです。違いを確認するには、両方のコンストラクトの基になるポインター構造を確認する必要があります。

(cons 1 2) => [ 1 | 2 ]
(list 1 2) => [ 1 |   ]
                    \
                  [ 2 |  ]
                        \
                        NIL

関数が適切なリストを期待している場所でペアを使用しようとすると、ペア#<TYPE-ERROR expected-type: LIST datum: 2>の末尾がリストではないのに対し、 a の末尾は であるためlist エラーが発生します。


(defun prefix (a y) 
  (cond ((null y) nil)
         (t (cons (cons a (car y)) 
              (prefix a (cdr y))))))

(print (prefix 'a (list 1 2)))

この時点で、実行可能なプログラムができました。(print (prefix 'a (list 1 2)))その関数を定義した後に REPL で評価すると、

CL-USER> (print (prefix 'a (list 1 2)))

((A . 1) (A . 2)) 
((A . 1) (A . 2))

出力が重複していることに注意してください。これは、Lisp REPL が、評価した任意のフォームの戻り値を自動的に出力するためです。これは、実際にprint完全にドロップできることを意味します。

CL-USER> (prefix 'a (list 1 2))
((A . 1) (A . 2))

あなたが望んでいたことを行う (最初の引数を接頭辞としてシンボルの新しいリストを作成する) には、実際にはいくつかの文字列操作を使用internしてシンボルを作成する必要があります。

(defun prefix (a y) 
  (cond ((null y) nil)
         (t (cons (intern (format nil "~a~a" a (car y)))
              (prefix a (cdr y))))))

(prefix 'a (list 1 2))今すぐ返す必要があります(A1 A2)。Common Lisp は大文字と小文字を区別せず、自動的に小文字の記号を大文字に変換することに注意してください。


再帰は優れた学習ツールであり、末尾再帰はSchemeの主要な反復構造ですが、通常、可能であれば反復的に行うことをお勧めします。CL は末尾呼び出しの最適化を保証しないことに注意してください。したがって、これは通常、loopまたはmapcar(場合dolistによっては) を使用することを意味します。

(defun iterative-prefix (a a-list)
  (loop for elem in a-list
        collect (intern (format nil "~a~a" a elem))))

(defun map-prefix (a a-list)
  (mapcar 
   (lambda (elem) (intern (format nil "~a~a" a elem)))
   a-list))

これらはどちらも再帰バージョンと同じ出力を提供しますが、十分に長いリストを指定した場合、スタック領域が不足するという同じリスクはありません。

CL-USER> (prefix 'a (make-list 100000 :initial-element 1))
Control stack guard page temporarily disabled: proceed with caution
Control stack guard page temporarily disabled: proceed with caution
; Evaluation aborted on #<SB-KERNEL::CONTROL-STACK-EXHAUSTED {1004D674F3}>.

CL-USER> (iterative-prefix 'a (make-list 100000 :initial-element 1))
(A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 ...)

CL-USER>
于 2013-02-12T14:50:59.280 に答える
0

2つのコメント:

まず、consは、consの最初の引数とセルのcdrの2番目の引数を持つ新しいconsセルを返します。したがって、(cons 1 2)は、表示されている新しいconsセル(1.2)を作成します。

結果をリスト(1 2)にする場合は、2番目の引数をリストにする必要があります:(cons 1'(2))

第二に、あなたはかなり醜いCスタイルのLispコードを書いた。検討:

(defun prefix (a y)
  (if ((null y) nil)
    (cons (cons a (car y)) (prefix a (cdr y)))))
(print (prefix a '( 1 2))))

注:次の行でのみ使用される変数を作成しないでください。コードを適切にレイアウトします(人々はLispを読むときに括弧を解析せず、従来のレイアウトに依存しています)。テストとデフォルトが1つしかない場合(つまり、if / then / else)、condの代わりにifを使用します。

あなたのコードにはまだエラーがあります(私はそれらを修正していません)。このコードを実際にREPL内に記述したようには見えません。そうすると、Lispコードを記述した方がはるかに簡単になります。これは、Algol言語に対するLispの大きな利点の1つです。

于 2013-02-12T07:59:01.447 に答える