mjk LISPその1
(a b c)といったリスト(LISt)を基本とした処理言語(Processor)がLISPというコンピュータ言語です。
(関数 引数1 引数2)といった構造をとります。
因みにMATLABは行列(MATrix)研究室(LABoratory)の略です。
Mjk LISPはANSI common LISPとは互換性がありません。以前のCommon
LISP, the Language 2nd-editin(CLtL2)とも互換性がありません。たぶんCommon LISP初期のversionのどっかの方言をobject指向に独自に発展させた言語なんだと思います。
このサイトではSteel Blank Common LISP (SBCL)との比較を時々載せてみます。
Hello Worldと表示させます
(print “Hello World)
Graph termにも表示されます。
print関数の代わりにformat関数を使えば同様のことができます。
(format nil “Hello World”)
としても同じ結果になりますが、この時はGraph termに出力されません。
error関数を使うと新しいwindowに表示されます。
warn関数を使うとGraph termに出力されます。
help-on-version関数でGraphとLISPのversionが表示されます。
integer 32bit整数 -1, 0, 1, 2, …
float 32bit浮動小数点 -1.0, 1.0e+3, pi
character 文字 #\文字として表します。#\a, #\A, #\0
symbol 記号 a abc tなど setq、letやdefvarなどで予め定義する必要があります。mjk LISPには|〇 〇|といった|と|で空白を使用可能にする機能はありません。(quote
記号)の省略形である'記号は記号として認識されます。
simple-vector ベクトル・・・mjk LISPでは文字列です。“文字文字・・・”として表します。“a”, “abc”。
function 関数 (function 関数名), #’関数名のように表します。(function
* ) #’setq
matrix 行列 (matrix ‘((1 2 3))), #m((1 2)(3 4))のように表します。2次元にしか対応していません。
streamストリーム・・・ファイルの入出力のときに使用します。
packageパッケージ・・・別途定義した定数や関数などをまとめたもの
G-widget・・・信号処理のオブジェクト。コントロールパネルではアイコンで表示されている。mjk
LISPのウリ。
X-widget・・・信号処理のオブジェクト。G-widget以外のオブジェクト。
X-window・・・mjk LISPでGUIを使うための基礎となるオブジェクトらしい。
その他・・・other type〇〇と表示される
決まり事
「’」は引用を示すquote関数の省略形として用います。
「`」のbackquote機能はありません。が使わないようにしてください。
「,」のcomma, unquote機能もありません。使わないようにしてください。
「”」は文字列を定義するときに使います。
「;」semi colonは注釈文を書くときに使います。
「:」:〇〇は関数でkeyを指定するときに使います。
スペース「 」、は基本的に使わないこと。mjk
LISPでは|〇空白〇|という記号は|〇と〇|として認識されます。
記号用には「-」を使うこと。「_」は推奨されません。
「.」は浮動小数点用にのみ用います。
大文字と小文字は区別されます。
*名前*は大域変数に用います。
リストはカッコで型を囲った構文
(型
型 型・・・)
になります。因みに型の事をアトムatomといいます。
整数は何もなければ10進数、#bで始まれば2進数、#oだと8進数、#xだと16進数となります。結果は10進数で表示されます。
17
17
#b10001
17
#o21
17
#x11
17
2,8,16進数表記は負の整数は扱えません。ゼロとなります。
#x-11
0
浮動小数点はドット「.」をつけるかeをつけます。32bit浮動小数点のみです。
1.
1.0
1e0
1.0
1e-2
0.01
ANSIのCommon LISPと異なりsやdをつけて32/64bit浮動小数点を明示することはできません。
1.0d
円周率πはpiと表されます。
pi
3.1415926
ANSIのCommn LISPと異なりmjk LISPでは分数は使えません
1/4
Steel Blank Common Lisp (SBCL)ではつかえます。
* 1/4
1/4
記号に #と空白は使えません。’ " ( )は\と一緒であれば使用可能です。a〜z、A〜Z、& ! % [ ]
{ } + -* /が最低1文字必要です。それらを含んだ上なら0〜9 + - / * < >が使えます。
記号として使えるかどうかはsymbolp関数を使います。使える場合はtと表示されます。「’」で始まれば原則全部記号として認識されますが、一部例外もあります。
(symbolp ‘+-=)
t
(symbolp ‘@![)
t
(symbolp ‘\’\’”)
t
(symbolp ‘#a)
警告音が出てそのままです。#で始まるのはダメとされました。
Steel Blank Common Lispでは|で挟むことで空白が使えますがmjk
LISPでは使えません。
(symbolp '|a b|)
* (symbolp '|a b|)
T
symbolp関数は本来は記号かどうかをみます。
(setq a ‘abc)
abc
(symbolp a)
t
文字列として使えるかどうかはstringp関数を使います。使える場合はtと表示されます。"は単独では使用できず\"で表現されます。
(stringp “\”” )
t
(stringp “””)
\”の形でないので実行されません。
データの型を調べるのはtype-of関数を使います。
(type-of データ)という構文になります。
(type-of 1)
integer
文字は#\aという構文になります。文字コードが日本語仕様なので\となっていますが、欧米語だとバックスラッシュ(\の半角)です。
(type-of #\a)
character
円周率π、piの型を調べます
(type-of pi)
float
32bitの浮動小数点です。64bitの浮動小数点は定義されていません。
記号aの型を調べます。
(type-of a)
これはエラーになります。aを記号として使うときはquote関数を使います。(quote a)、または省略形として'aとします。Returnキーを押したとき、aのみが表示されます。
(quote a)
a
'a
a
記号aの型を調べる正解は以下となります。
(type-of ‘a) ;記号
symbol
文字列の型を調べます。文字列は"文字"で囲まれた構文となります。
(type-of "a")
simple-vector
(type-of “Hello World”)
simple-vector
simple-vectorと表示されますが、mjk LISPではcharacterのsimple vector、即ち文字列を意味します。
ANSI Common LISPでは他に整数や小数のsimple vectorもあります。
Steel Blank Common Lispでは以下の通りです。
* (type-of "Hello World")
(SIMPLE-ARRAY CHARACTER (11))
関数の型を調べます。足し算の関数+を調べてみます。
(type-of +)
エラーとなります。
関数の型を調べるには(function 関数名)か#'関数名の構文をとります。
(type-of (function +))
function
(type-of #’+)
function
#がないとsymbolと表示されます。
(type-of (quote +))
symbol
(type-of '+)
symbol
リストは(型 型 型)という構文です。
(type-of (1 2 3))
これはエラーになります。正しくは(list 型 型 型)か省略形の'(型 型 型)とします。
(type-of (list 1 2 3))
cons
(type-of (quote (1 2 3)))
cons
(type-of '(1 2 3))
cons
リストと表示されてほしいのですが、consと表示されます。consはconstruct
cell、略してコンスセルcons cellとあらわします。リストはコンスセルを繋げたものなので、これで正解です。コンスセルについては後述しますので、ここでは深入りしません。
なお、backquote「`」はmjk LISPでは使えません。
'(1 2 3)
(1 2 3)
`(1 2 3)
Steel Blank Common Lispでは以下の通りです。
* '(1 2 3)
(1 2 3)
* `(1 2 3)
(1 2 3)
行列は(matrix リストのリスト)か省略形の#m((…)(…))と表します。
行列は(matrix (list (list 1 2 3)(list 4 5 6)))、面倒なのでquote関数を使って(matrix
(quote ((1 2 3)(4 5 6))))、quote関数を省略して (matrix '((1 2 3)(4 5 6)))、さらに省略して#m((1 2 3)(4 5 6))と表します。
(matrix ‘((1 2 3)(4 5 6)))
#<2x3 matrix>
初期状態ではコンソールには中身は表示されません。中身を見るには以下のようにします。
(setq *print-array* t)
t
(matrix ‘((1 2 3)(4 5 6)))
#m( ( 1.0 2.0 3.0 ) ( 4.0 5.0
6.0 ))
#m((1 2 3)(4 5 6))
#m( ( 1.0 2.0 3.0 ) ( 4.0 5.0 6.0 ))
*print-array*をtにすることで初期設定では50行、12列まで表示できます。それ以上は..となります。
mjk LISPでは行列のとりうる型は二次元配列の32bit浮動小数点となります。
尚、上記では改行部分が半角スペースとなっています。テキストファイルなどにコピーすると
#m(
( 1.0 2.0 3.0 )
( 4.0 5.0 6.0 ))
となります。で行列の型を見てみます。
(type-of #m((1 2 3)(4 5 6)))
matrix
(type-of (matrix ‘((1 2 3)(4 5 6))))
matrix
(type-of #m((1 2 3)(4 5 6)))
matrix
行列の中身を見ない初期状態に戻します。
(setq *print-array* nil)
nil
Steel Blank Common Lispでは以下の通りです。
* #2A((1 2 3)(4 5 6))
#2A((1 2 3) (4 5 6))
* (type-of #2A((1 2 3)(4 5 6)))
(SIMPLE-ARRAY T (2 3))
Steel Blank Common Lispは1次、3次の配列も作れますが、mjk LISPでは2次限定です。
* (type-of #1A(1 2 3))
(SIMPLE-VECTOR 3)
* (type-of #3A(((1 2 3))((1 2 3))))
(SIMPLE-ARRAY T (2 1 3))
真偽の真t、偽nilの型を見てみます。
(type-of t)
symbol
(type-of nil)
cons
mjk LISPではtはsymbol、nilはconsとして扱われます。ANSI Common
LISPではそれぞれboolean、nullとして扱われます。
Steel Blank Common Lispでは以下の通りです。
* (type-of t)
BOOLEAN
* (type-of nil)
NULL
type-of関数を使って、その他の型を見てみます。
(type-of *standard-output*);ストリーム
stream
(type-of *package*);パッケージ
package
(type-of *application-shell*)
X-widget
(type-of (G-widget “display”))
plotter
(type-of (G-widget “file”))
diskfile
typep関数を使うと、真ならt、偽ならnilと表示されます。(typep 何か '型)という構文をとります。
1.0が数字かどうか、整数かどうか、浮動小数点かどうか、記号かどうか調べてみます。
(typep 1.0 'number)
t
(typep 1.0 'integer)
nil
(typep 1.0 'float)
t
(typep 1.0 'symbol)
nil
記号aが数字かどうか、文字かどうか、文字列かどうか、記号かどうか調べてみます。
(typep ‘a ‘number)
nil
(typep ‘a ‘character)
nil
(typep ‘a ‘string)
nil
(typep ‘a 'symbol)
t
関数+が記号かどうか、関数かどうか調べてみます。
(typep #'+ 'symbol)
nil
(typep #'+ 'function)
t
(typep #'setf ‘function)
定義されてない記号や関数だとエラーが出ます。
〇〇pという関数も用意されています。(〇〇p 何か)という構文をとります。
(integerp 1)
t
(characterp #\a)
t
(stringp "a")
t
(functionp #’cons)
t
(functionp (function tan))
t
(G-widget-p (G-widget “file”))
t
〇〇p関数には以下のものがあります。
数字かどうかnumberp、整数かどうかintegerp、浮動小数点かどうかfloatp、文字かどうかcharacterp、文字列かどうかstringp、関数かどうかfunctionp、ストリームかどうかstreamp、リストかどうかlistp、コンスセルかどうかconsp、リストの構成要素のアトムatomかどうかatomp、行列かどうかmatrixp、パッケージかどうかpackagep。
nilかどうかはendpまたはnullを使います。
(〇〇p 何か)といった構文をとります。
また(〇〇p 数字 数字 数字・・・)といった構文をとる関数もあります。全部真のときtをそうでないときはnilを返します。
ゼロかどうか
(zerop 1.0 1)
t
奇数かどうか
(oddp 1 3 5)
t
偶数かどうか
(evenp 2 4 6)
t
リストはCONStruct CELL、略してコンスセルcons cellで構成されています。
コンスセルは
Contents
of the Address part of the Register、略してカーcarと
Contents
of the Decrement part of the Register、略してクダーcdr
という2つのメモリのアドレス(C言語のポインタ)から構成されています。
carが最初のアドレス、cdrが次のアドレスです。
cons関数はコンスセルを作成します。
(cons 1 nil)
(1)
の場合、carのアドレスに1が、cdrのアドレスはnilが格納されています。
(list 1)は(cons 1 nil)となります。
(list 1)
(1)
cdrが2の場合をみてみます。
(cons 1 2)
(1 . 2)
1と2の間にドット.があります。
このようにドットがあるリストをドットリストやドットペアというそうです。なおmjk LISPはドット「.」はnilとして認識されるようです。
(cons 1 .)
(1)
(list 1 . 2)
(1 nil 2)
プログラムミスのもとになりそうなので使わないほうが無難です。
またカンマ「,」も使わないでください。カンマ「,」をつけるとsystem::unquoteといったLISPで評価できない特殊な値になります。
(list 1,2)
(system::unquote 2)
リスト(1 2)をみてみます。
(list 1 2)
(1 2)
左と右のコンスセルが連結され、左のコンスセルのcdrには右のコンスセルのcarのアドレスが格納されています。ドットリスト(1 . 2)とリスト(1 2)が別物であることが分かります。
cons関数を使ってリスト(1 2)を作成してみます。
(cons 1 (cons 2 nil))
(1 2)
コンスセル、即ちcarとcdrのペアがゼロ個の場合を見てみます。
(list )
nil
'()
nil
'()はnilとなります。null関数でnilかどうか調べてみます。
(null '())
t
いろいろ試してみます。
(cons nil nil)
(nil)
(cons '(1 2) nil)
((1 2))
(cons '(1 2) '(3 4))
((1 2)(3 4))
型をカッコで囲った形、(型 型 型・・・)がリストです。リストの構成要素をアトムatomといいます。リストとアトムを合わせてS式というそうです。
(list 1 2 3)の省略形は'(1 2 3)です。'〇は本来(quote 〇)で評価せず、という意味があります。
(list 1 2 3)
(1 2 3)
(quote (1 2 3))
(1 2 3)
'(1 2 3)
(1 2 3)
car関数はリストの最初のアトムを返します。
(car (list 1 2 3))
1
cdr関数は最初のアトムを除いたリストを返します。
(cdr (list 1 2 3))
(2 3)
単にcons関数でくっつけたものを分解しているだけです。
(car (cons 1 (list 2 3)))
1
(cdr (cons 1 (list 2 3)))
(2 3)
いろいろ試してみます。
(cdr (list 1))
nil
(car (list))
nil
(car nil)
nil
(car nil)
nil
(cdr nil)
nil
(cdr cons(1 2))
2
atomp、listp、consp関数を使っていろいろ試してみます。
(atomp (cons 1 2))
nil
尚ANSI Common LISPではatomp関数の代わりにatomを使います。
* (atom (cons 1 2))
NIL
続けます。
(consp (cons 1 2))
t
(listp (cons 1 2))
t
(atomp t)
t
(consp t)
nil
(listp t)
nil
(symbolp t)
t
(atomp nil)
t
(consp nil)
nil
(listp nil)
t
(symbolp nil)
t
nilは真偽のsymbolとして、アトムとして、空のリストとして認識されます。
アトムは同じ型でなくてもOKです。
(list 1 2.3 'a #\b "c" #'+)
(1 2.3 a #\b
"c" #<Pascal: +>)
尚、記号’aはa、関数#’+は#<Pascal: +>と表記されてしまいます。
car関数はリストの最初のアトムを返します。
(car (list 1 2.3 'a #\b "c" #'+))
1
cdr関数はリストの最初のアトム以外のリストを返します。
(cdr (list 1 2.3 'a #\b "c" #'+))
( 2.3 a #\b "c" #<Pascal: +>)
quote関数はReturnキーを押したても実行されないという機能があります。
(quote (a b c))
(a b c)
(quote (a b c))
(a b c)
(list (quote a)(quote b)(quote c))
(a b c)
(quote (+ 1 2 3))
(+ 1 2 3)
quote関数内にquote関数がある場合は、最初のquote関数しか実行されません。実行されなかったquote関数は'の形で表示されます。
(quote (quote (+ 1 2 3)))
'(+ 1 2 3)
quote関数の効果を打ち消すのがeval関数です。quote関数1個につき、eval関数1個に相当します。
(eval (quote (+ 1 2 3)))
6
(eval (quote (quote (+ 1 2 3))))
(+ 1 2 3)
(eval (eval (quote (quote (+ 1 2 3)))))
6
mjk LISPではbackquote「`」は使えません。backquoteはquote関数内の一部を実行できる関数です。komma「,」や「,@」とセットで使います。
Steel Blank Common Lispでは以下の通りです。このbackquoteはマクロを使うとき大変便利なのですがmjk
LISPでは対応していません。
* `(1 2 (+ 3 4 5))
(1 2 (+ 3 4 5))
* `(1 2 ,(+ 3 4 5))
(1 2 12)
* `(1 2 ,'(3 4 5))
(1 2 (3 4 5))
* `(1 2 ,@'(3 4 5))
(1 2 3 4 5)
(関数
〇 〇 〇)の構文をとります。
四則演算の+-*/が普通に使えます。
10+20の計算
(+ 10 20)
30
Graph termには表示されません。表示させるには以下のようにします。
(print (+ 10 20))
30
30
四則演算の関数+、-、*、/は複数の値をとることができます。
10+20+30+40の計算
(+ 10 20 30 40)
100
10*20*30*40の計算
(* 10 20 30 40)
240000
1.2×102+3.4×103の計算
(+ 1.2e+2 3.4e+3)
3520.0
(10+20)-(30+40)の計算
(- (+ 10 20)(+ 30 40))
-40
16進数の表示は#xを使います。16(0x10)と14(0x0e)の計算をしてみます。
(+ #x10 #x0e)
30
お約束の1÷0です。
(/ 1 0)
inf.0
(type-of inf.0)
エラーとなります。Steel Blank Common Lispでも対応していません。
次にです。
(sqrt -2)
-nan.0
(type-of –nan.0)
エラーとなります。
Steel Blank Common Lispでは複素記号#Cを用いて以下のようになります。
* (sqrt -2)
#C(0.0 1.4142135)
桁どり関数です。
(floor pi);切り捨て
3.0
(round pi);四捨五入
3
(ceil pi);切り上げ
4.0
(truncate pi);正負のゼロ方向への切り捨て
3
format関数を使って円周率π、piを文字列として表示させてみます。
(format nil "~a" pi)
" 3.1415926"
mjk LISPでは倍精度浮動小数点でなく、単精度浮動小数点となっています。整数部3桁、小数部10桁で表示させます。
3ドット10Fでなく、3カンマ10Fであることに注意してください。
(format nil “~3,10F” pi)
"3.1415926000"
piは定数となっていて小数第8桁以降はゼロになっています。
ネイピア数をexp関数で求め、小数第30位まで表示させてみます。赤字移行は計算誤差です。
(exp 1)
2.71828182846
(format nil “~3,20F” (exp 1))
"2.718281828459045090795598298428"
正解は
“2.71828182845904523536
0287471352”
です。
sin(π/6)の計算
(sin (/ pi 6))
0.499999992265
210の計算。mjk LISPではpow関数を使いますが、ANSI Common
Lispではexpt関数になります。
(pow 2 10)
1024.0
乱数の作成はrand関数を用います。0〜231の整数を求めます
(rand)
1102520059
random関数を使うと0〜任意の数までの整数を求めることができます。
(random 100)
37
1+、1-という関数も用意されています。
(1+ pi)
4.1415926
(1- pi)
2.1415926
max関数を使って最大値を求めます。
(max 1 2 3 4 5)
5
min関数を使って最小値を求めます
(min 1 2 3 4 5)
1
1>0か2>=1>=1>=0かを調べます。
(> 1 0)
t
(>= 2 1 1 0)
t
1>2かを調べます。lessp関数でもいいようです。
(> 1 2)
nil
(lessp 1 2)
t
1==1を調べます。eql関数でもいいようです。
(= 1 1)
t
(eql 1 1)
t
1==1.0を調べます。
(= 1 1.0)
t
(eql 1 1.0)
nil
1は整数、1.0は浮動小数点なのでeql関数ではnilとなりました。
いろいろ試してみます。
(eql 1.0 1.00)
t
(eql 1.0000000000000000000000000000003 1.000)
t
=は2つ以上の値をとることができます。
1==1.0==00
(= 1 1.0 (pow 0 0))
t
等しくないは/=を使います。
(/= 1 2)
t
関数=は数字しか使えません。
(= (list 1 2)(cons 1 '(2)))
エラーとなります。
真偽のt nilはeql関数が使えます。
(eql t t)
t
(eql nil (list))
t
リストの時はequal関数を使います。
(eql (list 1 2)(cons 1 '(2)))
nil
(equal (list 1 2)(cons 1 '(2)))
t
equal関数はeql関数で真tだと真tとなりますが、equalでtでもeql関数でtになるとは限りません。
紛らわしい関数にeq関数があります。メモリのアドレスが一致しているかどうかを見る関数です。
(eq pi pi)
nil
第1アトムのpiと第2アトムのpiが格納されているアドレスが異なるのでnilとなります。
(eq ‘pi ‘pi)
t
piは定数で同じアドレスを見に行っているのでtとなります。
cons関数はアトムとリストを結合しますが、append関数はリストとリストを結合します。
(cons 1 '(2))
(1 2)
(append '(1)'(2))
(1 2)
(cons '(1 2)'(3))
((1 2) 3)
(append '(1 2)'(3))
(1 2 3)
cons関数と違ってappend関数は3個以上のリストをとることもできます。
(append '(1 2)'(3 4)’(5 6))
(1 2 3 4 5 6)
リストのカッコをとるのはvalues-list関数を使います。
(values-list ‘(1 2 3 4 5))
1 2 3 4 5
リストがなくなった場合、あとで使われるのは最初のアトムだけです。
(+ (values-list '(1 2 3 4 5) )10)
11
リストの長さはlength関数を使います。
(length ‘(1 2 3 4 5))
5
(list ‘())
0
(length (cons 1 2))
1
(length (list 1 2)
2
但し、リストの内のリスト数まではカウントされません。
(length '(1 '(2 3 4)))
2
文字、文字列の場合を見てみます。
(cons #\a '(2 3))
(#\a 2 3)
(append '(#\a) '(2 3))
(#\a 2 3)
(cons "a" '(2 3))
("a" 2 3)
(append '("a") '(2 3))
("a" 2 3)
append関数よりcons関数の方がカッコが少なくて楽です。
記号や関数の場合は実行されないように(quote 〇)か省略形の'〇とします。
(cons (quote a) '(1 2 3))
(a 1 2 3)
(cons 'max '(1 2 3))
(max 1 2 3)
実行させるにはeval関数を使います。
(eval (cons 'max '(1 2 3)))
3
同様の処理をするのに特殊な関数としてapplyがあります。(apply 関数 リスト)という構文をとります。
(apply #'max '(1 2 3))
3
car関数はリストの最初のアトムを返し、cdr関数は残りのリストを返します。
(car '(1 2 3 4 5 6 7))
1
(cdr '(1 2 3 4 5 6 7))
(2 3 4 5 6 7)
car関数とcdr関数を組み合わせればn番目のアトムを取り出せます。
(car (cdr '(1 2 3 4 5 6 7)))
2
(car (cdr (cdr '(1 2 3 4 5 6 7))))
3
(car (cdr (cdr (cdr '(1 2 3 4 5 6 7)))))
4
cdrの回数文だけカッコが増えて面倒です。
cdrを2回する関数としてcddr、cdrを3回する関数としてcdddr、cdrを4回する関数としてcddddrが用意されています。
(cdr '(1 2 3 4 5 6 7)))
(2 3 4 5 6 7)
(cddr '(1 2 3 4 5 6 7))
(3 4 5 6 7)
(cdddr '(1 2 3 4 5 6 7))
(4 5 6 7)
(cddddr '(1 2 3 4 5 6 7))
(5 6 7)
5回目用のcdddddrは用意されていません。
(functionp (function cdddddr))
エラーになります。
そこで5回目以降はnthcdr関数を使います。
n回cdr関数を実行するには(nthcdr n リスト)という構文をとります。
(nthcdr 1 '(1 2 3 4 5 6 7)))
(2 3 4 5 6 7)
(nthcdr 3 '(1 2 3 4 5 6 7))
(3 4 5 6 7)
(nthcdr 5 '(1 2 3 4 5 6 7))
(4 5 6 7)
nthcdrの最終結果用の関数がlast関数です。
(last '(1 2 3 4 5 6 7))
(7)
nthcdr関数やlast関数とcar関数を組み合わせることでn+1番目のアトムの取り出しが容易となります。
(car (nthcdr 0 '(1 2 3 4 5 6 7))))
1
(car (nthcdr 1 '(1 2 3 4 5 6 7))))
2
(car (nthcdr 2 '(1 2 3 4 5 6 7))))
3
(car (nthcdr 3 '(1 2 3 4 5 6 7))))
4
(car (last '(1 2 3 4 5 6 7))))
7
まだ面倒という人のために(car (nthcdr n リスト))のnの数だけdを増やすことでn+1番目のアトムを取り出す関数が用意されています。
cadr、caddr、cadddrです。これだとさらにカッコの数が少なくなり便利です。
(cadr '(1 2 3 4 5 6 7))
2
(caddr '(1 2 3 4 5 6 7))
3
(cadddr '(1 2 3 4 5 6 7)))))
4
5番目のアトムを取り出すcaddddr関数は用意されていません。
(caddddr '(1 2 3 4 5 6 7)))))
dの数が0〜4で1〜5までのアトムの取り出ししか対応してません。
そこで1〜10番目のアトムを取り出す関数としてfirst、second、third、・・・tenth関数が用意されています。
(first '(1 2 3 4 5 6 7 8 9 10 11))
1
(second '(1 2 3 4 5 6 7 8 9 10 11))
2
(tenth '(1 2 3 4 5 6 7 8 9 10 11))
10
11番目以降の時はnth関数を使います。(nth n リスト)という構文をとります。
(nth 0 '(1 2 3 4 5 6 7 8 9 10 11))
1
(nth 4 '(1 2 3 4 5 6 7 8 9 10 11))
5
(nth 10 '(1 2 3 4 5 6 7 8 9 10 11))
11
nthcdr関数はリストの最初からn個のアトムを取り除いたリストを返す関数ですが、butlast関数は最後尾ラストからn個のアトムを取り除く関数です。
nthcdr関数の書式は(nth n リスト)でしたが、butlast関数は(butlast リスト n)と逆になります。
(butlast '(11 10 9 8 7 6 5 4 3 2 1) 0)
(11 10 9 8 7 6 5 4 3 2 1)
(butlast '(11 10 9 8 7 6 5 4 3 2 1) 1)
(11 10 9 8 7 6 5 4 3 2)
(butlast '(11 10 9 8 7 6 5 4 3 2 1) 5)
(11 10 9 8 7 6)
リストの後ろからn番目のアトムを取り出すには、butlast関数とlast関数とcar関数を組み合わせます。nthcdr関数と同じく、後ろからn番目のアトムを取り出すにはbutlast関数の値もn-1にする必要があります。
(butlast '(11 10 9 8 7 6 5 4 3 2 1) 6)
(11 10 9 8 7)
(last (butlast '(11 10 9 8 7 6 5 4 3 2 1) 6))
(7)
(car (last (butlast '(11 10 9 8 7 6 5 4 3 2 1) 6)))
7
残念ながらカッコを減らすための関数は標準では用意されていません。
リストの順序を逆転する関数としてreverse関数が用意されています。
(reverse '(11 10 9 8 7 6 5 4 3 2 1))
(1 2 3 4 5 6 7 8 9 10 11)
reverse関数とnth関数を組み合わせたらリストの最後尾からn-1番目のアトムをとりだすといった操作が楽になります。
(nth 0 (reverse '(11 10 9 8 7 6 5 4 3 2 1)))
1
(nth 6 (reverse '(11 10 9 8 7 6 5 4 3 2 1)))
7
member関数はリスト内の該当するアトム以降のアトムからなるリストを取り出します。
(member 3 '(1 2 3 0 1 2 3))
(3 0 1 2 3)
リスト内リスト用の特殊なリスト操作関数も用意されています。
以下のようなリストを作成します。
'((1 "apple" 100)(2 "banana" 50)(3
"persimmon" 120)(4 "grape" 400))
((1 "apple" 100) (2 "banana" 50) (3
"persimmon" 120) (4 "grape" 400))
このようなリストのことを連想リストassociation list略してalistといいます。
caarlistは連想リストの最初だけを取り出す関数です。
(caarlist '((1 "apple" 100)(2 "banana" 50)(3
"persimmon" 120)(4 "grape" 400)))
(4 3 2 1)
assoc関数は連想リストの最初のアトム(キー)が一致するリストを取り出します。
(assoc 3 '((1 "apple" 100)(2 "banana" 50)(3
"persimmon" 120)(4 "grape" 400)))
(3 “persimmon” 120)
assoc関数はキーが数値でない場合はうまく動作しないようです。
acons関数は連想リストにデータを追加するときに使います。
(acons 0 '("orange" 30) '((1 "apple" 100)(2
"banana" 50)(3 "persimmon" 120)(4 "grape" 400)))
((0 "orange" 30) (1 "apple" 100) (2
"banana" 50) (3 "persimmon" 120) (4 "grape" 400))
mapcarという関数を使えばもっといろんなことができます。(mapcar (function 関数名) リスト)という構文をとります。
(mapcar (function car)'((1 "apple" 100)(2
"banana" 50)(3 "persimmon" 120)(4 "grape" 400)))
(1 2 3 4)
caarlistと同じです。2番目のアトムを取り出します。
(mapcar (function second)'((1 "apple" 100)(2
"banana" 50)(3 "persimmon" 120)(4 "grape" 400)))
("apple" "banana" "persimmon"
"grape")
番号のない連想リストに番号を加えます。
(mapcar (function cons)'(1 2 3 4 ) '(("apple"
100)("banana" 50)("persimmon" 120)("grape" 400)))
((1 "apple" 100) (2
"banana" 50) (3 "persimmon" 120) (4 "grape" 400))
リストのアイテムごとの計算もできます。なおfinverseは逆数を求める関数です。
(mapcar (function 1+)'(1 2 3 4 5))
(2 3 4 5 6)
(mapcar (function finverse)'(1 2 3 4 5))
( 1.0 0.5
0.333333333333 0.25 0.2)
リストとリストの計算もできます。
(mapcar (function +)'(1 2 3)'(4 5 6)'(7 8 9)))
(12 15 18)
set関数を使って記号Aをリスト(1,2,3)と定義します。A=(1,2,3)です。
(set (quote A) ‘(1 2 3))
(1 2 3)
(set ‘A ‘(1 2 3))
(1 2 3)
実はset quoteといちいち定義するのが面倒なのでsetq関数なるもののほうがよく使われます。
(setq A ‘(1 2 3))
(1 2 3)
記号を値を知るには、そのまま記号のみを入力するかsymbol-value関数を使います。
A
(1 2 3)
(symbol-value ‘A)
(1 2 3)
eval関数でも同様のことができます。
(eval ‘A)
(1 2 3)
eval関数とsymbol-value関数の違いを見てみます。記号に記号を定義してみます。
(setq B A)
A
(symbol-value ‘B)
A
(eval ‘B)
(1 2 3)
setq関数でA=(1 2 3)とB=(4 5 6)を定義します。同時に複数定義することもできます。
(setq A ‘(1 2 3) B ‘(4 5 6))
(4 5 6)
Bしか表示されないのでappend関数で確かめてみます。
(append A B)
(1 2 3 4 5 6)
上の2つの実行してみます。
(setq A ‘(1 2 3) B ‘(4 5 6))(append A B)
(4 5 6)
(append A B)が実行されません。
そこでprogn関数を使います。
(progn (setq A '(1 2 3) B '(4 5 6))(append A B))
(1 2 3 4 5 6)
最後の結果が表示されました。
prog1関数、prog2関数、progn関数をを使えば1行で複数のリストを実行することができます。prog1関数の結果は第1実行文の結果、prog2関数の結果は第2実行文の結果、progn関数の結果は最終実行文の結果が表示されます。
(prog1 (setq A '(1 2 3))(setq B '(4 5 6))(setq C '(7 8 9)))
(1 2 3)
最初のリストの実行結果が表示されています。確認です。
(append A B C)
(1 2 3 4 5 6 7 8 9)
(prog2 (setq A '(1 2 3))(setq B '(4 5 6))(setq C '(7 8 9)))
(4 5 6)
2番目のリストの実行結果が表示されています。
(append A B C)
(1 2 3 4 5 6 7 8 9)
(progn (setq A '(1 2 3))(setq B '(4 5 6))(setq C '(7 8 9)))
(7 8 9)
最後のリストの実行結果が表示されています。
(append A B C)
(1 2 3 4 5 6 7 8 9)
setq関数と似ていますが、let関数というのもあります。こちらは(let …)ないでしか値が保持されません。局所変数として用います。
(setq A '(1 2 3) B '(4 5 6))
(4 5 6)
(let ((A '(4 5 6))(B '(7 8 9)))(append A B))
(4 5 6 7 8 9)
(appendA B)
(1 2 3 4 5 6)
記号には関数を代入することもできます。
(setq x 1 y 2 z 3 plus '(+ x y z))
(+ x y z)
(eval plus)
6
大域変数はdefparameter関数やdefvar関数を用います。お約束として*名前*といった変数名にします。暗黙の掟です。defvar関数は一旦適宜すると元の値は上書きされません。setq関数とことなり、注釈文をつけることができます。注釈文は後で書き換え可能です。defparameter関数やdefvar関数で折角定義してもsetq関数だと上書きされてしまいます。
(defparameter *a* 10)
*a*
*a*
10
(defparameter *a* 3 “important value”)
*a*
*a*
3
(describe ‘*a*)
nil
(defvar *b* 4)
*b*
*b*
4
(defvar *b* 20 “very important value”)
*b*
*b*
4
(describe ‘*b*)
nil
定数はdefconstant関数を使います。注釈文をつけれます。お約束として+名前+とすることが多いそうです。が、mjk LISPでは*名前*のままです。setq関数でも値が上書きされません。
(defconstant +c+ 7 “constant”)
+c+
+c+
7
(defconstant +c+ 100)
nil
+c+
7
(setq +c+ 5)
(describe ‘+c+)
nil
定数かどうかはconstantp関数を使います。
(constantp +c+)
t
(constantp pi)
t
(constantp *main-menu*)
t
mjk LISPでは|名前|という形式にしても空白は使えません。
(defvar |a b| 3)
今までのリスト操作はコンスセルの接続の変化だけでした。carとcdrのアドレスのうち、cdrの値だけを変更していました。carの値を
push関数はcons関数と似ていますが、書き換えが行われます。
(setq A ‘(1 2 3 4))
(1 2 3 4)
(cons 0 A)
(0 1 2 3 4)
A
(1 2 3 4)
cons関数ではAの値は書き換わっていません。書き換えるには以下のようにします。
(setq A (cons 0 A)
(0 1 2 3 4)
いちいちsetq関数を使うのは面倒です。そこでpush関数を使います。
(push 0 A)
(0 1 2 3 4)
A
(0 1 2 3 4);
Aの値は書き換わっています。
nconc関数はappend関数と似ていますが、書き換えが行われます。
(progn (setq A '(1 2 3) B '(4 5 6))(append A B)A)
(1 2 3)
(progn (setq A '(1 2 3) B '(4 5 6))(nconc A B)A)
(1 2 3 4 5 6)
注意
nconcは上書き用の記号にnilを使うと結果がnilとなります。
(progn (setq A nil)(nconc A '(1 2))A)
nil
pop関数はcdr関数と似ていますが、書き換えが行われます。
(progn (setq A '(1 2 3))(cdr A)A)
(1 2 3)
(progn (setq A '(1 2 3))(pop A)A)
(2 3)
delete関数は該当するアトムを取り除き、値を書き換えます。
Aの値は書き換わっていません。
(setq A ‘(1 2 3 4)) (progn (setq
A '(1 2 3 4))(delete 2 A)A)
(1 3 4)
rplaca関数は最初のアトムを書き換えます。
(progn (setq A '(1 2 3 4))(rplaca A 0)A)
(0 2 3 4)
rplacd関数は最初のアトム以外を書き換えます。
(progn (setq A '(1 2 3 4))(rplacd A '(3 5 7))A)
(1 3 5 7)
nreverse関数はリストの順序を入れ替えます。が、壊れています。
(progn (setq A '(1 2 3 4))(nreverse A))
(4 3 2 1)
A
(1)
nreverse関数の結果は正しく表示されていますが、値の上書きされていません。
「“」と「”」で囲まれた範囲が文字列simple-vectorです。
(type-of “abc”)
simple-vector
(typep “abc” ‘string)
t
(stringp “abc”)
t
文字列の中で”は\”として表示されます。\と表示されてますが、バックスラッシュです。
(print "\"")
“\””
string関数で記号を文字列にします。文字は文字列にしません。
(string ‘abc)
“abc”
(string #\a)
nil
str-append関数で文字列を連結します。
(str-append "abc" "def")
“abcdef”
(str-append "abc""def""ghi")
"abcdefghi"
注意 “”を使わないこと。壊れます。
(str-append “a” “”)
“”
(str-append “” “a”)
Graph termです。
** SIGNAL HANDLER: Signal SIGSEGV (11: segmentation violation)
Memory
allocated (163.02 Mb)
Shall we make a
coredump (y/n) ?
強制終了です。
format関数でも同様のことができます。~aは文字列を、~sはそのままを(“を\”形式で)表現します。
(format nil "~a~a~s"
"abc""def""ghi")
"abcdef\"ghi\""
数字はformat関数で文字列にします。~dは整数、~fは小数、~sはそのままです。
(format nil “~d” 1)
“1”
(format nil “~f” 1.0)
"1.000000"
(format nil "~2,10f" (sqrt 2))
"1.4142135624"
(format nil “~s” 3.14159265358979)
" 3.14159265359"
2進数~b、8進数~o、16進数表現~xはformat関数の文字列表記によります。桁数を定義することもできます。
(format nil "~b, ~o, ~x" 123 123 123)
"1111011, 173, 7B"
(format nil “~8b, ~4o, ~4x”, 123 123 123)
" 1111011,
173, 7B"
0〜自然数をアルファベット表記するのもformat関数を使います。
(format nil “~r” 123)
"one-hundred-twenty-three"
10のn乗は~eを使います。
(format nil “~e” 123)
" 1.23000E+02"
スペースは~tを使います。
(format nil “~t” 5)
"
"
改行は~%を使います。
(format nil “1~%2~%3~%”)
"1
2
3
"
文字列の長さはlength関数が使えます。
(length “abcd”)
4
文字列の比較はequal関数が使えます。
(equal “abc” “abc”)
t
character関数は1文字の文字列を文字にします。#\Spaceなどはうまく表示されません。
(character “A”)
#\A
string関数で文字を文字列にするのは・・・できません。
(string #\A)
nil
万能のformat関数を使います。
(format nil "~a" #\A)
"A"
(format nil "~a~a" #\A #\B)
"AB"
char-code関数は文字をアスキー番号にします。
(char-code #\A)
65
code-char関数というのはなくて、mjk LISPでは代わりにint-char関数というのがあります。
int-char関数は文字のアスキー番号を文字にします。
(int-char 65)
#\A
文字列を記号にするにはintern関数を使います。
(intern "abc")
abc
文字列を数字にするにはread-from-string関数を使います。parse-integer関数はありません。
(read-from-string "12.34")
12.34
定番のif関数です。
(if t "true" "false")
“true”
(if nil "true" "false")
“false”
when関数というのもあります。条件文が真のとき最後の実行文を実行し、偽のときはnilを返します。
(when t "a" "b" “c”)
“c”
(when nil “a” “b” “c”)
nil
unless関数はwhenとは逆に条件文が真のときnilを、偽のときに最後の実行文を実行します。
(unless t "a" "b" "c")
nil
(unless nil “a” “b” “c”)
"c"
cond関数は場合分け用の条件文に用います。
(cond (t 1)(t 2)(t 3)(t 4)(t "end"))
1
(cond (nil 1)(t 2)(t 3)(t 4)(t "end"))
2
(cond (nil 1)(nil 2)(nil 3)(nil 4)(t "end"))
“end”
(cond (nil 1)(nil 2)(nil 3)(nil 4)(nil "end"))
nil
case関数も場合分け用の条件文に用います。
(case 2 (1 "a")(2 "b")(3 "c"))
2
(case 0 (1 "a")(2 "b")(3 "c"))
nil
条件文にはand関数、not関数やor関数が使えます。
(if (not t) “a” “b”)
“b”
(if (not nil) “a” “b”)
“a”
(if (and t t) "a" "b")
“a”
(if (and t nil) “a” “b”)
“b”
(if (or t nil) "a" "b")
“a”
(if (or nil nil) "a" "b")
“b”