データ型

データ型
プログラミング言語論 — 6-1
データ型 (data type) とは
プログラミング言語において非常に重要
• ハードウェアからの抽象
• 記述性,可読性
• 信頼性,安全性
– 型検査による型誤りの検出
• 効率
– 型情報を用いた最適化
プログラミング言語論 — 6-2
型検査
型誤りを検出する
• 静的な型検査
– コンパイル時,リンク時に検出
• 動的な型検査
– 実行時に検出
– 配列の添字範囲チェック等
• 強く型付けされた言語
– 型誤りが必ず検出される
プログラミング言語論 — 6-3
変数の型宣言
• 明示的な型宣言
• 暗黙的な型宣言
– ML, Haskell, Scala 等の言語での型推論
• 変数に型のない言語
– Lisp, Prolog や多くのスクリプト言語では,変
数は型を持たない
(setq x 123)
(+ x 456)
(setq x "abc")
(+ x 456)
; エラー
プログラミング言語論 — 6-4
型の構造
• 部分型 (部分範囲型)
subtype INDEX is INTEGER range -10..10;
type DAY is (MON, TUE, WED, THU, FRI, SAT, SUN);
subtype WEEKDAY is DAY range MON..FRI;
• 派生型
type YEAR is new INTEGER;
• 多相型
– 型をパラメータに取る型
– Java 5 の Generics (parameterized types)
プログラミング言語論 — 6-5
Java 5 の Generics
• Java 2 以前
ArrayList の要素は任意の Object
ArrayList list = new ArrayList();
list.add(new Integer(123));
Integer x = (Integer)list.get(0);
/* cast が必要 */
• Java 5
ArrayList の要素の型を型パラメータで指定
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(new Integer(123));
Integer x = list.get(0);
/* cast は不要 */
プログラミング言語論 — 6-6
データ型の種類
• 原始型 (primitive types)
–
–
–
数型,文字型
論理型
列挙型 (enum)
• 集成型 (aggregate types),派生型
–
–
–
–
–
–
–
–
配列型
文字列型
レコード型 (構造体,struct)
可変レコード型 (共有体,union)
集合型
リスト型
ポインタ型
関数型
プログラミング言語論 — 6-7
原始型
• 数型
• 文字型
– C や Java では整数として取り扱われる
– Java では Unicode を用いる
• 論理型
– C には存在しない.if (0 < x < 9) 等の誤
りが生じる
• 列挙型 (enum)
– C では整数として取り扱われるので安全でない
– Java 5 で typesafe な列挙型が導入された
プログラミング言語論 — 6-8
列挙型
C 言語では enum は単なる整数定数
enum day { MON, TUE, WED, THU, FRI, SAT, SUN };
int main() {
enum day d;
int x;
d = SUN;
x = SUN;
printf("%d\n", d);
}
/* エラーとならない */
/* 6 が表示される */
Java 5 ではオブジェクト
enum Day { MON, TUE, WED, THU, FRI, SAT, SUN };
public static void main(String[] args) {
Day d;
int x;
d = Day.SUN;
x = Day.SUN;
/* エラー */
System.out.println(d); /* SUN が表示される */
}
プログラミング言語論 — 6-9
配列型
• 要素の型はすべて同じ
• Pascal では,整数や列挙型の部分範囲をインデッ
クスとして利用可能
type a = array [-5..5] of integer;
type b = array [MON..FRI] of integer;
• C では配列の大きさはコンパイル時に定数でな
ければならないが,Java では実行時に配列の大
きさを指定可能
• C では,配列名は配列の先頭要素へのポインタ
値を表す (sizeof の引数となる時等を除く).
int a[SIZE];
p = a;
/* p = &a[0] と同じ */
プログラミング言語論 — 6-10
文字列型
• C の場合
– 文字の配列として実現されており,プログラ
マが最大長を宣言する必要がある (動的にメ
モリ領域を確保することは可能)
– 文字列終端に nul 文字 (’\0’) を置く必要が
ある
• Java の場合
– オブジェクトとして実現されており,処理系
が自動的に領域管理を行う
– 文字列長も処理系が管理
プログラミング言語論 — 6-11
レコード型
• 直積構造
• C では構造体 (struct)
struct t {
int i;
float f;
int a[SIZE];
};
struct t s;
struct t *p;
s.i = 123;
s.a[0] = 456;
p = &s;
p->i = 123;
p->a[0] = 456;
/* p->i は (*p).i と同じ */
プログラミング言語論 — 6-12
可変レコード型 (1)
• メンバがメモリー上で同一領域を占める
• C では共用体 (union)
union t {
int i;
float f;
};
union t u;
u.i = 123;
printf("%d\n", u.i);
printf("%f\n", u.f);
/* これは一種の型誤り */
プログラミング言語論 — 6-13
可変レコード型 (2)
• どのメンバを利用しているかを表すタグフィー
ルドを付けて用いることが多い
• タグの値を動的に検査する言語もある
enum tag { TAG_INT, TAG_FLOAT };
struct t {
enum tag tag;
union {
int i;
float f;
} value;
} s;
s.tag = TAG_INT;
s.value.i = 123;
if (s.tag == TAG_INT) {
printf("%d\n", s.value.i);
}
プログラミング言語論 — 6-14
集合型 (1)
• Pascal で利用できるが,集合の要素数は 1 語の
ビット数に制限される
• ビット演算に対応する
Pascal での例
type COLOR = set of (RED, GREEN, BLUE);
var C, C1, C2: COLOR;
C1 := [RED,GREEN];
C2 := [GREEN,BLUE];
C := C1 + C2;
/* 和集合 */
C := C1 * C2;
/* 積集合 */
C := C1 - C2;
/* 差集合 */
if RED in C1 then
/* 要素 */
.....;
プログラミング言語論 — 6-15
集合型 (2)
C で同様の事を実現する例
enum { RED=01, GREEN=02, BLUE=04 };
int c1, c2, c3;
c1 = RED | GREEN;
c2 = GREEN | BLUE;
c = c1 | c2;
c = c1 & c2;
c = c1 & ~c2;
if ((RED & c1) != 0)
.....;
/*
/*
/*
/*
和集合 */
積集合 */
差集合 */
要素 */
プログラミング言語論 — 6-16
リスト型 (1)
• 関数型言語,論理型言語等で用いられる
• Lisp では以下の関数を用いる
– (car x): リスト x の先頭要素
– (cdr x): リスト x の先頭要素を除いた残り
のリスト
– (cons x y): リスト y の先頭に x を追加し
たリストを作成
プログラミング言語論 — 6-17
リスト型 (2)
(setq x ’(a (b c) d))
(car x)
(cdr x)
(cdr (cdr x))
; a
; ((b c) d)
; (d)
(cdr (cdr (cdr x)))
(car (cdr x))
; ()
; (b c)
(cons ’e x)
(car (cons ’e x))
(cdr (cons ’e x))
; (e a (b c) d)
; e
; (a (b c) d)
• サスマンほか著 「計算機プログラムの構造と解釈 第二版」,
ピアソン・エデュケーション,4600 円
プログラミングの様々な概念を Scheme を用いて解説した名著
プログラミング言語論 — 6-18
ポインタ型
• ハードウェアでのアドレスに相当する
• 動的なデータ領域や再帰的な構造を表現するた
めに用いられる
• C ではスタック上のオブジェクトを指すことが
可能なため,特にバグを生じやすい (dangling
pointer)
char *readline() {
char buffer[1000];
fgets(buffer, 1000, stdin);
return buffer;
}
プログラミング言語論 — 6-19
関数型
• C では関数へのポインタを利用
• Lisp ではラムダ式で名前のない関数を記述できる
(defun inc (x) (+ x 1))
(mapcar ’inc ’(1 2 3))
=> (2 3 4)
(mapcar (lambda (x) (- x 1)) ’(1 2 3))
=> (0 1 2)
• Scheme では変数の束縛環境を含めた関数クロー
ジャと呼ばれるものを利用できる
(define (make-func y) (lambda (x) (+ x y)))
(define func (make-func 1))
(func 123)
=> 124
プログラミング言語論 — 6-20
多相型について (1)
• 単相 (monomorphic)
関数,手続き,オペランドが一つの型だけを持つ
• 多相 (polymorphic)
関数,手続き,オペランドが一つ以上の型を持つ
– アドホック (ad-hoc polymorphism)
真の多相ではない
– 汎用 (generic polymorphism)
無限の型に対して働く
プログラミング言語論 — 6-21
多相型について (2)
• アドホック
– 多重定義 (overloading)
それぞれの型に対して定義されている.例え
ば,整数,浮動小数点数,文字列に対する+の
演算.強制型変換が行われることもある
– 継承 (inheritance polymorphism)
派生型 (subtype) が基底型 (supertype) の演
算を継承する.オブジェクト指向言語で用い
られる
プログラミング言語論 — 6-22
多相型について (3)
• 汎用
– パラメータ付き (parametric polymorphism)
型のパラメータを持つ
∗ C++の template
与えられた型ごとに別々の目的コードが
生成される
∗ Java の generics
単一の目的コードが生成される
プログラミング言語論 — 6-23
多相型の例 (1)
Java 5 での利用の例
List<Integer> list = new ArrayList<Integer>();
list.add(new Integer(123));
Integer x = list.get(0);
/* cast 不要 */
Iterator<Integer> iter = list.iterator();
while (iter.hasNext()) {
Integer y = iter.next(); /* cast 不要 */
}
Map<String,Integer> map =
new HashMap<String,Integer>();
map.put("abc", new Integer(456));
Integer z = map.get("abc");
プログラミング言語論 — 6-24
多相型の例 (2)
Java 5 での定義の例
class Pair<T1,T2> {
T1 first;
T2 second;
public Pair(T1 f, T2 s) {
first = f;
second = s;
}
public T1 first() {
return first;
}
}
public T2 second() {
return second;
}
Pair<String,Integer> pair =
new Pair<String,Integer>("abc", new Integer(123));
String x = pair.first();
プログラミング言語論 — 6-25
抽象データ型 (1)
• 型をその構造によって定義するのでなく,操作に
よって定義する
• インターフェイスと実現を分離
– インターフェイスは可能な操作を規定
– 実現部分は隠蔽 (encapsulation) する
• C では,インターフェイス部分をヘッダファイル
に分離して記述するのが一般的
• Java では,分離するのではなく,公開部部分に
public 修飾子,隠蔽部分に private 修飾子を用
いる
プログラミング言語論 — 6-26
抽象データ型 (2)
• スタック
– push, pop ができる物
– 配列,リスト等の何で実現されているかは隠す
プログラミング言語論 — 6-27