ここで使っている言語の名前であるLispは、_LIST Processing_の略です。となれば、この言語には要素のリストを扱う仕組みが備わっていると考えるかもしれませんが、そのとおりです!
Common Lispにはリスト以外のデータ構造もありますが、リストは今でも非常によく使われています。
Common Lispにおけるリストは、要素の並びです。主に歴史的な理由から、consとも呼ばれます。
要素は同じ型である必要はありません。
たとえば、1、two、"III"からなるリストを作ることができます。
次のように、クォートしたリストをそのまま入力するだけでかまいません。'(1 two "III")と書くと、リストが作成されて評価されます(評価結果は(1 two "III")です)。
リストを作るための主な関数としては、listとconsの2つがあります。
listは0個以上の引数を取り、それらの値で作られたリストに評価されます:
(list 1 'two "III") ; => (1 two "III")
consは2つの要素を取り、1つ目の要素をcar(リストの先頭)、2つ目の要素をcdr(リストの残り)とするリストを作ります:
(cons 1 2) ; => (1 . 2) ;; (a list without `nil` as its `cdr` is printed in this way)
(cons 1 nil) ; => (1)
(cons 1 (cons 2 nil)) ; => (1 2)
carとcdrという関数を使うと、リストのcarとcdrにアクセスできます。
(firstとrestはcarとcdrの同義語で、まったく同じように動作します。)
リストの長さはlengthで調べることができます。
空のリストの長さは0です。
任意の要素にはnthでアクセスできます(リストのインデックスは0から始まることに注意してください)。
リストの長さより大きいインデックスを指定しても、エラーには_なりません_。
その場合はnilに評価されます:
(nth 23 '(short list)) ; => nil
リストの先頭のほうの要素にアクセスするための補助メソッドもいくつかあります。first、second、thirdと続き、tenthまであります。便利ですよね?
2つ以上のリストはappendで結合できます:
(append '(a b c) '(1 2 3)) ; => (A B C 1 2 3)
(append '(a b c) '()) ; => (A B C)
appendに渡す引数は、それぞれリストである必要があります。
Lisp星人のLeslieは、買い物に行く必要があります。買い物の配列を作ることはとても大切です。そこに物を追加したり、物を取り除いたりする必要があります。
もちろん、Lisp星人にとっては、紙とペンだけでは物足りません。「List」は「Lisp」という単語の大半を占めているほどです! 買い物を管理するには、いくつかの関数を書く必要があります。
Leslieが買い物の配列をうまく管理できるように、手伝ってあげませんか?
まず、Leslieは空の配列を作る必要があります。そのためにはnew-listという関数がぴったりです。
(new-list) ; => ()
あらら…Leslieにはすでにいくつか買いたいものがあるので、3つの品物を受け取り(幸いなことに、Leslieが作る配列はいつも3つの品物だけです。それ以上でもそれ以下でもありません!)、それらで新しい買い物の配列を作る関数が必要です。3つの品物を受け取り、それらの配列を返す関数list-of-thingsを書いてみましょう。
(list-of-things 'bread 'milk 'butter) ; => '(bread milk butter)
店に行く前に、Leslieは食料庫を覗いて何が必要かを確認します。配列の先頭に品物を追加する関数add-to-listを書いて、手伝ってあげましょう。
(add-to-list 'butter '(bread)) ; => '(butter bread)
買い物中、Leslieは次に何を探せばよいかを知りたくなります。また、配列を先読みして、2番目、3番目、あるいは23番目(Leslieのラッキーナンバー)の品物を確認するのも好きです。
first-thingは配列の最初の品物に評価されますsecond-thingは2番目の品物に評価されますthird-thingは3番目の品物に評価されますtwenty-third-thingは23番目の品物に評価されます(first-thing '(bread butter milk)) ; => 'bread
(second-thing '(bread butter milk)) ; => 'butter
(third-thing '(bread butter milk)) ; => 'milk
Leslieは配列の最初の品物を見つけて、それを棚から取り除きたいと思っています。入力の配列から最初の品物以外のすべてを含む配列に評価される関数remove-first-itemを書いて、手伝ってあげましょう。
(remove-first-item '(bread butter milk)) ; => '(butter milk)
Leslieは、うっかり1つではなく2つの買い物の配列を作ってしまったことに気づきました! 1つ目の配列の要素の後に、2つ目の配列の要素が続く配列を返す関数list-appendを書いてください。
(list-append '(bread salt) '(butter milk)) ; => '(bread salt butter milk)
Leslieは、この買い物が長引きそうで心配になってきました。この配列にはいったい何個の品物があるのでしょうか? Leslieの配列の長さを教えてくれる関数just-how-longを書いてください。
(just-how-long '(bread milk butter salt)) ; => 4
Exercismに登録すれば、27個のコンセプト92個の演習、そして本物の人間によるメンタリングとともに、Common Lispを学んでマスターできます。すべて無料です。