Emacs Lisp: consセルと引用符?

このQ&Aのポイント
  • Emacs Lispについて勉強しています。リストとconsセルについての質問です。
  • Lisp Interactionモードでのリスト作成時のエラーについて質問があります。
  • リストとconsセルの振る舞いについて質問です。
回答を見る
  • ベストアンサー

Emacs Lisp: consセルと引用符?

お世話になります。 Emacs Lispについて勉強しています 今は、リスト、consセルについて勉強しています。 以下、拙い質問になるかと思いますが、よろしくお願いします。 Lisp Interactionモードのバッファに、 '(1 2 3) というリストを書いて行末でC-jを押下すると (1 2 3) という式を返します。 しかし、 (1 2 3) という式を書いて行末でC-jを押下すると Debugger entered--Lisp error: (invalid-function 1) というエラーになります。 これは、1が関数、2と3が引数として評価されるからですね。 さて、リストではなく「consセル」を作るとします。 (1 . 2) と書いてC-jを押下すると、やはり Debugger entered--Lisp error: (invalid-function 1) というエラーになります。 '(1 . 2) であれば (1 . 2) と、正しくconsセルが出来ます。 '(1 . nil) であれば (1) というリストを返します。 これは、cdr部がnilであるconsセルはリストになるからですね。 では、3と、「1とnilからなるリスト」のconsセルを作って、結果的に(3 1)というリストにしようとします。 '(3 . '(1 . nil)) この場合は、予想に反して (3 quote (1)) となります。 '(3 . (1 . nil)) であれば、 (3 1) となります。一方、 (3 . (1 . nil)) とすると、 Debugger entered--Lisp error: (invalid-function 3) となります。 まとめると、 3と、「1とnilからなるリスト」のconsセルを作って、結果的に(3 1)というリストにしようとした場合は、 '(3 . (1 . nil)) のように外側のconsセルはクォートし、内側のconsセルはクォートしない、ということになりますね。 このクォートの振る舞いの違いはなぜでしょうか。 よろしくお願いします。

質問者が選んだベストアンサー

  • ベストアンサー
  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

ん~, どこに着目しているのかがよくわからない.... まず Lisp インタプリタの動作としては「S式を全部読み込んでから評価」です. で #1 にも書いたように 'x は読み込んだ直後, 評価よりも前に (quote x) に変換されます. これはもちろん (* 3 (+ 2 1)) でも同じで, 「(* 3 (+ 2 1)」まで読み込んだ時点 (まだ最後のかっこは読み込んでいません) で「(+ 2 1)」というリストは完成してるけど, この時点で評価しているわけではありません. S式全体を読み込んでからおもむろに評価を始めます. でスペシャルフォーム quote 自体は「引数 (1個) を評価せずそのまま返す」から, (quote (* 3 (+ 2 1))) を評価した結果は引数である (* 3 (+ 2 1)) そのものです. これに対し「*」のような普通の関数は「全ての引数を評価し, 得られた値に対して当該関数 (今の場合は『*』) を適用する」という動作をします. つまり (* 3 (+ 2 1)) に対しては 1. S式全体はリストで, その先頭要素「*」は「普通の関数」である 2. だからすべての引数 (今の場合は「3」と「(+ 2 1)」) を評価する 3. 最後に得られた値に対して関数「*」を適用する 結果 (6 ではなく) 9 という値が得られます.

TYWalker
質問者

お礼

ありがとうございます! たしかに関数とスペシャルフォームというものの差が分かっていませんでした。 事情を説明すると、自分的にはEmacsのカスタマイズをちょこちょこっとやるつもりで、薄い本に取り組んでいたのですが、Lispそのものの説明は割愛されているので、疑問が湧いてしまいました。 やはりLispそのものの定義を明らかにしないと無理筋だと思いますので、そういう本を勉強しようと思います。 (追加質問で申し訳ありませんが、そういう勉強に適したLispの本のおすすめがあれば教えてください。) 重ね重ねありがとうございます。

その他の回答 (2)

  • myuki1232
  • ベストアンサー率57% (97/170)
回答No.3

単純に言えば、quote は渡された引数をすべて(あなたの言葉を使えば)「関数扱い」しないからです。 すなわち、 '('x) = (quote (quote x)) を評価すると単に (quote x) になります。 おそらくあなたは、普通の関数から類推して(例えば (+ (* 2 3) 1) = (+ 6 1) = 7 のように) quote の引数も評価されると思われたのでしょうが、 quote は関数ではなく特殊形式と呼ばれるもので、内側の関数を(当然、特殊形式も)評価しません。

TYWalker
質問者

お礼

こちらもありがとうございます。 関数と特殊形式ですね。理解しました。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.1

Emacs Lisp だとどう説明するんだったっけ.... 以下「普通の Lisp」の場合 (内容自体は Emacs Lisp でも同じですが, 表現は違うかもしれません): シングルクォートは「スペシャルフォーム quote の省略形」として読み込まれます. つまり 'x と入力するとあたかも (quote x) と入力したかのように読み込まれ, これが評価されることで x が得られます. で '(3 . '(1 . nil)) は (quote (3 . (quote (1 . nil)))) と書いたものとみなされますから, 評価すれば (3 . (quote (1 . nil))) すなわち (3 quote (1)) となります.

TYWalker
質問者

お礼

お礼ありがとうございますではなくて、回答ありがとうございますです。失礼しました!!! 本当にありがとうございます!!!!

TYWalker
質問者

補足

お礼ありがとうございます。 私が知りたいのは、 (1 . nil) ;;; Debugger entered--Lisp error: (invalid-function 1) ;;; carが関数扱いされてエラー '(1 . nil) (1) ;;; carが関数扱いされない<=当然 '(3 . (1 . nil)) (3 1) ;;; 内側のconsセルのcar(1)が関数扱いされない<=なぜ? のように、consセルのcdr側になるときはquoteしなくても、(の直後の要素が関数扱いされない理由です。 リストの場合も、 '(* 3 (+ 2 1)) (* 3 (+ 2 1)) (* 3 (+ 2 1)) 6 のように、先頭で一度クォートすれば、要素の中にリストがあっても第一要素を関数扱いしませんね。 この理由です。。

関連するQ&A

  • Emacs Lisp: 関数に引数が渡せない?

    お世話になります。 ----------------------------------------- ; -*- lisp-interaction -*- ; このファイルはtest.elisというファイル名で、そのファイルを開いたバッファで実行 (buffer-name) ;;; ミニバッファに以下のように表示される ;;; "test.elis" (buffer-name "test.elis") ;;; ★引数を与えるとそのバッファ名を返すはず ;;; エラーになる ;;; Debugger entered--Lisp error: (wrong-type-argument bufferp "test.elis") (get-buffer "test.elis") ;;; ミニバッファに以下のように表示される ;;; #<buffer test.elis> (buffer-file-name) ;;; ミニバッファに以下のように表示される ;;; "/home/userName/test.elis" (buffer-file-name "test.elis") ;;; ★引数を与えるとそのバッファのファイル名を返すはず ;;; エラーになる ;;; Debugger entered--Lisp error: (wrong-type-argument bufferp "test.elis") ----------------------------------------- Emacs Lispの勉強として、上のようなファイルを開いて、各関数の括弧綴じ)の後ろでC-x C-eとタイプして実行し、挙動を調べています。 ★をつけたbuffer-nameおよびbuffer-file-name関数は、それぞれ引数を渡したバッファ名、および、そのバッファのファイル名が返ると思われるのですが、実際には上記のようにエラーになります。 これはどうしてでしょうか。 Emacsは23.4.1を使っています。 よろしくお願いします。

  • Lispの問題

    lispを最近習い始めた初心者です。以下の問題【2】つが解けないので回答をお願いします。 【1】 ;;; CONS CL-USER(1): (cons 'a 'b) (A . B) ; ドッティドペア ;;; (1 2 3) ;;; (1 . 2) 関数 CONS は上に並べたある関数の特別な場合と同じと見ることができる それは何か? 以下の ◇ は何に相当するか? ;;; (cons a b) ≡ (◇ a b) 【2】関数 PRINC を利用し、リストをドッティドペアとしてプリントする関数を定義してみよ です。【2】の問題は (dotted-print '(1 2 3)) => (1 . (2 . (3 . NIL))) が例としてあるのですが、理解できません。 例についても解説していただけると助かります。 【1】,【2】単体の回答でもどうかご教授御願いします。 後lisp初心者にオススメなサイトなどあったら教えてください。

  • Emacs-Lispでの正規表現について

    例えば、以下のようなS式を評価した場合、"11" "12" "13"がヒットすると思います。 (search-forward "1[123]" nil t) しかし、私の環境では"11"や"12"ではなく"1[123]"にヒットしてしまいます。 [ ]や?等のメタキャラクターが機能していないようなのですが、どうすればメタキャラクターを有効化できるのでしょうか? ご教授宜しくお願い致します。 環境 OS:CentOS6.0 Emacs23.1.1 Lisp-interactionモードのscratchバッファに以下の様に書いて、C-jで評価しています。 (search-forward "1[123]" nil t) 12

  • format関数が使えません(Lisp)

    Emacs23でformat関数を評価してもエラーが出て使うことができません。*scratch*で評価してもloadしても同じです。 例えば (format t "ddd") で Debugger entered--Lisp error: (wrong-type-argument stringp t) format(t "ddd") eval((format t "ddd")) eval-last-sexp-1(t) eval-last-sexp(t) eval-print-last-sexp() call-interactively(eval-print-last-sexp nil nil) このようなエラーが出て実行できません。 一体どうしてwrong-type-augumentとでるのかさっぱりわかりません。どなたか知恵を貸してください。

  • リストを逆順にする関数(LISP)

    「初めての人のためのLISP[増補改訂版]」のP150のリストを逆順に関数がよくわかりません。 以下がその関数です。 (defun nreverse (x) (nrev2 x nil)) (defun nrev2 (x r) (cond ((null x) r) (t (nrev2 (cdr x) x) (rplacd x r) ))) xがnilになるまで再帰を繰り返し、((null x) r) で再帰を戻りますがなぜs (rplacd x r)でリストが逆順になるんでしょうか。例えばxを(a b c)とすると x:(a b c) r:nil ↓ x:(b c) r:(a b c) ↓ x:(c) r:(b c) ↓ x:nil r:(c) ↓ rplacd (c) (b c) ↓ rplacd (b c) (a b c) ↓ rplacd (a b c) nil となるのですが、これじゃあ全然リストは逆順になりませんよね。 誰が教えてください、お願いします。 ちなみにrplacd は第一引数のcdrを第二引数に変換する関数です。

  • LISPでatomの数を数える

    XLISPでlistの中のatomの数を数えたいんです。 下のようにlistの中のatomだけを抜き出してリストにすることはできました。 (DEFUN F1(L) (COND((NULL L) NIL) ((LISTP(CAR L))(F1(CDR L))) (T (CONS (CAR L)(F1(CDR L)))) ) ) このコードを実行すると次のようになります。 (F1 '((A B) C D (E F) G)) (C D G) 後はこれをlengthで数えるだけだと思うのですがそのやり方が分かりません。 それとももしかしてSETQで変数を設定して Tのところで値を1つずつ足していくのでしょうか?

  • Maximaのフーリエ変換について

    Maximaでフーリエ変換するために、 load(fft); で読み込んでから、 fft(exp(-t^2/18),t,w); を実行したところ、 Maxima encountered a Lisp error: Error in $FFT [or a callee]: $FFT [or a callee] requires less than three arguments. Automatically continuing. To reenable the Lisp debugger set *debugger-hook* to nil. というエラーが出てきて計算が出来ません。 どうしたら計算できるんでしょうか? わかる方教えてください。

  • emacsでサブディレクトリもload-path に追加する方法

    Meadow3 で使っていた拡張を Linux の emacs-22.3 で使えるものは流用 しようとして、失敗しました。 Meadow 側の site-lisp 以下を Linux のホームディレクトリに .lisp/myelisp と いうフォルダをつくりました。 そして、.emacs に、(setq load-path (cons "~/.lisp/myelisp/" load-path)) と書きました。 ~/.lisp/myelisp/ には、Meadow にあった、subdirs.el があるのですが、 サブディレクトリをロードパスとして認識できずに、emacs 起動時に、 サブディレクトリ以下にある .el ファイルを読み込めないというエラーが 発生します。 M-x describe-variable RET load-path RET でロードパスを確認すると、 load-path is a variable defined in `C source code'.Its value is ("~/.lisp/myelisp/" "/usr/share/emacs/22.3/site-lisp" "/usr/share/emacs/site-lisp" "/usr/share/emacs/site-lisp/egg" "/usr/share/emacs/site-lisp/site-gentoo.d" "/usr/share/emacs/site-lisp/tamago" "/usr/share/emacs/site-lisp/egg/egg" 以下省略 と表示されます。 "/usr/share/emacs/22.3/site-lisp" に Medow のsite-lisp 以下を置くとう まく動くのですが、ログインユーザのホームディレクトリに、2階層以上の 階層構造のあるemacsの拡張ファイルを置くにはどうしたらよいのでしょう か。

  • Lisp の問題がわかりません

    次の問題のプログラムを教えてください。 《多項式のかけ算》 1つの変数を含む線形多項式のかけ算を行なう関数 poly-times を定義する. 多項式は例えば 2x^4 - x^3 + x^2 + x-1 → (+ (* 2 X X X X) (* -1 X X X) (* X X) X -1) 2x4 - x3 + x2 + x-1 → (+ (* 2 X X X X) (* -1 X X X) (* X X) X -1) のように LISP の式で表現するものとする. すなわち, 第1要素が " +", 第2要素以降が項であるようなリストである. 演算子は +, * のみとし, -, / は使わない. 変数には X というシンボルを用いる. 項は, 整数, シンボル X, あるいは, 第1要素が "*" であるリストのいずれかであり, リストの場合, 第2要素に整数が入り第3要素以降は X, あるいは, 第2要素以降が X であるものとする. (実行例) >(poly-times '(+ X 2) '(+ X -2)) (+ (* X X) -4) >(poly-times '(+ (* 2 X X) X 5) '(+ (* X X) (* -5 X) 3)) (+ (* 2 X X X X) (* -9 X X X) (* 6 X X) (* -22 X) 15) 各項をかけ合わせてそれらの和をとればよいので, 2つの項をかける関数とか, 多項式と項を足す関数などを作って組み合わせれば できそうです. たとえば, (defun poly-times (x y &aux z) (dotimes (i (length (cdr x))) (dotimes (j (length (cdr y))) (setf z (embed-term (term-times (nth i (cdr x)) (nth j (cdr y))) z)))) (cons '+ z)) などと定義しておいて, 項同士のかけ算を実行する関数 term-times と, 1つの項を多項式に加える関数 embed-term をうまく定義してやれば 完成です. 自分でも試してみましたが、場合分けなどが多く、解けませんでした。 わかる方の御協力をお願いいたします。

  • Maximaでグラフを描くために

    http://www1.bbiq.jp/kougaku/maxima.htmlのサイトにしたがって、file:///C:/Documents%20and%20Settings/サムソン/Local%20Settings/Temporary%20Internet20Files/Content.IE5/2TC7QXU5/maxima_plot3d_graph%5B1%5D.png (z=√(x2+y2))のグラフを描こうとしたのですが、wxMaximaでplot3d(sqrt(x^2+y^2),[x,-100,100],[y,-100,100],[grid,20,20]);と入力すると ____ Maxima encountered a Lisp error: Error in PROGN [or a callee]: Cannot create the file C:/Documents and Settings/サム・ン/maxout.gnuplot.Automatically continuing.To reenable the Lisp debugger set *debugger-hook* to nil. ____ に出てきて、出力できませんでした。 ファイル名の途中にある「サム・ン」がいけないのかと思い、コマンドプロンプトをつたって「samson」に使用者名を変更して再度試してみたのですがやっぱりダメでした。 どなたかお教え下されば幸いです。