読者です 読者をやめる 読者になる 読者になる

レガシーコード生産ガイド

私に教えられることなら

PAIPメモ 2

CommonLisp

PAIP(実用Common Lisp)を読んでて気になって調べた事などをメモします。

今回はch3.11から。Common Lispの概要です。

多次元配列

1次元の場合

CL-USER> #1A(1 2 3)
#(1 2 3)

又は

CL-USER> #(1 2 3)
#(1 2 3)

2次元の場合

CL-USER> #2A((1 2 3) (4 5 6))
#2A((1 2 3) (4 5 6))

#nAのnで次元を表すようです。

アクセスはarefを使い、外側のインデックスから並べて取得します

CL-USER> (aref #2A((1 2 3) (4 5 6)) 0 1)
2

#(a b c)の略記法で入れ子にした場合、二次元配列に見えますが、(1 2 3)(4 5 6)はリストになるようです。

CL-USER> #((1 2 3) (4 5 6))
#((1 2 3) (4 5 6))

CL-USER>(aref #((1 2 3) (4 5 6)) 0)
(1 2 3)

CL-USER> (cdr (aref #((1 2 3) (4 5 6)) 0))
(2 3)

CL-USER> (every #'arrayp #((1 2 3) (4 5 6)))
NIL

CL-USER> (every #'listp #((1 2 3) (4 5 6)))
T

次数がオーバーした場合も同様にリストになるようです。

CL-USER> #1A((1 2 3) (4 5 6))
#((1 2 3) (4 5 6))

CL-USER> (aref #1A((1 2 3) (4 5 6)) 0)
(1 2 3)

CL-USER> (cdr (aref #1A((1 2 3) (4 5 6)) 0))
(2 3)

CL-USER> (every #'arrayp #1A((1 2 3) (4 5 6)))
NIL

CL-USER> (every #'listp #1A((1 2 3) (4 5 6)))
T

つまり配列表記の中はquoteと同じことになるんでしょうか。

試してみると、変数は評価されず、そのままシンボルになりました。

CL-USER> (defvar x 1)
X

CL-USER> #(x x x)
#(X X X)

CL-USER> (map 'vector #'symbolp #(x x x)) ; シンボルか調べる
#(T T T)

二次元配列ではなく、配列の配列として扱いたい場合は素直に配列表記を重ねればOKでした

CL-USER> #( #(1 2 3) #(4 5 6) )
#(#(1 2 3) #(4 5 6))

CL-USER> (aref #( #(1 2 3) #(4 5 6) ) 0)
#(1 2 3)

CL-USER> (every #'arrayp #( #(1 2 3) #(4 5 6) ))
T

データ型

123はt型、ということでt型かnull型かをifで判定してるかと思ったんですが

CL-USER> (typep 123 't)
T

CL-USER> (typep "abc" 't)
T

CL-USER> (typep nil 'null)
T

nilもt型でした

CL-USER> (typep nil 't)
T

subtypepで関係を調べてみます。PAIPでのsubtypepの説明だとTかNILを返すようですが、 SBCLだと二値返ってきました。CLtL1と2の違いでしょうか?

(describe 'subtypep)で調べてみると、(subtypep type1 type2)では

  • type1がtype2のサブタイプの場合はTとT
  • サブタイプでは無い、と判断できたらNILとT
  • 型が不明な場合はNILNIL

ということでした。

subtypepを使ってt型とnull型の関係、それとPAIPでの「型nullはsymbolとlistのサブタイプである」という説明から、 シンボルとリストとの関係も調べてみます。

CL-USER> (subtypep 't 'null)
NIL
T

CL-USER> (subtypep 'null 't)
T
T

CL-USER> (subtypep 'symbol 't)
T
T

CL-USER> (subtypep 'null 'symbol)
T
T

CL-USER> (subtypep 'list 't)
T
T

CL-USER> (subtypep 'null 'list)
T
T

ということで、null型はt型のサブタイプでした。 グラフはnull型 -> シンボル、リスト -> t型と菱型になっているのでしょうか。

ifは「nil以外は全てtと判断される」と説明されますが、 コード上でもそのようにnull型かどうかを判断してそうです。

print

printprin1princの違いは

  1. readで再度読める形で出力する
  2. 出力前に改行し、出力後にスペースを1つ出力する

という特徴について、

  • print 1と2
  • prin1 1
  • princ なし

です。printは「出力前に」改行するんですね。試してみます。

* (progn (print 'hoge) (print 'fuga))

HOGE 
FUGA 
FUGA

* (progn (print 'hoge) (prin1 'fuga))

HOGE FUGA
FUGA

prin1を使ったほうでは続けて出力されています。

また、printprin1princは全て引数をそのまま返します。 printデバッグする場合、引数の値が知りたければそのまま包めば良さそうです。

* (defun yo (x) (1+ x))
YO

* (yo 2)
3

* (yo (print 2))

2 
3
広告を非表示にする