Common Lispでは、コンピューターに無視される「コメント」を書くことができます。1行コメントは1つ以上のセミコロン(;)で始まります。ときには、次のような書き方も見かけます:
(code...) ; => value
これは、その行のコードを実行したあとにCommon Lispが返す値をコメントで示しているものです。
行末の短いコメントにはセミコロンを1つ、コードのまとまりの上に置くやや長いコメントには2つ、関数などの説明を書く長いコメントには3つ、ソースファイルの先頭に置くヘッダーのようなコメントには4つ使うのが慣習です。
Common Lispのすべてのオブジェクトは、「アトム」(単一で分割できない値)か「コンス」(構成する関数consによって作られる)のどちらかです。
コンスは、最初の要素と残りの要素という2つの部分からできています。
歴史的な理由から、この2つの部分はcarとcdrと呼ばれています。
こうしたコンスがコードとして評価されるとき、最初の要素(car)が呼び出される関数を表し、残りの要素(cdr)がその関数への引数を表します:
(<function> <arg1> <arg2> ... <argN>)
; ^ car ^ | ^ cdr ^
Common LispのコードはすべてS式(Symbolic Expression)でできています。これは略してsexprと呼ばれます。すべてのsexprは、アトムかコンスのどちらかです。S式は評価されると、自動的に何らかの値を返し、その値が式の代わりになります。defunを使って自分の関数を書くときは、defunの本体の最後の値が自動的に返されます:
;; Defining a new function
(defun gimme-foo () 'foo)
;; Calling the function as an S-Expression
(gimme-foo) ; => FOO
Common Lispにおけるシンボルは特別な値で、他の値を指すこともあれば、_キーワード_の場合は自分自身を指すこともあります。 シンボルがLispによって評価されると、そのシンボルが指す値に置き換えられます:
foo ; => <whatever-foo-points-to>
:foo ; => :FOO
キーワードは先頭のコロン(:)で表されることに注意してください。
クォートとは、S式の前に'を付けることです。これにより、Lispにその式を評価しないように伝えます。
defunの例で'fooをクォートしたことで、LispがFOOの指す先を探そうとする(そして見つけられない)のを避け、代わりにFOOという値そのものを返しました。
FOOがプログラムのどこにも定義されていなければ、次のようになります:
foo ; => <ERROR! Lisp doesn't know what foo points to!>
'foo ; => FOO
今のところは、関数からシンボルを返す方法だと考えておけば大丈夫です。クォートについては、今後のコンセプト演習で改めて取り上げ、その意味をさらに掘り下げていきます。
そこの君! そう、勇敢なLisp使いの君です! Lisp宇宙人のLennyが助けを必要としています! LennyはほかのLisp宇宙人たちと遊びに出かけたがっていますが、両親から、部屋をきれいにするまでどこにも行かせないと言われています。
困ったことに、Lennyには床がほとんど見えません! アトムとコンスの区別も、シンボルとキーワードの区別も、carとcdrの区別もつきません!
Lennyがこの散らかった部屋を片付けるのを手伝ってあげませんか? 括弧を忘れないでくださいね。必要になりますから!
Lennyが始める前に、シンボルとキーワードがどんなものかをもう一度おさらいする必要があります。最後に片付けてからずいぶん時間がたっていますからね…。2つの関数を定義して、Lennyを手伝ってあげましょう。lennys-favorite-foodは何らかのシンボルに評価され、lennys-secret-keywordは何らかのキーワードに評価されます。たとえば次のようになります。
(lennys-favorite-food) ; => LASAGNA
(lennys-secret-keyword) ; => :ALIENS-ARE-REAL
好きなシンボルやキーワードを返してかまいません(ラザニアが好きでなかったり、宇宙人の存在を信じていなかったりしても大丈夫です)。
シンボルとキーワードのおさらいが済んだら、いよいよ掃除の時間です! 部屋のあちこちに散らばったS式を仕分けるために、Lennyはどれがコンスでどれがアトムなのかを見極める助けを必要としています。2つの関数、is-an-atom-pとis-a-cons-pを実装して手伝ってあげましょう。
(is-an-atom-p 'one-thing) ; => T
(is-an-atom-p '(two things)) ; => NIL
(is-a-cons-p 'one-thing) ; => NIL
(is-a-cons-p '(two things)) ; => T
このセクションでは、役に立つかもしれないLispの組み込み関数がいくつかあります。また、今のところは、Tを真、NILを偽と単純に考えておけば大丈夫です。
最後に、Lennyのコンスはかさばりすぎてきちんと片付けられないので、小さな部品に分ける必要があります。コンスを分解するための2つの関数(first-thingとrest-of-it)を定義して、手伝ってあげませんか?
(first-thing '(first second third)) ; => FIRST
(rest-of-it '(first second third)) ; => (SECOND THIRD)
ここでも、Common Lispの組み込み関数がいくつか役に立つでしょう。
Exercismに登録すれば、27個のコンセプト92個の演習、そして本物の人間によるメンタリングとともに、Common Lispを学んでマスターできます。すべて無料です。