lisp の説明をする簡単なお仕事です

バイト先にコンピュータ関係の学科の大学生がいて、何を間違ったか lisp の勉強をし始めたらしく(プライベートで)、バイト中に lisp の質問をしてくる。

「2番目の要素」?

なにやら lisp の本を読んでるらしくて、例題にあった「与えられたリストの2番目の要素を返す関数を作れ」みたいなのを教えようとしたのだけど、説明してるうちに「2番目の要素」とはなんなのかわからなくなった。

そっこーで思いついたのが

(defun 2nd (list) (second list))

なのだけど、これじゃ元も子も無いので封印。

なんかまだ知ってる関数が '(define lambda car cdr cons) くらいだとか言い出したので、defun と define の違いは無視しつつ、car と cdr で2番目の要素を得るべくコンスセルの説明をしてみた。

(a b c d)

という list は

(a . (b . (c . (d . nil))))

という構造だというとこまでは(どうやら)理解してもらえたのだけど、ここで俺が混乱し始める。というのは list = 入れ子になったコンスセル と考えると、さっきの (a b c d) という list は (a . (b . (c . (d . nil)))) というコンスセルで、この (a . ...) つーコンスセルの2番目の要素というのは ... の部分= (b . (c . (d . nil))) で、つまり 2nd の定義はこうなる。

(defun 2nd (list) (cdr list))

な、なんだってー

でも常識的に考えて list (a b c d) の2番目の要素は symbol b だろ。

(defun 2nd (list) (car (cdr list)))

lisp の式は順番が逆

気を取り直して「与えられた list の cdr の car が2番目の要素なんで、それを返す関数を書けばおk」と言ったら、彼はこんな式を書いた。

(cdr (car list))

しむらー、ぎゃk

それ見て思ったのが、ふつーの言語というか C 風(?)の言語だと「この list の cdr の car」というのは

myList.cdr.car

みたいになって、ドットを「の」に置換すれば 'mylistのcdrのcar' となんともナチュラルに読める。lisp は逆から読まないといけない。

(cdr (car my-list))
         ;1. my-list の
    ;2. car の
;3. cdr

英語だと "cdr of car of my-list" でむしろナチュラルなのか?

else ってなんて読むの

あと、lisp と言えば再帰だろってんで再帰の説明をしようと

(defun fact (x)
  (if (= x 1)
      1
    (+ x (fact (- x 1)))))

みたいなコードを書いたら「とりあえずその if から説明してください」というので、そっからかよ!と心の中で突っ込みつつ (if CONDITION THEN-FORM ELSE-FORM) とか書いて説明してあげたら、「イルスはいらないんですか?」とおっしゃる。

俺「イルス?」
大学生「イルス」
俺「・・・イルス?」
大学生「イルスw」
俺「・・・・・(!)エルス?」
大学生「エルス!?」

学校で他の言語を教授が教えるときに「イルス」って言ってるんだろうか。

それより、もっと読めないのが cdr。「くだー」って読んでるけど、cdar とどうやって区別しろと。

xyzzy を薦めておいたら

「何に使うのかわからなかったんで五目並べやってました」

#n# とか #n=ナントカ とか

ちゃんとした説明を読んだことが無いので間違ってるかもしれない。この記法(?)のちゃんとした名前を、そんなものあるのかどうかも知らないけど、知らないのでどう調べていいのかわからない。"common-lisp #n#" とかでググっても # が無視されちゃうみたいだし。

n は任意の数字、10桁までやってみたけど使えるみたい。#n= と書いておくと、それ以降のコードで #n# と書いてある部分が に置換される。 が評価された結果ではなくて式そのもののままのカタチで出現する(のと同等)。置換されるのは macro 展開より前っぽい感じ(展開後でも同じ結果になるだけかも)。

(let* ((a 3)
       (#1=b (incf a)))
  (+ a #1#))
=> 8

(let* ((a 3)
       (b #1=(incf a)))
  (+ a #1#))
=> 9

上の場合は (+ a b)、下の場合は (+ a (incf a)) なので(というか、動作を見てる限りそれと同等の挙動をするので)違う結果になる。

追記: 単に source-text の置換では説明のつかない挙動を思い出した

(eq '(a b c) '(a b c))
=> nil

(eq #1='(a b c) #1#)
=> t

うーん。なんとなく理解はできるんだけど説明はできそうにない。