データタイプ

AutoLISP のデータタイプ

AutoLISP では、LISP の基本的なデータタイプに加えて、AutoCAD を扱うために特別なデータを格納したオブジェクトが拡張されています。

データタイプを調べるには、type 関数を使います。

AutoLISP関数
(type item)
item:アトム、リスト
指定された項目のタイプを返します。
戻り値:シンボル

type 関数は以下の戻り値をシンボルの形で返します。

  戻り値(シンボル) 説明
LISP基本タイプ nil type 関数に nil を与えると、nil を返します。
INT 整数
REAL 実数
STR 文字列
SYM シンボル
LIST リスト
SUBR 関数 (AutoLISP 定義済みの関数、またはコンパイルされたユーザー定義の関数)
USUBR 関数 (ユーザー定義の関数でコンパイルされていないもの。コンパイルされると SUBR に変化するので注意が必要)
AutoLISP 拡張 ENAME 【図形名】
FILE ファイルディスクリプタ
PICKSET 選択セット
VL-CATCH-ALL-APPLY-ERROR エラーオブジェクト
VLA-object ActiveX オブジェクト
VARIANT バリアント型
SAFEARRAY セーフ配列
EXRXSUBR 外部 ObjectARX アプリケーション
PAGETB 関数ページングテーブル

type 関数の返り値が、シンボルであることに留意してください。タイプの確認は以下のようになります。「T」は真を表す定義済みのシンボルです。「nil」はここでは偽を表す定義済みのシンボルです。

_$ (= (type 1) 'INT) ⏎
T
_$ (/= (type 1) 'INT) ⏎
nil

シンボルの引数や返り値

type 関数の返り値がシンボルであることに、違和感を覚えるかもしれません。他の言語ならば数字や文字列、あるいはプリプロセッサで置き換えられる分かりやすい定義済みの定数、あるいは列挙型の値などを使用するところを、変数名のような扱いのシンボル名を使用しています。LISP では、シンボルの扱いが容易で、シンボル名は他と重複のないユニークなもののため、シンボル名を定義済みの定数のようなデータとして利用するプログラミングスタイルが見受けられます。数字より可読性があり、文字列と異なり大文字小文字の区別が無いので気軽に使うことができます。そして、シンボルの値を変更しない限り適当なシンボル名を自由に使っても問題は起こりません。例えばシンボル LIST は、重要な関数の名前でもありますが、シンボル名 LIST だけを扱うなら、中に何が代入されていようとかまわないことになります。

次の例は、動物を表すシンボルを引数で受け取り、鳴き声を表す文字列を返します。

(defun cryOfAnimal (animal)
    (cond
        ((= animal 'rooster) "cock-a-doodle-doo")
        ((= animal 'duck) "quack")
        ((= animal 'cow) "moo")
        ((= animal 'dog) "bow-wow")
        ((= animal 'cat) "meow")
        ((= animal 'mouse) "squeak")
    )
)

関数のテスト結果は以下のとおりです。

_$ (cryOfAnimal 'rooster) ⏎
"cock-a-doodle-doo"
_$ (cryOfAnimal 'dog) ⏎
"bow-wow"
_$ (cryOfAnimal 'mouse) ⏎
"squeak"

整数

整数をプログラムに記述するには、小数点を含まない数字として書きます。同様に【コンソール】などに表示される場合は、小数点を含まない数字で表されます。

0
10
-100

内部的には32 ビットの符号付き整数として扱われ、最上位のビットが正負を表します。範囲は2,147,483,647 から-2,147,483,648 までです。

整数 ビット表現
2147483647 01111111 11111111 11111111 11111111
256 00000000 00000000 00000001 00000000
2 00000000 00000000 00000000 00000010
1 00000000 00000000 00000000 00000001
0 00000000 00000000 00000000 00000000
-1 11111111 11111111 11111111 11111111
-2 11111111 11111111 11111111 11111110
-256 11111111 11111111 11111111 00000000
-2147483648 10000000 00000000 00000000 00000000

関数の引数に、範囲を超える整数を入力すると、AutoLISP は自動的に整数を実数に変換します。次の関数は、与えられた値をそのまま返す関数です。

(defun overFlowTest (value)
    value
)

この関数に、以下のように整数の範囲を超える値を与えると自動的に実数に変換されます。

_$ (overFlowTest 5000000000) ⏎
5.0e+009

なお、本来なら上の例で、整数の下限-2,147,483,648 の場合は整数として扱われるはずですが、この自動変換では実数になります。理由は不明です。

また、計算の結果が整数の範囲を超えた場合は、間違った値を返します。次の例は整数の最大値に1 を加えることで意図的にオーバーフローを行っています。

_$ (1+ 2147483647) ⏎
-2147483648

整数の有効範囲

Common Lisp の解説書には、LISP の整数には範囲が無い、メモリーの許す限り大きな数が扱えると書いてあるかもしれませんが、AutoLISP は異なります。

実数

実数をプログラムに記述するには、小数点を含んだ数字として書きます。

0.0
10.0
-100.0
25.35678

また、以下のような e を用いて指数表現で書くこともできます。

1.0e+010
-2.14748e+009
1.23457e-005

e の前の数値を仮数部と呼び、e の後の正負の整数が指数部です。表される実数は「仮数×10 指数」の形で表現されます。「5.0e+012」の場合は5.0×1012 を表します。

実数は倍精度浮動小数点形式で格納されます。内部的に数値を表すのに64 ビットの領域を使用する形式で、仮数と指数で表現されています。領域が限られるので、数値によっては仮数の丸めが行われます。このことは、実用上は問題が無いが、わずかな不確かさ、いわゆる誤差を含むことを示しています。

なお、実数を【コンソール】などに表示する場合は、仮数部が丸められることがありますが、内部的な精度をそのまま表すものではありません。

有理数(分数)、複素数

これらの他に Common Lisp の解説書では、LISP の有理数(分数)や複素数について述べられていますが AutoLISP にはありません。

type 関数を用いる以外に、整数や実数といった数値であるかを調べる関数があります。

AutoLISP関数
(numberp item)
item:アトム、リスト
指定された項目が整数または実数か調べます。
戻り値:nil、またはnil 以外

引数が、整数か実数でない場合は nil を返します。

_$ (numberp 1) ⏎
T
_$ (numberp 1.0) ⏎
T
_$ (numberp "AutoCAD")⏎
nil
_$ (numberp 'quote) ⏎
nil

テスト関数

LISP の基本関数の中で「~であるかを調べる」ものは、末尾に p が付いているものが多くあります。p は述語を意味する predicate の p であると言われています。「レモンは黄色い」の「黄色い」が述語です。

数学の論理学の分野では、「xは動物である」などという、xに「馬」や「草」を代入して真偽が判定されるものを、命題関数(propositional function)または述語と呼びます。

⽂字列

文字列をプログラムで記述するには、一連の文字を”(ダブルクォーテーション)で囲みます。日本語も扱えますが、文字列関係の関数はエラーにならないだけで特に文字コードに配慮したつくりにはなっていません。

“AutoCAD”
“プログラミング”

¥(円記号)と組み合わせることにより、ダブルクォーテーションで囲んだ文字列の中に制御文字(エスケープコード) を含めることができます。念のため、表向きはエスケープコードは 2 文字など複数の文字で表されていますが、内部的には 1 文字を意味します。

コード エスケープされた文字 10 進数 8 進数
¥¥ ¥ 92 ¥134
¥" " 34 ¥42
¥e エスケープ(ESC キーが押された時のコード) 27 ¥33
¥n 改行文字(次の行の先頭に改行する) 10 ¥12
¥r 復帰文字(現在の行の先頭に戻る) 13 ¥15
¥t タブ 9 ¥11
¥nnn 8 進コードが nnn の文字(例:「?」 → ¥77、「A」 → ¥101)
AutoLISP プログラムでは、文字コードを記述したい場合は 10 進数または、このエスケープコードを使って8 進数で表します。2 進数や 16 進数で記述することはできません。
   

内部的には、使用する文字列ごとに適切な大きさのメモリーが自動的に確保され、文字列が編集されれば、そのつど新しい文字列用のメモリーが確保されます。使用されなくなった文字列のメモリーは LISP のガーベージコレクションの仕組みで自動的に回収されます。

char 型

文字列の他に、一文字を表す文字型は特別には用意されていません。一文字を扱う場合は、整数として表すか、文字列の先頭といったかたちで扱います。一文字を整数で表した場合、 ASCII 文字コードは一文字 8 ビットの大きさですが、AutoLISP では整数の大きさ 32 ビットの大きさを消費します。この差は、文字を本格的に扱う特殊なプログラムならともかく、AutoCAD で使うプログラム上では問題となることは無いでしょう。

シンボル

値を代入できるのがシンボルでしたが、シンボル自体も他のシンボルに代入できるので、データタイプの一つです。

type 関数を用いる以外に、シンボルであるかを調べる関数が用意されています。

AutoLISP関数
(vl-symbolp object)
object : アトム、リスト
指定されたオブジェクトがシンボルかどうかを調べます。
戻り値:nil、またはnil 以外

以下の実行例は、変数名や関数名はシンボルであり、数値や文字列はシンボルではないことが分かります。リストもシンボルではありません。

_$ (vl-symbolp 'a)⏎
T
_$ (vl-symbolp 'quote) ⏎
T
_$ (vl-symbolp 10.0) ⏎
nil
_$ (vl-symbolp "AutoCAD")⏎
nil
_$ (vl-symbolp '(1 2 3)) ⏎
nil

リスト

以上の基本的な各種アトムに加えて、LISP のリストも基本データタイプの一つになります。

type 関数を用いる以外に、リストであるかを調べる関数が用意されています。

AutoLISP関数
(listp item)
item : アトム、リスト、またはドットリスト
指定された項目がリストかどうかを調べます。
戻り値 : nil、またはnil 以外

listp 関数は、項目がリストかどうか調べます。引数が nil の時は、空のリストと同じことなので listp 関数は T を返します。またドットリストもリストと判定されます。

_$ (setq a '(1 2 3 4 5)) ⏎
(1 2 3 4 5)
_$ (listp a) ⏎
T
_$ (setq a nil) ⏎
nil
_$ (listp a) ⏎
T

リストでは無いと判定されるのは、数値や文字列、及び変数名や関数名といったシンボルそのものです。

_$ (listp 1) ⏎
nil
_$ (listp "AutoCAD")⏎
nil
_$ (setq a 1) ⏎
1
_$ (listp a) ⏎
nil
_$ (listp 'a) ⏎
nil
_$ (listp 'quote) ⏎
nil
AutoLISP関数
(vl-consp list-or-cons-object)
list-or-cons-object : リスト、またはドットリスト
リストがnil かどうかを調べます。
戻り値 : nil、またはnil 以外

vl-consp 関数の意味は引数がコンスセルかどうかを判定します。コンスセルについてはリストの所で別途説明します。具体的に真となる場合は、空リストでないリストかドットリストだった場合です。引数が nil の場合、listp 関数はリストと判定しますが、vl-consp 関数は nil となるのが異なります。listp 関数と null 関数を組み合わせたものと理解できます。

_$ (vl-consp '(1 2 3 4)) ⏎
T
_$ (vl-consp '("name" . "apple"))⏎
T
_$ (vl-consp nil) ⏎
nil
_$ (vl-consp '())⏎ ;空のリスト(nil と同義)
nil
_$ (vl-consp '(nil)) ⏎
T
_$ (vl-consp 1)
nil
    
AutoLISP関数
(atom item)
item : アトム、リスト
指定された項目がアトムかどうかを調べます。
戻り値 : nil、またはnil 以外

リストで無いものがアトムです。atom 関数は、listp 関数とは逆の動作をします。ただし、nil は空のリストを表すとともに、アトムでもあります。

_$ (atom '(1 2 3 4 5)) ⏎
nil
_$ (atom nil) ⏎
T

アトムであると判定されるのは、数値や文字列、及び変数名や関数名といったシンボル、関数自身、その他のオブジェクトです。

_$ (atom 1) ⏎
T
_$ (atom "AutoCAD")⏎
T
_$ (setq a 1) ⏎
1
_$ (atom a) ⏎
T
_$ (atom 'a) ⏎
T
_$ (atom 'quote) ⏎
T
_$ (atom quote) ⏎
T

listp - vl-consp - atom

listp 関数と atom 関数はほぼ逆の意味になりますが、どちらも nil は真と判定されるため完全に逆とはなりません。atom 関数の逆の意味になるのは、vl-consp 関数となります。

関数

実数や文字列と同じように、関数を変数すなわちシンボルに代入して扱うことができることが関数型言語の特徴です。ここで代入される関数とは、関数名を別として関数の内容を表す部分です。関数オブジェクトと呼んでイメージした方が良いでしょうか。そして代入されたシンボル名が関数名として扱われます。関数は LISP のアトムです。

ユーザー定義の関数型タイプを表す USUBR はコンパイルされていない LISP ソースから定義された関数です。作業が進んで、既にコンパイルされたファイルから定義されると SUBR に型が変わります。そのため、関数を型判定している場合は、実際は USUBR と SUBR の両方を確認する必要が出てきます。

外部 ObjectARX アプリケーション由来の関数 EXRXSUBR であるものは、そういったプログラムを作っていない限りめったに扱うことはありませんが、AutoLISP の標準関数のようでいて実は EXRXSUBR であるものがあります。例えば startapp 関数です。これは、メモ帳などの外部のプログラムを AutoLISP から起動する関数ですが、【独自の名前空間 VLX アプリケーション】を作成すると、「(vl-arx-import 'startapp)」と宣言して関数をインポートしなければ使えなくなるので注意が必要です。EXRXSUBR である関数は、次のコードを実行すると一覧が得られます。

(vl-remove-if-not
  (function (lambda (symbol) (= (type (vl-symbol-value symbol)) 'EXRXSUBR))
  )
  (atoms-family 0)
)

図形名

AutoCAD の図面データベース内で、図形やレイヤーなどのオブジェクトをハンドリングするためのデータタイプです。【図形名】はファイルを開き直すと、読み込まれたメモリーによって以前とは異なる値が割り当てられます。【図形名】から ActiveX の VLA オブジェクトに変換することができる関係にあります。また、図面を開き直しても変わらない ID として図形が持っているデータの中の「ハンドル」が利用できます。

【図形名】は atom 関数で判定するとアトムである T が返りますが、厳密に LISP においてのアトムなのかそうでないかははっきりしません。二つのシンボルに、同一の図形の【図形名】が代入されている場合、それが同一であるかを判別する際は、eq 関数か equal 関数を使用してください。= 関数では、正しく判別できないので注意してください。

_$ (setq ename1 (car (entsel))) ⏎   ; 図形Aを選択
<図形名: 7ffff7afca0>
_$ (setq ename2 (car (entsel))) ⏎   ; 同じ図形Aを選択
<図形名: 7ffff7afca0>
_$ (eq ename1 ename2) ⏎
T
_$ (equal ename1 ename2) ⏎
T
_$ (= ename1 ename2) ⏎
nil

VLA オブジェクトはリスト的な扱いをされますから、【図形名】もリストのようなものと考えておいた方がいいでしょう。

ファイルディスクリプタ

ファイルディスクリプタは、AutoLISP の open 関数で開いたファイルを表すオブジェクトです。

選択セット

AutoLISP 特有のオブジェクトです。【選択セット】は、ユーザーによって選択された図形をリストアップしたものです。

【選択セット】をシンボルに代入すると、シンボルはポインターで【選択セット】オブジェクトを参照している状態になります。よって、関数の引数に【選択セット】を与えてもポインターのコピーが作られるだけで、【選択セット】オブジェクトのコピーが作成されるわけではありません。【選択セット】はアトムとして扱われます。そして、二つの【選択セット】の内容が等しいかどうかは、内容を一つ一つ取り出して検査しなければなりません。

エラーオブジェクト

AutoLISP の例外を扱うオブジェクトです。エラーオブジェクトについては、制御構造の項で扱います。

その他のデータタイプ

VLA オブジェクト、バリアント型とセーフ配列は、ActiveX 対応関数を使用する際に必要となるものです。これらのデータタイプは関数を使ってデータを変換して生成しなければならず、一手間がかかります。そのため AutoLISP プログラムに慣れてから使ってみる方がよいでしょう。これらについては、稿を改めて説明します。