- 001_Common-Lisp-基本構文説明.md
- 002_Common-Lisp-リスト操作説明.md
- 003_Lisp処理系起動時のディレクトリパス-カレントディレクトリパスを取得-設定する.md
-
-
Save otaon/0a731fb98016c163732114c4d2c40430 to your computer and use it in GitHub Desktop.
- Common Lisp の基本的なシンタックスをすぐに確認するため。
- 自分用の備忘録。
- Common Lisp および Lisp が、括弧だらけのプログラミング言語だと知っていること。
- Common Lisp の実行環境を持っていること。(CLisp、Steel Bank Common Lisp など)
LispはAST(抽象構文木)を直接記述するかの如くプログラミングする言語である。 換言すれば、ソースコードが殆どASTと等価であると見做せる。 また、Lispは、そのインタプリタによって、逐次的にソースコードを読み込み、評価し、表示する。 そして、この手順を繰り返してソースコード全体を処理する。 この仕組みを、REPL(Read–Eval–Print Loop)という。
上記のとおり、Lispのソースコードは殆どASTであるから、データ構造も命令と同様の表現を取る。
即ち、全ての表現を同様に記述できる。
これを実現するために、Lispでは「コンスセル」という概念を導入している。
これは、単純化すれば、2つの値を代入できる__箱__である。
視覚化するならば、[a|b]
のようになる。このaおよびbに、値が入る。
ここで、値とは、シンボルへの参照と、コンスセルへの参照を意味する。
即ち、Lispでは、ソースコードのアトムとしてシンボルがあり、それらが構造を成す際にコンスセルを用いているといえる。
下記に、Lispに於けるリスト表現を樹形図として表す。
'(A)
*--nil
|
A
'((A B) (C D))
*----------*-----nil
| |
*--*--nil *--*--nil
| | | |
A B C D
リストは(list hoge foo bar)
の様に記述する。
またはquote('
)を使って、'(hoge foo bar)
の様に記述する。
'(a b c)
; => (A B C)
```cl
(defun describe-path (edge)
`(there is a ,(caddr edge) going ,(cadr edge) from here.))
`(describe-path '(garden west door))
; => (THERE IS A DOOR GOING WEST FROM HERE.)
下記に、コンスセル、および、リストの操作方法を示す。
(cons 'x 'y)
; => (x . y)
(cons 'x nil)
; => (X)
(cons 'x ())
; => (X)
; 先頭のスロットのデータを取り出す
(car '(a b c d))
; => A
; 2番めのスロットのデータを取り出す
(cdr '(a b c d))
; => (b c d)
; car cdr を同時に行う関数
(cadr '(a b c))
= (car (cdr '(a b c)))
; => 'B
コロンを先頭に付けたシンボルの使い方の1つ。 シンボルが、そのシンボルそのままの名前を評価値に持つ。 用途は以下がある。
- 普通の変数により、値を持たせる
(let ((cigar 5))
cigar)
; => 5
:cigar
; => CIGAR
- 定数として使う
定数として使うと、Lispが処理をより効率的に最適化してくれる。
(let ((:cigar 5))
:cigar)
; => LET :CIGAR is constant ...
Lispに於ける変数は、他の言語と同様に、グローバル変数とローカル変数が存在する。
上書き | 定義 |
---|---|
する | (defparameter *global-value* num) |
しない | (defvar *global-value* num) |
(let ((variable-name1 value1)
(variable-name2 value2))
...)
例:
(let ((a 5)
(b 6))
(+ a b))
Lispに於ける関数は、グローバル関数とローカル関数が存在する。
(defun function-name (arguments)
...)
例
(defun function-name (arguments)
(ash (+ *small* *big*) - 1))
; (ash x n) := xを2進nビット左にずらす
(flet ((function-name1 (arguments)
...)
(function-name2 (arguments)
...))
...)
例:
(flet function-name (arguments)
(ash (+ *small* *big*) - 1))
; (ash x n) := xを2進nビット左にずらす
(labels ((function-name1 (arguments)
...)
(function-name2 (arguments)
(function-name1 *arguments*))
...)
例:
(labels ((a (n)
(+ n 5))
(b (n)
(+ (a n) 6)))
(b 10))
; => 21
下記の表記を使うことで、関数をパラメータとして渡すことができる。
これを使って、mapcar
関数などに使用できる。
; (方法1)
#'関数名
; (方法2)
(function 関数名)
一連の処理を纏める。 関数名を定義するまでもない場合に有効。
構文:
(lambda (args) (procedure))
; => <FUNCTION :LAMBDA...>
例:
; 下記のように通常どおり処理を纏めると関数名が必要となる。
(defun half (n)
(/ n 2))
; => <FUNCTION :HALF...>
; 下記のように書けば関数名は不要となる。
(lambda (n)
(/ n 2))
(mapcar (lambda (n) (/ n 2)) '(2 4 6))
; => (1 2 3)
Lispでは、返り値を複数持つ関数が存在する。例えばread-line
がそれにあたる。
通常は1つ目の返り値しか使われない。
(read-line)
; abc
; => "abc";
NIL
下記のようにvalues
を用いる。
(defun foo ()
(values 3 7))
(foo)
; => 3;
; 7
multiple-value-bind
を用いる。
multiple-value-bind
の構文:
(multiple-value-bind '(binding-vals) (func-call))
- '(binding-vals)
- (func-call)の返り値を束縛するための変数のリスト。
- (func-call)
- 複数の返り値を持つ関数の呼び出し。
例:
(defun foo ()
(values 3 7))
(multiple-value-bind (a b) (foo)
(list a b))
; => ( 3 7)
; fooの返り値を、aとbに束縛している。
(if (審議を返すリスト)
(真の時に実行するリスト)
(偽の時に実行するリスト))
例:
(if (= (+ 1 2) 3)
'yup
'nop)
; => YUP
(when (条件式)
(処理1)
(処理2)
...
(処理n))
例:
(when (oddp 5)
(setf *number-is-odd* t)
'odd-number)
; => ODD-NUMBER
*number-is-odd*
; => T
(unless (条件式)
(処理1)
(処理2)
...
(処理n))
例:
(unless (oddp 4)
(setf *number-is-odd* nil)
'even-number)
; => EVEN-NUMBER
*number-is-odd*
; => nil
上記に示したif
などの制御構文では、実行処理するリストが1つのみ持てる構造となっている。
しかし、一般的には複数の処理を行いたいことが多い。
prognは、中のリストを順繰りに評価して、最後の評価値をprognフォームの値として持つ。
例:
(if '(oddp 5)
(progn (setf *number-was-odd* t)
'odd-number)
'even-number)
; => ODD-NUMBER
*number-was-odd*
; => T
condは、C言語のswitch-case
文のようなものである。
つまり、複数のシンボルに対するテストを行う。
caseのシンボルと、それぞれのシンボルをeqlを使って評価する。
もし偽と評価されれば、otherwiseに分岐される。
(case シンボル
((分岐のシンボル) (処理1)
(処理2)
...
(処理n))
((分岐のシンボル) (処理1)
(処理2)
...
(処理n))
(otherwise (処理1)
(処理2)
...
(処理n)))
condは、caseと同じく、C言語のswitch-case
文のようなものである。
ただし、C言語のswitch-case
文とは異なり、シンボルではなく条件式を列挙する。
(cond ((条件式1) (処理1)
(処理2)
...
(処理n))
((条件式2) (処理1)
(処理2)
...
(処理n))
(t (処理1)
(処理2)
...
(処理n)))
; 条件式nが全て偽だった時、tが実行される。
loopは、第1引数の値によって、異なるループ方法を指定できる。
(loop repeat 10
collect 1)
; => (1 1 1 1 1 1 1 1 1 1)
(loop for n from 1 to 10
collect n)
; => (1 2 3 4 5 6 7 8 9 10)
(loop for n from 1 to 10
collect (+ 100 n))
; => (101 102 103 104 105 106 107 108 109 110)
C言語の||
や&&
のように、制御構文の条件式に使用できる。
(or (oddp 4) (setf *it-is-even* t))
; => T
*it-is-even*
; => T
短絡評価: 下記のように、lispは短絡評価する。
(or (oddp 5) (setf *it-is-even* t))
; => T
*it-is-even*
; => NIL
ここでは、シンボル、または、リストの値が他のリストの値と等しいか、また、リストの中に目当ての値が入っているかなどを調べるための方法を示す。
シンボルやリストが等しいか否かを調べる関数。
関数名 | 比較方法 |
---|---|
eq | シンボル同士はeqで比較する |
equal | シンボル同士では無い場合はequalで比較する |
eql | eqと似ているが、更に数値と文字を比較できる |
equalp | equalと似ているが、少し緩い評価をする |
= | 数値用のequal |
string-equal | 文字列用のequal |
char-equal | 文字用のequal |
例:
(defparameter *fruit* 'apple)
=> *FRUIT*
(cond ((eq *fruit* 'apple) 'its-an-apple)
((eq *fruit* 'orange) 'its-an-orange))
(equal 'apple 'apple)
=> T
(equal 'apple 'apple)
=> T
(equal (list 1 2 3) (list 1 2 3))
=> T
(equal '( 1 2 3) (cons 1 (cons 1 (cons 2 (cons 3 nil)))))
=> T ; 中身が同じなら同一とみなす
(equal 5 5)
=> T ; 整数同士
(equal 2.5 2.5)
=> T ; 浮動小数点同士
(equal "foo" "foo")
=> T ; 文字列同士
(equal #\a #\a)
=> T ; 文字同士
(eql 'foo 'foo)
=> T ; シンボル同士
(eql 3.4 3.4)
=> T ; 数値同士
(eql #\a #\a)
=> T ; 文字列同士
; equalpの使い方
(equalp "Bob Smith" "bob smith")
=> T ; 大文字、小文字を無視した文字列同士
(equalp 0 0.0)
=> T ; 整数と浮動小数点数
オブジェクトを指定した形式へ強制変換する。 (coerce : 〜を無理にdoさせる。)
(coerce '(a b c) 'vector) ; => #(A B C)
(coerce 'a 'character) ; => #\A
(coerce 4.56 'complex) ; => #C(4.56 0.0)
(coerce 4.5s0 'complex) ; => #C(4.5s0 0.0s0)
(coerce 7/2 'complex) ; => 7/2
(coerce 0 'short-float) ; => 3.5L0
(coerce 3.5L0 'float) ; => 3.5L0
(coerce (cons 1 2) t) ; => (1 . 2)
; NOTE: タイプtは、全てのオブジェクトのタイプの集合。
; 従って、この場合は、引数をそのまま返す。
補集合。(高階関数)
例:
(complement #'alphanumericp)
; => アルファベットでも数字でもない
この記事では、リスト操作において基本的な関数の使い方について説明する。
リスト中に、或る要素が含まれているか否かを調べる。
(if (member 1 '(3 4 1 5))
'one-is-in-the-list
'one-is-not-in-the-list)
=> ONE-IS-IN-THE-LIST
; memberの実際の返り値
(member 1 '(3 4 1 5))
=> (1 5)
上記に示したように、memberの挙動は下記のとおりである。
- リストをcarして、
- その要素が目的oの値だったら、
- そのリストを返す。
- 違ったら、cdrしたリストを対象にする。
- 上記1-4を繰り返す。
- 最後までみつからなかったらNILを返す。
例:
(member 3 '(1 5 3 4 (1 2) 4))
; => (3 4 (1 2) 4)
(member '(1 2) '(3 (1 2) 4))
; => NIL ; シンボルでないと駄目らしい
第2引数をcar
した値を対象に、第1引数に指定した関数を使って評価し、
真を返した値を返す。
もしも全ての値が偽だった場合はnil
を返す。
(find-if #'func '(hoge foo bar))
; => (foo bar)
#'oddp
により、リストから奇数が見つかるまで順に操作して、
真を返す値を見つけたら、その値を返す。
さもなければ、空リストを返す。
例:
(if (find-if #'null '(2 4 5 6))
'there-is-an-odd-number
'there-is-no-odd-number)
; => 'THERE-IS-AN-ODD-NUMBER
注意:nilを探す場合には使えない。
(find-if #'null '(2 4 nil 6))
; => NIL
連想リスト(association list)から、指定したキーのリストを返す。
(assoc 'key2 '((key1 (list1)) (key2 (list2)) (key3 (list3))) )
; => (KEY2 (LIST2))
例:
(assoc 'a '((a (1 2)) (b (3 4)) (c (5 6)) (d (7 8))) )
; => (A (1 2))
リストを第n引数(2 <= n <= N)に持ち、これの要素を1つずつ取り出して、第1引数の関数に対して適用する。
(mapcar #'func '(list-for-arg-1) ... '(list-for-arg-n))
; => `((func '(list-for-arg-1の第1要素) ... '(list-for-arg-nの第1要素))
; (func '(list-for-arg-1の第2要素) ... '(list-for-arg-nの第2要素))
; ...
; (func '(list-for-arg-1の第m要素) ...'(list-for-arg-nの第m要素)))
定義:
(defuc mapcar (func mlist)
(cond ((null mlist)
nil)
(t
(cons (funcall func (car mlist)) (mapcar func (cdr mlist))) ))
; 上記のmapcarを実行した結果
(mapcar #'+ '(1 2 3))
; =>
; mapcarを定義にしたがって展開する
(cons (funcall #'- (car '(1 2 3))) (mapcar #'- (cdr '(1 2 3))))
; =>
(cons (funcall #'- 1) (mapcar #'- '(2 3)))
; =>
(cons -1 (mapcar #'- '(2 3)) )
; =>
(cons -1 (cons (funcall #'- 2) (mapcar #'- '(3))) )
; =>
(cons -1 (cons -2 (mapcar #'- '(3)) ) )
; =>
(cons -1 (cons -2 (cons (funcall #'- 3) (mapcar #'- nil)) ) )
; =>
(cons -1 (cons -2 (cons -3 (mapcar #'- nil)) ) )
; =>
(cons -1 (cons -2 (cons -3 nil) ) )
; =>
(-1 . (-2 . (-3 . () ) ) )
; =>
(-1 -2 -3)
引数に会う複数のリストを、1つのリストに統合する。 (append = 〜を付け加える。)
(append '(list1) '(list2) '(list3))
; => (LIST1 LIST2 LIST3)
例:
(append '(many had) '(a) '(little lamb))
; =>
(MARY HAD A LITTLE LAMB)
引数にある、複数の要素を持つリスト(要素1つのリストでも良い)の各要素を、 引数にある関数の引数として渡す。
(apply #'func '(list))
例1:
(apply #'1+ '(2))
; => 3
(apply #'+ '(2 3))
; => 5
(apply #'append '((mary had) (a) (little lamb)) )
; => (MARY HAD A LITTLE LAMB)
とあるゲーム(Land of Lispのアレ)を例に示す。
- とあるゲームにおいて、各々の場所について(つながっている他の場所 移動方向 移動方法)を定義
(defparameter *edges* '((living-room (garden west door)
(attic upstairs ladder)
(garden (living-room east door)
(attic (living-room downstairs ladders))))
- とあるゲームにおいて、登場するオブジェクトを定義
(defparameter *objects* '(whiskey bucket frog chain))
- とあるゲームにおいて、登場するオブジェクトが置いてある場所を定義
(defparameter *objects-locations* '((whiskey living-room)
(bucket living-room)
(chain garden)
(frog garden)) )
- 与えられたパスを表示する
(defun describe-path (edge)
'(there is a ,(caddr edge) going ,(cadr edge) from here.))
; 使用例:
(describe-path '(garden west door))
; => (THERE IS A DOOR GOING WEST FROM HERE.)
- 指定した場所のパスを一覧表示する
(defun describe-paths (location edges)
(apply #' append (mapcar #'describe-path (cdr (assoc location edges)) )) )
; 使用例:
(describe-paths 'living-room *edges*)
; => (THERE IS A DOOR GOING WEST FROM HERE. \
; THERE IS A LADDER GOING UPSTAIRS FROM HERE.)
- 指定した場所にあるオブジェクトを一覧表示する
(defun objects-at (loc objs obj-locs)
(labels ((at-loc-p (obj)
(eq (cadr (assoc obj obj-locs)) loc)))
; remove-if-not : predicate が偽となる要素を取り除く
(remove-if-not #'at-loc-p objs)))
; 使用例:
(objects-at 'living-room *objects* *objects-locations*)
; => (objects-at *objects* *objects-locations*)
; => (remove-if-not #'at-loc-p *objects*)
; => 下記の評価を*objects*リストの全要素について行い、
; 真になったものをリストから消す。
; (eq (cadr (assoc obj obj-locs)) 'living-room)
; │ └ '((whiskey living-room)
; │ (bucket living-room)
; │ (chain garden)
; │ (frog garden))
; └ 'whiskey
; => T
; (eq (cadr (assoc obj obj-locs)) 'living-room)
; │ └ '((whiskey living-room)
; │ (bucket living-room)
; │ (chain garden)
; │ (frog garden))
; └ 'bucket
; => T
; (eq (cadr (assoc obj obj-locs)) 'living-room)
; │ └ '((whiskey living-room)
; │ (bucket living-room)
; │ (chain garden)
; │ (frog garden))
; └ 'frog
; => F
; (eq (cadr (assoc obj obj-locs)) 'living-room)
; │ └ '((whiskey living-room)
; │ (bucket living-room)
; │ (chain garden)
; │ (frog garden))
; └ 'chain
; => F
; => (WHISKEY BUCKET)
- 全てのオブジェクトを一覧表示する
(defun describe-objects (loc objs obj-loc)
(labels ((describe-obj (obj)
`(you see a ,obj on the floor.) ))
(apply #'append (mapcar #'describe-obj (objects-at loc objs obj-loc)))))
; 使用例:
(describe-objects 'living-room *objects* *objects-locations*)
; => (apply #'append (mapcar #'describe-obj (objects-at living-room *objects* *objects-locations*)))
; => (apply #'append (mapcar #'describe-obj '(WHISKEY BUCKET)))
; => (apply #'append '((YOU SEE A WHISKEY ON THE FLOOR.) '(YOU SEE A BUCKET ON THE FLOOR.)))
; => (append '(YOU SEE A WHISKEY ON THE FLOOR.) '(YOU SEE A BUCKET ON THE FLOOR.))
; => (YOU SEE A WHISKEY ON THE FLOOR. YOU SEE A BUCKET ON THE FLOOR.)
リストから引数の要素を探す。
(find 'keyword 'object :key #'func)
; 'objectの各要素に対してfuncを適用した値がkeywordと等しいとき、その要素を返す。
例:
(find 'y '((5 x) (3 y) (7 z)) :key #'cadr)
; => (3 Y)
引数をリストの先頭に追加する。
(push val '(list))
; => (val list)
下記の2つの処理は同等。
(push 7 *foo*)
(setf *foo* (cons 7 *foo*))
例:
(defparameter *foo* '(1 2 3))
(push 7 *foo*)
; => (7 1 2 3)
*foo*
; => (7 1 2 3)
assoc
関数は、先頭から探索して、最初に見つかった要素を返したら残りは無視する。
従って、assoc
とpush
を使用すると、alistの値を、さも変更したかのように見せられる。
例:
(defparameter *foo* '((whiskey living-room)
(bucket living-room)
(chain garden)
(frog garden)))
(assoc 'whiskey *foo*)
; => (WHISKEY LIVING-ROOM)
(push '(whiskey void) *foo*)
(assoc 'whiskey *foo*)
; => (WHISKEY VOID)
*foo*
; => ((WHISKEY VOID)
; (WHISKEY LIVING-ROOM)
; (BUCKET LIVING-ROOM)
; (CHAIN GARDEN)
; (FROG GARDEN))
この記事では、文字列表示、および、文字列操作において基本的な関数の使い方について説明する。
print
関数は下記の順で文字を表示する。
- 現在の表示位置が行頭でない場合、改行する
- 引数を表示する
- 空白を表示する
例:
(progn (print "this")
(print "is")
(print "a")
(print "test"))
; => "this"_
; "is"_
; "a"_
; "test"_
; (_ = 空白)
prin1
関数は下記の順で文字を表示する。
- 引数を表示する
例:
(progn (prin1 "this")
(prin1 "is")
(prin1 "a")
(prin1 "test"))
; => "this""is""a""test"
人間が見やすい形式で文字を表示する。
例:
(princ '3) ; => 3
(print '3) ; => 3
(princ '3.4) ; => 3.4
(print '3.4) ; => 3.4
(princ 'foo) ; => FOO
(print 'foo) ; => FOO
(princ '"foo") ; => foo
(print '"foo") ; => "foo"
(princ '#\a) ; => a
(print '#\a) ; => #\a
#\newline
を用いることで、文字列中に改行を入れられる。
(progn (princ "This sentence will be interrupted")
(princ #\newline)
(princ "by an annoying newline character."))
; => This sentence will be interrupted
; by an annoying newline character.
シンボルを含むリストを文字列に変換する。
(print1-to-string '(a b c d))
; => "(A B C D)"
(cons 'foo (list (prin1-to-string '(a b c d))))
; => (FOO "(A B C D)")
上記のprin1-to-string
関数の出力を、人間が読みやすい形式にしたもの。
(princ-to-string '(a b c d))
; => "(A B C D)"
(princ-to-string '(a b "c" d))
; => "(A B c D)"
prin1-to-string
関数やprintc-to-string
関数の一般形。
(write-to-string object :escape t)
; =
(print1-to-string object) ; escape文字が出力される
; 例
(write-to-string "abc" :escape t)
; => "\"abc\""
(write-to-string object :escape nil :readably nil) ; escape文字が出力されない
; =
(princ-to-string object)
; 例
(write-to-string "abc" :escape nil :readably nil)
; => "abc"
標準入力から得たものを返す。文字列からS式になる。
構文:
(read &optional input-stream eof-error-p eof-value recursive-p) ; => Object
- &optional
- ここから右の引数は任意。
- input-stream
- ストリーム指定。
デフォルト : 標準入力 - eof-error-p
- eof検出時の処理指定。
t : デフォルト。エラーを吐く。
nil : エラーを吐かない。 - eof-value
- eof検出時の返り値指定。
デフォルト : nil - recursive-p
- これがnil以外の場合、readを再帰呼び出しする。
デフォルト : nil
例:
(read)
; a
; => A
(read)
; 'a
; => 'A
(read)
; (a)
; => (A)
(read)
; "a"
; => "A"
; readの挙動について細かく見るための例を以下に示す。
(setq a "line 1
line2")
; 1の後ろに改行文字があることに注意
; => "line 1
; line2"
(read (setq input-stream (make-string-input-stream a)))
; => LINE
(read input-stream)
; => 1
(read input-stream nil nil)
; => LINE2
(read input-stream nil nil)
; => NIL
入力から1行ずつ得たものを文字列として返す。
NOTE: newlineからeofまで見る。
NOTE:
read-line
関数は、実行するたびに新規に文字列を生成する。
したがって、大量の行を読み込むとガベージコレクションの実行時間が長くなりすぎる。
read-sequence
を使用する方が実用的である。
しかし、read-sequence
は使用が面倒である。
構文:
(read-line &optional input-steam eof-error-p eof-value recursive-p) ; => line, missing-newline-p
- &optional
- ここから右の引数は任意。
- input-stream
- ストリーム指定。
デフォルト : 標準入力 - eof-error-p
- eof検出時の処理指定。
t : デフォルト。エラーを吐く。
nil : エラーを吐かない。 - eof-value
- eof検出時の返り値指定。
デフォルト : nil - recursive-p
- これがnil以外の場合、readを再帰呼び出しする。
デフォルト : nil - 返り値2: missing-newline-p
- 次の行があるか否かを表す。
read-line実行時にnewlineで終わっていたらnilを返す。
eofで終わっているか、第1引数がeof検出時の返り値ならtを返す。
例:
(read-line)
; abc
; => "abc";
; NIL
(read-line)
; "abc"
; => "\"abc\"";
; NIL
(setq a "line 1
line2")
; 1の後ろに改行文字があることに注意
; => "line 1
; line2"
(read-line (setq input-stream (make-string-input-stream a)))
; => "line 1";
; NIL
(read-line input-stream nil nil)
; => "line2";
; T
(read-line input-stream nil nil)
; => NIL;
; T
文字列から、それを入力とみなして返す。
構文:
(read-from-string string
&optional
eof-error-p
eof-value
&key
start
end
preserve-whitespace)
; => Objects, position
- eof-error-p
- eof検出時の処理指定。
t : デフォルト。エラーを吐く。
nil : エラーを吐かない。 - eof-value
- eof検出時の返り値指定。
デフォルト : nil - start end
- 文字列の左端と右端を指定する。
デフォルト : start = 0, end = nil - preserve-whitespace
- whitespaceを保持するか否かを指定する。
t : 保持する。
nil : デフォルト。保持しない。 - 返り値 position
- 読まれなかった文字列の最初の箇所を指す。
もし全体が読み込まれた場合、positionは文字数か、文字数 + 1を示す。
例:
(setq foo "(a b c d)")
; => FOO
(read-from-string foo)
; => (A B C D);
9
第1引数の文字を、第2引数の文字列の両端から除く。 第1引数の文字は1つずつ独立して評価される。 つまり、第1引数に"abc"が指定されている場合、 両端から「a」「b」「c」の文字のいずれにもマッチしない文字が現れるまで文字を取り除き続ける。
(string-trim "abc" "abcaakaaaabckabcbaaa")
; "abcaakaaaabckabcbaaa"
; ~~~~~kaaaabck~~~~~~~ (~の部分が除かれる)
; => kaaaabck
(string-trim '(#\Space #\e #\t) " trim me ")
; => "rim m"
NOTE: スペース、タブ、改行を削除対象に指定したい場合は下記のようにする。
; 下記のリストを指定する。
(string-trim '(#\Space #\newline #\tab #\IDEOGRAPHIC_SPACE) str)
第1引数の文字を、第2引数の文字列の左端から除く。
第1引数の文字を、第2引数の文字列の右端から除く。
文字列やリストを結合する。
(concatenate 'string "Hello " "world.")
; => "Hello World."
(concatenate 'list '(a b) '(c d) '(e f))
; => (A B C D E F)
NOTE: ただし、リストの場合はappend
を用いれば良い。
(append '(a b) '(c) '(d))
; => (A B C D)
与えられたテスト関数の結果によって値を置き換える。 (substitute : 〜を代用する。代理をする。)
構文:
(substitute-if new-item predicate sequence &key from-end start end count key)
; => result-sequence
- new-item
- 置換後の要素。
- predicate
- 述語。#'関数を描く要素に照らし合わせて真偽を判断する。
- sequence
- proper sequence(適切なシーケンス)
1次元配列や、popしていくと()になるリスト。
つまり、巡回リスト以外のリスト、および、最後のセルの参照先が非nil以外のリスト。 - count
- 置換する要素数の上限。
デフォルト : nil - from-end
- countがセットされていれば働く。右端から上限判定を行う。
デフォルト : false - start end
- 置換範囲は、startより大きく、end以下までの中に制限される。
デフォルト : start = 0, end = nil
例: :start 2 :end 5 なら (x x o o o x x)
例:
(substitute-if #\e #'digit-char-p "I'm a l33t hack3r!")
; => I'm a leet hacker!
引数の文字列から、両端の各々任意の文字数を取り除いた文字列を返す。 NOTE: endの一つ前までしか表示されないことに注意。
構文:
(subseq sequence start-position end-position)
; end-position は optional で、デフォルトではnil
例:
; "h e l l o w o r l d"
; 0 1 2 3 4 5 6 7 8 9 10
(subseq "hello world" 3)
; => "lo world"
(subseq 3 5)
; => "lo"
文字列の長さを返す。
(length "abcdefg")
; => 7
行頭に出力ストリームがない場合、newlineを出力する。
; 下記のようなプロンプトの状態になっているときを考える。
; 前の出力 CLISP>
(fresh-line)
; => プロンプトが下記のように改行される。
; 前の出力
; CLISP>
出力ストリームにnewlineを出力する。
関数内でfileをopenし、streamで制御する。 処理終了時には、fileをcloseする。
構文:
(with-open-file (my-stream
"file-name.txt"
:direction :output
:if-exsts :supersede)
(procedure))
- my-stream
- ストリーム名の指定
- "file-name.txt"
- open対象のファイル名
- :direction
- 方向(入出力)の指定
output : 出力
input : 入力 - :if-exsts
- ファイルが既存のとき、
:supersede : 置き換える - procedure
- ファイルオープン後の処理
例:
(with-open-file (my-stream
"file-name.txt"
:direction :output
:if-exsts :supersede)
(princ "Hello File!" my-stream))
; => princでmy-streamに投げた文字列を、そのmy-streamに関連付けたファイルに書き出す。
Lisp処理系起動時のディレクトリパス/カレントディレクトリパスを取得/設定する
truenameを使う。
(truename "./") ;=> #P"処理系を起動したディレクトリ"
(truename "./") ;=> #P"/users/username/"
; ディレクトリパスを取得する
(sb-posix:getcwd) ;=>#P"カレントディレクトリ"
; ディレクトリパスを設定する
(sb-posix:chdir #P"設定したいディレクトリ")
; ディレクトリパスを取得する
(ext:default-directory) ;=>#P"カレントディレクトリ"
; ディレクトリパスを設定する
(ext:cd #P"設定したいディレクトリ")
; ディレクトリパスを取得する
(extensions:default-directory) ;=>#P"カレントディレクトリ"
; ディレクトリパスを設定する
(setf (extensions:default-directory) #P"設定したいディレクトリ")
カレントディレクトリの設定後に (truename "./")
が返す値が変化しているかは処理系により異なる。
また、処理系によっては *default-pathname-defaults*
に起動時のディレクトリが入っている。
CLISPだと *default-pathname-defaults* ;=> #P""
のとおり空白だった。
SBCLだと、 *default-pathname-defaults*
を書き換えないとload
関数で参照する先が変わらない。