プログラミング演習


II

この演習では,nx-lispというEWS4800 上のLisp を使います.マニュアルに
よれば,配列のごく一部(この演習では扱わない)が
Common Lisp と違うだけと
書いてありますが,それ以外にもいくつか違いがあります.違いについてはその都
度説明します.

1.Lispの入門


(テキスト1.11.3

Lisp はカッコだらけでよく分からない,とかいわれます.そこで,まずはLisp
の最初ということで括弧()に慣れることにします.右括弧と左括弧の対応に注意
してください.扱うのは数字だけにします.最初は
Lisp を電卓に代わりに使って
みましょう.数字相手なので
Lisp の関数(プログラミング)と数学の関数の対応
が分かりやすいと思います.


Lisp を立ち上げる前の準備
1)Lisp 用のディレクトリを作ります.
~/Lisp(作業用ディレクトリ)
~/Lisp/Report(レポート提出用ディレクトリ)
2)~/Lisp にディレクトリを移動してください.
Lisp を起動したディレクトリが,Lisp のワーキングディレクトリに
なります.


Lispの使い方
起動方法は
nxlisp と入力します.
終了方法は
(quit) と入力します.


以下の例では「▼」でリターンキーを表しています.
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
satya03% nxlisp▼(起動方法)
NX-LISP REV 020
Copyright (C) NEC Corporation 1990 1991 1992 1993

USER>

USER>(quit)(終了方法)
satya03%
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


それでは四則演算をやってみましょう
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>(+ 1 2)

3
USER>(- 10 2)

8
USER>(* 3 2)

6
USER>(/ 8 2)

- 1 -


4
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


Lisp では関数「+, -, *, /,...」を最初に,その後に引き数(パラメータ)
を並べ,括弧でくくります.つまり,
(関数n1n2...) です.

上の説明でも分かるように,2個以上の数でも計算できます.また1個の数で
も計算できます.
(* 1 2 3 4 5)
(- 6)
なども実行してみてください.
2個以上の数のとき,引き算「
-」と割り算「/」はどういう計算をしていますか?
考えてください.


平方根もあります.いろいろな数で試してみましょう.
(sqrt 2)

括弧を使って,組み合わせることができます.
(sqrt (+ (* 3 3) (* 4 4)))(ピタゴラスの定理,ベクトルのノルム)


ここまでうまくいきましたか?思い通りにならなかったり,エラーが出た人も
いると思います.そのときの対応の仕方をまとめておきます.


閉じ括弧「)」が対応しないのに「リターン」を入力したら,足りない括弧を
補ってください.カーソルは自動的に対応済みの括弧の所まで進んでくれます.
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>(sqrt (+ (* 3 3) (* 4 4))
)
↑カーソルがここまで来ている
5.0
USER>(sqrt (+ (* 3 3) (* 4 4)
))

5.0
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


括弧や,スペースの位置,記号の順番を間違えたりするとエラーが出力されま
す.試しにやってみましょう.エラーの状態から抜けるには
1」を入力します.
(皆さんのウィンドウではエラーは日本語で出ます.)
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>(+2 4)+2の間にスペースがない)

Error: 20118 '2' is illegal function.

To continue, type an option number:
0: Call DEBUGGER.
1: Return to toplevel.
Number : 1(「1 」を入力)
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


また,括弧が多すぎるときは計算はうまくいって答えが出力されますが,「
USER>」のプロンプトの次に「Warning」がでます.その後に「USER>」は出ませ
んが,入力待ちの状態です.構わずに次の入力をしてください.

- 2 -


┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>(+ 1 2 3 4))(右括弧が多い)

10
USER>
Warning: 02266 Extra RIGHT parenthesis appeared.
(* 2 3)(構わず入力)

6
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


いろいろエラーが出る例です.試してください.
(*6 8)
(10 9)
(1 + 2)
1 + 2
+ 3 4
12/8

〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
Common Lisp nx-lisp の違い
Common Lisp では,
◎分数も扱えます.たとえば,
(/ 3 6) -> 1/2
(/ 3 6.0) -> 0.5
(+ 1/2 1/3) -> 5/6
となります.
◎平方根は負の数に対しても定義してあります.たとえば,
(sqrt -2) -> #c(0.0 1.4142135623730951)
で,ちゃんと複素数になります.
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜


<練習>===========================
Lisp で計算してみましょう
(12 + 34) * 56/7.0
2 * 3.1415 * 3
2 * pi * 3
(実は「pi」という定数がある)
12, 3, 4.5, -6/7.0, 89 の平均(答えは21.5285....


2次方程式2x2+ 7x + 5 = 0 の解の値を求めてください.(2次方程式の解
の公式は覚えていますね.2乗
^」は使えません.「*」を使うこと.)
==============================

2.関数の定義


(テキスト2.12.4

さて,ベクトルのノルム(大きさ)をいくつか計算するとします.
ベクトル
(2,3) ----> (sqrt (+ (* 2 2) (* 3 3)))
ベクトル(3,4) ----> (sqrt (+ (* 3 3) (* 4 4)))

これではたいへんなので,数学でやるように
norm という関数norm(x,y) =

IMAGE image/pro-pri101.gif

- 3 -


とできたらいいわけです.


そこで,自分の関数を定義してみましょう.括弧の対応を間違えないようにして
ください.間違えたときは,もう一度同じ関数名で正しく入力してください.
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>(defun norm (x y) (sqrt (+ (* x x) (* y y))))

NORM
USER>(norm 1 2)
2.236068
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

書き方は
(defun 関数名(引き数) 関数本体)
となっています.
使い方は,
+ / と同じで(関数名

引き数) です.

norm の場合では,
引き数は,「
x yで,(「x y」のようなコンマはいりません)
関数本体は,
(sqrt (+ (* x x) (* y y))) で,1つ1つのベクトルのときの計算式の
(sqrt (+ (* 2 2) (* 3 3))) と対応しています.


適当な所でリターンを入力すると,分かりやすくなります.エラーの対処法で
説明したように,括弧が足りないと対応の取れている所までカーソルが進んでくれ
るので,それを利用して入力すると間違いが少なくなります.
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>USER>(defun norm (x y)
(sqrt (+ (* x x)
(* y y)
)
)
)

NORM
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


うまく入力できたかどうかのチェックの仕方
pp という関数を使うと入力され,記憶されている状態をきれいに出力してくれ
ます.ただし,この関数は
Common Lisp 共通のものでなく,nx-lisp にあるも
のです.また,「\」(ウィンドウの設定によって「
\」)が「+」「-」の前に付き
ますが気にしないでください.
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>USER>(pp norm)
(DEFUN NORM (X Y)
(SQRT (\+ (* X X)
(* Y Y))))
USER>
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


<練習>===========================
次の関数を定義してください.動作の確認もすること.

- 4 -


f1(x) = 1/x(逆数)(分数ではだめなので,x は実数で与える
ことにする.たとえば
. (f1 5.0)
f2(x) = x2(「*」を使う.)
f3(r) = 2*pi*r(円周)
f4(r) = pi*r2(円の面積)
f5(x,y) = (x + y)/2.0x,y の平均)
==============================


さて,上の練習で2乗の関数f2 を定義したので,これを使ってnorm をもう
一度作ってみましょう.新しく作る関数の名前は
norm2 とします.
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>(defun norm2 (x y) (sqrt (+ (f2 x) (f2 y))))

NORM2
USER>(norm2 1 2)
2.236068
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


Lisp では,このように複雑な関数(プログラム)を,小さく簡単な関数に分け,
それを組み合わせて作ります.

IMAGE image/pro-pri102.gif

このくらいの長さになるとLisp の中で直接入力して,一発でOK とはいかな
いと思います.そこでエディタ(
vi, ng,...)で編集してLisp に読み込む方法
を説明します.
まず,エディタ用にウインドウを1つ開いてください.そして,エディタで次の
ファイルを作ります.
ファイル名:
~/Lisp/tube.lisp
中味────────────
(defun tube (r h)
(* pi r r h))
└─────────────


次に,Lisp を起動してから,以下のようにします.
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>(load "tube.lisp")
;; Loading /groupB/denshi/t940999/Lisp/tube.lisp

T
USER>(tube 2 3)
37.69911184307752D0
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


(load "ファイル名") のファイル名は,違うディレクトリ(Lisp のワーキン
グディレクトリでない所)のファイルの場合はパス名を使って指定してください.

- 5 -


load のときに片方の「"」を忘れてたり,「" ダブルクオート」の所を「'
シングルクオート」を入れてしまってLisp がおかしくなったときは次のように「
")」を入力して普通のエラーの状態にして対応してください.
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>(load "Lisp/Report/report01.lisp)
")

Error: 22322
'#p"/groupA/staff/kobori/Lisp/Report/report01.lisp)
"' does not exist.

To continue, type an option number:
0: Call DEBUGGER.
1: Return to toplevel.
Number : 1

USER>
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


この演習でのLisp では,「エディタ」<-->(load...)」を繰り返してプ
ログラムを作っていきます.


〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
提出課題


確認の意味で次の課題を考えてください.
提出するファイル名は,
report??.lisp になっています.今までの例とは違っ
て関数名とファイル名が違います.また,ディレクトリにも注意してください.


01
次の関数poly2(a,b,c,x) を作ってください.係数が,a, b, c の2次関数
x のときの値を求めるもの.poly2(a,b,c,x) = a x2+ b x + c で,たと
えば,
(poly2 1 2 3 4) --> 27 となります.
ファイル名:
~/Lisp/Report/report01.lisp
中味────────────
(defun poly2 ...
...)
└─────────────


02
p.5の<問題>の2次方程式の解を求める関数のうち,rootplus を作ってくだ
さい.正しくできているかどうかは,答えを
01poly2 に入れて確かめてくださ
い.
ファイル名:
~/Lisp/Report/report02.lisp
中味────────────
(defun rootplus...
...)
└─────────────
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜

- 6 -


3.条件判断


if


(テキスト3.13.2, 3.4

まだ扱うのは数字だけにします.今までやったことだけでは複雑な関数を作るの
は難しいので,「
if」を使えるようにしましょう.その前に「真」「偽」を表した
り,判定したりする方法を説明します.
Lisp では
-> T
-> NIL
となっています.「偽」が「F」や「False」でないことに注意してください.


数の大小の判定や,等しいかどうかの判定は,「<」や「=」という関数を使いま
す.
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>(< 2 3)2 < 3 の真偽を判定)

T
USER>(= 2 3)

NIL
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


この他にも>, >=, <=, /=not equal; !=)があります.当然,not,
and, or
があります.
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>(not T)

NIL
USER>(not Nil)

T
USER>(and (= 1 1) (= 1 2))

NIL
USER>(or (< 1 2) (> 1 2))

T
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


and orは2個以上の引き数に対して真偽を計算できます.いろいろ試し
てください.(テキストの
3.4をよく読んでください.)


つぎは「if」です.(if については,テキストには書いてありませんが,C
などの
if と基本的には同じなので,分かりやすいと思います.)
使い方は,
(if 条件真のときの値偽のときの値) です.「偽のときの値」
を省略した
(if 条件真のときの値) も使えます.
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>(if (= 1 1) (+ 3 4) (* 5 6))

7
USER>(if (= 1 2) (+ 3 4) (* 5 6))

- 7 -


30
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


それでは絶対値を計算する関数absolute を作ってみましょう.エディタで次
のファイルを作り,実際に動かしてください.また,プログラムの内容も理解する
ようにしましょう.
ファイル名:
~/Lisp/absolute.lisp
中味────────────
(defun absolute (x)
(if (< x 0) (- x) x))
└─────────────


〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
提出課題


ファイル名とディレクトリに注意してください.また,必ず実行して結果が正し
いかどうか確認しましょう.


03
与えられた数字が負でなければ平方根を,負のときはそのままの数字を出力する
関数
sqrt-plus を作ってください.
ファイル名
~/Lisp/Report/report03.lisp
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>(sqrt-plus 2)

1.414214
USER>(sqrt-plus -2)

-2
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


04
3つの数を与えられたときに,1番大きい数を答える関数max3 を作ってくださ
い.
ファイル名
~/Lisp/Report/report04.lisp
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>(max3 4 6 3)

6
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


05
5つの数を与えられたときに,1番大きい数を答える関数max5 を作ってくださ
い.ただし,
if をたくさん使って作るとたいへなので,04max3 を利用してく
ださい.2乗の関数
f2 norm2 の関係を思い出してください.
ファイル名
~/Lisp/Report/report05.lisp
中味────────────
(defun max3 ...
)
(defun max5 ...
)
└─────────────┘
report05.lispをロードするだけでmax5 が動くようにmax3 も一緒にファイ

- 8 -


ルに入れておくことにします.


06
3つの数を与えられたときに,それらから直角3角形を作ることができるときに
T」,そうでないときに「NIL」を返す関数pythagoras を作ってください.
ファイル名
~/Lisp/Report/report06.lisp


07
3つの数を与えられたときに,それらを辺の長さとする3角形を作ることが
できるかどうかを判定する関数
triangle を作ってください.
ファイル名
~/Lisp/Report/report07.lisp
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>(triangle 3 4 5)

T
USER>(triangle 1 2 5)

NIL
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜

4.条件判断


cond


(テキスト3.3

引き続き,扱うのは数字だけにします.ここでは,Cのswitch に似ている「
cond」と,再帰的な関数について説明します.cond if をいくつも並べたよう
なものです.

IMAGE image/pro-pri103.gif

次の関数を作って,試してみましょう.
(defun onetwo (x)
(cond ((< x 0) 0)
((= x 1) 1)

- 9 -


((= x 2) 11)))
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>(onetwo 2)

11
USER>(onetwo -9)

0
USER>(onetwo 10)

NIL
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


最後の例のように, NIL が返ってきたりすると困る場合が多いので,多くの場合,
最後に
(t (.....)) のように必ず成立する条件「t」をもつ式を置きます.Cで
default に対応します.特に再帰的な関数のときには,これがないと永久に終
わらないことになる場合があるので注意が必要です.(
t T と同じ.大文字で
も小文字でも構いません.


次のように改良しましょう.
(defun onetwo (x)
(cond ((< x 0) 0)
((= x 1) 1)
((= x 2) 11)
(t 11111111)))
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>(onetwo 10)

11111111
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


再帰的な関数(テキスト7.1
再帰的な関数といえば,階乗
n! が有名ですが,Lisp で作ると,


if を使えば,
(defun fact (n)
(if (= n 0) 1
(* n (fact (- n 1)))))

cond を使えば,
(defun fact (n)
(cond ((= n 0) 1)
(t (* n (fact (- n 1))))))
となります.
当然ですが,必ず停止するような条件を忘れないようにしてください.


再帰的な関数を定義することは,Lisp だけでなく多くの言語において基本的な
プログラミングの技法となるので,テキストの7章を,理解できるまで何度でも読
むようにしましょう.


数に対する関数(テキストの各所にあるので,索引で探してください.)
zerop0 ならばT,そうでなければNIL
(= n 0)
(zerop n) と同じです.


evenp, oddp:偶奇の判定

- 10 -


1+, 1-1 増やしたり,1 減じたりする関数
(スペースは間になし.「
1 +」ではない.)
(1+ 3) -> 4, (1- 10) -> 9

〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
提出課題


以下では,すべて再帰的な関数を定義することになります.これらの課題により,
再帰的な関数の定義の方法について理解を深めるようにしてください.


08
自然数n に対して12+ 22+ 32+...+ n2を求める関数sigma2 を作って
[!]n
ください.つまり,sigma2(n) = i2を,公式を使わずに再帰的な考え方で作っ
i=1
てください.
ファイル名
~/Lisp/Report/report08.lisp


09
0 以上の整数の足し算をする関数sum(m, n) = m + n を作ってください.も
ちろんこれは普通の「
+」ですが,ここでは「+」をより原始的な関数「1+,1-」を
使って,再帰的な考え方で作ってください.
再帰的な「
+」の定義:
n, m 0 以上の整数として,
sum(0, n) = n
sum(m, n) = sum(m-1, n+1)
足し算を作るのですから,「+」を使ってはいけません.「1+」と「1-」を使っ
て作ってください.
ファイル名
~/Lisp/Report/report09.lisp


10
Fibonacci の数列を求める関数を作ってください.
定義:
Fibonacci(1) = Fibonacci(2) = 1
Fibonacci(n) = Fibonacci(n-1) + Fibonacci(n-2) for n>=3
Fibonacci
の数列は,1, 1, 2, 3, 5, 8, 13, 21,... となります.
ファイル名
~/Lisp/Report/report10.lisp


11
xnx n 乗)を求める関数power を作ってください.
(power x n)
x:real; n:positive integer or 0
(power 2 10) -> 1024
(power 2.5 4) -> 39.0625
ただし,次のような再帰的な考え方で作ってください.
[!] 1if n=0
[!]
xif n=1
x
n= [!] [!]
[!] x(n/2)*x(n/2)if n is even and n >1
[!] [!]
x*x((n-1)/2)*x((n-1)/2)if n is odd and n>1
ファイル名~/Lisp/Report/report11.lisp


(参考).............................
n 乗の再帰的な定義は普通,簡単に

- 11 -


xn= [!]
[!] 1if n=0
[!] x*x(n-1)if n>0
とします.この問題はわざと作ったものです.
.................................
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
研究課題(提出は不要)


上の課題をif [cond] で作った人はcond [if] で作ってみてください.

 

5.リストの操作


(テキスト1.41.5, 2.6, 3.2

今回からリストを扱いたいと思います.
リストは,
(a b c d)
(0 1 2 3 4 5 6 7 8)
(shiga kyoto osaka nara)
((ryukoku (kyoto seta)) (ritsumeikan (kyoto minami-kusatsu)))
など括弧で囲まれたものです.
リストに対する操作には,いろいろな関数があります.


length

(テキスト5.4

length は,リストの長さを計算します.( ) の中にいくつ要素があるかを計算
します.次の例の2番目のように,
((ryukoku ... minami-kusatsu)))
length 2 となります.
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>(length '(a b c d))(クオート「'」を忘れないように)

4
USER>(length '((ryukoku (kyoto seta))
(ritsumeikan (kyoto minami-kusatsu))))

2
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


リストの前にクオート「'」をつける理由
今までは,左括弧「(」の次には「
+」や「sqrt」や「and」のように関数がき
ていました.クオートがないと「
a」や「shiga」が関数だと思ってエラーがでます.
実際にクオートがないとどうなるか確認してみてください.


<練習>=================================
次のリストの長さを求めてください.(クオート「
'」を忘れないように)
(a (b c) d)
(a b c d)
( )
(alpha)
(alpha beta gamm)
((a list within a list))
(( ))
((((( )))))
(( ) ( ) ( ) ( ))
(a (b ((c) d)))
=====================================


空のリスト

- 12 -


上の練習で,( ) の長さを求めましたが,長さ0 のリスト(空のリスト)を
NIL で表します.つまり「空」=「偽」となります.ここでは見やすくするために
括弧の間に「スペース」を入れてありますが,本来必要ないものです.
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>( )

NIL
USER>(length NIL)

0
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


リストのイコールの判定
2つのリストが等しいかどうかの判定には「
equal」を使います.「=」は数に対
してだけ使います.
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>(equal '(a b) '(a b))

T
USER>(equal '(a b) '(a b (c d)))

NIL
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


リストの一部分を取り出す関数
first--> リストの1番目の要素を取り出す.
second --> リストの2番目の要素を取り出す.
third--> リストの3番目の要素を取り出す.
fourth, fifth, sixth, seventh, eighth, ninth, tenth もあります.
rest--> リストから1番目の要素を除いたリストを作る.
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>(first '(a b c d e))

A
USER>(rest '(a b c d e))

(B C D E)
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


car cdr
Lisp
では伝統的にfirstrest でなくcar cdr を使ってきました.ど
ちらを使ってもいいのですが,読んだときの分かりやすさで,ここでは,
first
rest を使うことにします.ただし,他の本などのプログラムを読むときのために,
car cdr の意味はよく覚えておいてください.


(first リスト)= (car リスト)
(second
リスト) = (car (cdr リスト)) = (cadr リスト)
(third
リスト)= (car (cdr (cdr リスト)) = (caddr リスト)
(rest
リスト)= (cdr リスト)


caddr などは関数を合成したものを,圧縮して表現してあります.
(car (cdr (cdrリスト) = (caddrリスト)

- 13 -


このような関数(表現)を使うと
場合もあります.

firstrest を使うより短く書くことができる

((a b) (c d))からfirst, rest を使ってd を取り出すには次のようにし
ます.
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>(rest '((a b) (c d)))

((C D))
USER>(first '((C D)))

(C D)
USER>(rest '(C D))

(D)
USER>(first '(D))

D
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


<練習>===========================
上の例のように,最初のリストに
firstrest をうまく使って,最後の値まで変
形してみてください.
((a b) (c d)) ---->...----->a
((a (b c) d ((e) f))) ---->...----->d
((a (b c) d ((e) f))) ---->...----->e
((a (b c) d ((e) f))) ---->...----->f
また,second, third,...も使ってみてください.
==============================


cons, list, append
こんどはリストを作る関数です.
cons:リストの先頭に1個要素を加えたリストを作ります.
list:与えられた要素のリストを作ります.
append:与えられたリストを合わせたリストを作ります.
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>(cons 'a '(b c d))

(A B C D)
USER>(list 'a 'b 'c)

(A B C)
USER>(append '(a b) '(c d e))

(A B C D E)
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


<練習>===========================
1)
cons で1つのリストにしなさい.つまり,最初のものを後のリストの先頭に
入れてください.
例)
a (b c) (cons 'a '(b c)) --> (a b c) とする.
a NILNIL = ( ) のことでしたね)
NIL NIL
- 14 -


(a b c) NIL
(a b)
(c d)


2)cons だけとNIL, a, b, c を部品として使って,次のリストを作ってく
ださい.
例)
(a b c) (cons 'a (cons 'b (cons 'c NIL))) で作れる.
(a (b c))
((a b) c)
(((a) b) (c))

3)list を使って,1つのリスト(つまり(.....) の格好)にしなさい.なお,
list の引き数は1個でも構いません.
例)
a, b --> (list 'a 'b)--> (a b)
a
(a)
a, NIL
a, (b c), d
NIL
(a b), (c d)

4)append で1つのリスト(つまり(.....) の格好)にしなさい.
(a b c) NIL
NIL
(a b c)
NIL
NIL
==============================


リストかどうかの判定
listp を使えば,リストかどうかの判定ができます.listp の最後の「p」は
predicate の「p」です.
atom で,リストでなくシンボル(記号)かどうか調べることができます.
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>(listp '(a b c))

T
USER>(atom '(a b c))

NIL
USER>(listp 'seta)

NIL
USER>(atom 'seta)

T
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


空のリストの判定「null endp
null は空のリストかどうかを判定します.
endp はリストが空かどうか判定します.
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>(null ( )){= (null nil)}

T
USER>(endp ( )){= (endp nil)}

- 15 -


T
USER>(null '(a b c))

NIL
USER>(endp '(a b c))

NIL
USER>(null 'seta)

NIL
USER>(endp 'seta)

Error: Can't take CDR of SETA.
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


最後の例で分かるように,リストでないときにこの2つの関数は反応が違います.
null はリストでないときも知らん顔をしてNIL を返してくるので,注意する必
要があります.


リストを扱う関数の作成
リストの最初の要素を置き変える関数
replace-first を作ります.目標とす
る関数は次のような動きをします.(当然,まだ作ってないので,入力しても動作
しません.)
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>(replace-first '(a b c) 'aaa)

(AAA B C)
USER>(replace-first '(a b c) '(d e))

((D E) B C)
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


考え方としては,最初の引き数のリストの「rest」に,2番目の引き数を「
cons」すればいいわけです.
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>(cons 'aaa (rest '(a b c)))

(AAA B C)
USER>(cons '(d e) (rest '(a b c)))

((D E) B C)
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


したがって,これを関数にすると,
(defun replace-first (l x)(「l」は「L」の小文字)
(cons x (rest l)))
となります.


関数本体の構造は,今までの数字を扱っていたときと同じように対応しています.
(cons 'aaa (rest '(a b c))
↓↑↓↑
(consx(restl))

- 16 -


関数のなかでは,「l」や「x」にクオート「'」は付いていないのに注意してく
ださい.「
'x」だと単なる文字の「x」になってしまいます.


再帰的な考え方を使えば,簡単にリストに対していろいろな関数を作ることがで
きます.
length と同じ働きをするmy-length
(defun my-length (l)
(cond ((endp l) 0)
(t (1+ (my-length (rest l))))))
となります.
やっていることは分かると思いますが,一応説明しておきます.
リスト
l が空なら長さは0
そうでなければ,リストl を最初と残りに分ければ,長さは「残りの長さ」+1


リストに対する再帰的な関数のパターンは,おおよそ,
1)終了条件(たとえば,
((endp l) 0)
2)リストの最初の要素に対して何かして,
(判定したり,再帰的に関数を使ったり)
3)リストの残りに対して何かする.
(判定したり,再帰的に関数を使ったり)
となります.
たとえば,数字のリストのなかに,
0 があるかどうかを判定する関数zero-in
は,
(defun zero-in (l)
(cond ((endp l) nil)
((zerop (first l)) t)
(t (zero-in (rest l)))))
とまったく上のパターンのとおりです.
先にも書きましたが,再帰的な関数はとても重要なので,テキストの7章をよく
読んで理解するようにしてください.


〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
提出課題


すべて今まで出てきた関数を組み合わせて作ることができます.組み込み関数を
自分で作るような問題もありますが,それらを使ったのでは練習になりませんので,
自分で考えてください.


12
次の関数pair-pair を作ってください.4入力で,2つずつリストにして,そ
れを合わせたリストを作る.
ファイル名
~/Lisp/Report/report12.lisp
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
USER>(pair-pair 'a 'b 'c 'd)

((A B) (C D))
USER>(pair-pair 'a '(a a) '(b b) 'b)

((A (A A)) ((B B) B))
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


13
リストの1番目と2番目の要素を入れ替える関数replace12 を作ってください.
ファイル名
~/Lisp/Report/report13.lisp
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

- 17 -


USER>(replace12 '(a b c))

(B A C)
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


14
(mid-add1 '(take 2 cookies)) -> (take 3 cookies) のように,3要素
のリストの真ん中の数を1つ増やす関数
mid-add1 を作ってください.入力する
リストは必ず3要素で中央は数字とします.
ファイル名
~/Lisp/Report/report14.lisp


15
リストの最後の要素を取り除いたリストを作る関数tail-cut を作ってくださ
い.
(tail-cut '(a b c d)) -> (a b c)
ファイル名~/Lisp/Report/report15.lisp


16
リストの最後の要素を取り出す関数tail-get を作ってください.
(tail-get '(a b c d)) -> d
ファイル名~/Lisp/Report/report16.lisp


17
リストの中味を1つずつずらす関数rotate-L, rotate-R を作ってください.
(rotate-L '(a b c d)) -> (B C D A)
(rotate-R '(a b c d)) -> (D A B C)
ファイル名~/Lisp/Report/report17.lisp


18
リストの要素のn 番目を取り出す関数my-nth を作ってください.
(my-nth 1 '(a b c d)) -> A
(my-nth 3 '(a b c d)) -> C
(my-nth 10 '(a b)) -> NIL
(my-nth 1 ()) -> NIL
ファイル名~/Lisp/Report/report18.lisp
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
研究課題


1)上の課題のtail-get, my-nth に対して,Lisp には最初から組み込みの
関数
last, nth があります.違いを確認してみてください.

- 18 -