4 ソフトウェア工学 Software Engineering 抽象データ型 ABSTRACT DATA TYPE.

Slides:



Advertisements
Similar presentations
プログラミング論 第八回数字の計算,整数の入出力. 本日の内容 前回の課題(続き) 前回の課題(続き) 数字の計算をする 数字の計算をする – 加減乗除を行う – インクリメント演算子とデクリメン ト演算子.
Advertisements

プログラミング言語論 第10回(演習) 情報工学科 木村昌臣   篠埜 功.
プログラミング演習II 2004年11月 30日(第6回) 理学部数学科・木村巌.
京都大学情報学研究科 通信情報システム専攻 湯淺研究室 M2 平石 拓
プログラミングパラダイム さまざまな計算のモデルにもとづく、 プログラミングの方法論 手続き型 関数型 オブジェクト指向 代数 幾何.
プログラミング入門2 第10回 構造体 情報工学科 篠埜 功.
基礎プログラミングおよび演習 第9回
岩村雅一 知能情報工学演習I 第9回(C言語第3回) 岩村雅一
型宣言 (Type Declarations)
第2回:Javaの変数と型の宣言 プログラミングII 2007年10月2日.
12: コマンドライン引数 C プログラミング入門 総機1 (月1) Linux にログインし、以下の講義ページ を開いておくこと
型宣言(Type Declarations)
情報工学概論 (アルゴリズムとデータ構造)
12: コマンドライン引数 C プログラミング入門 基幹7 (水5) Linux にログインし、以下の講義ページ を開いておくこと
条件式 (Conditional Expressions)
項書換え系(1) 代数的仕様と項書換え Term Rewriting Systems(1)
プログラミング論 II 電卓,逆ポーランド記法電卓
ML 演習 第 7 回 新井淳也、中村宇佑、前田俊行 2011/05/31.
岩村雅一 知能情報工学演習I 第8回(後半第2回) 岩村雅一
プログラミング言語入門 手続き型言語としてのJava
PROGRAMMING IN HASKELL
プログラミング言語論 第3回 BNF記法について(演習付き)
PROGRAMMING IN HASKELL
第二回 VB講座 電卓を作ろう.
プログラミング応用 printfと変数.
プログラミング言語入門.
プログラミング演習I 2003年5月7日(第4回) 木村巌.
岩村雅一 知能情報工学演習I 第9回(後半第3回) 岩村雅一
岩村雅一 知能情報工学演習I 第8回(C言語第2回) 岩村雅一
P n ポインタの基礎 5 q m 5 7 int* p; int 型の変数を指すポインタ int* q; int 型の変数を指すポインタ int n=5, m=7; int 型の変数 int array[3]; int* pArray[3]; p = &n; ポインタにアドレスを代入しているのでOK.
オブジェクト指向言語論 第八回 知能情報学部 新田直也.
岩村雅一 知能情報工学演習I 第9回(後半第3回) 岩村雅一
2013年度 プログラミングⅡ ~ 計算してみよう ~.
2015年度 プログラミングⅡ ~ 計算してみよう ~.
岩村雅一 知能情報工学演習I 第9回(C言語第3回) 岩村雅一
フロントエンドとバックエンドのインターフェース
情報処理Ⅱ 第2回:2003年10月14日(火).
C言語 はじめに 2016年 吉田研究室.
岩村雅一 知能情報工学演習I 第9回(後半第3回) 岩村雅一
プログラミング演習I 2003年4月30日(第3回) 木村巌.
地域情報学 C言語プログラミング 第2回 変数・配列、型変換、入力 2017年10月20日
文字列へのポインタの配列 static char *lines[MAXLINES]; lines[0] NULL
12: コマンドライン引数 C プログラミング入門 基幹7 (水5) Linux にログインし、以下の講義ページを開いておく こと
PROGRAMMING IN HASKELL
情報処理Ⅱ 第2回 2005年10月14日(金).
情報処理Ⅱ 第2回 2006年10月13日(金).
アルゴリズムとデータ構造1 2009年6月15日
情報処理Ⅱ 2006年11月24日(金).
情報処理Ⅱ 第7回 2004年11月16日(火).
情報処理Ⅱ 2005年10月28日(金).
PROGRAMMING IN HASKELL
標準入出力、変数、演算子、エスケープシーケンス
プログラミング基礎a 第3回 C言語によるプログラミング入門 データ入力
オブジェクト指向言語論 第二回 知能情報学部 新田直也.
岩村雅一 知能情報工学演習I 第8回(後半第2回) 岩村雅一
岩村雅一 知能情報工学演習I 第8回(C言語第2回) 岩村雅一
アルゴリズムとデータ構造 2010年6月17日
PROGRAMMING IN HASKELL
Haskell Programming Language
データ構造と アルゴリズムI 第三回 知能情報学部 新田直也.
情報処理Ⅱ 第2回 2004年10月12日(火).
情報処理Ⅱ 2005年11月25日(金).
プログラミング基礎a 第5回 C言語によるプログラミング入門 配列と文字列
情報処理Ⅱ 小テスト 2005年2月1日(火).
JavaScript    プログラミング入門 2-3 式と演算子 2006/10/12 神津 健太.
printf・scanf・変数・四則演算
数理論理学 最終回 茨城大学工学部情報工学科 佐々木 稔.
情報処理Ⅱ 2006年10月27日(金).
12: コマンドライン引数 C プログラミング入門 基幹2 (月4) Linux にログインし、以下の講義ページ を開いておくこと
岩村雅一 知能情報工学演習I 第9回(C言語第3回) 岩村雅一
Presentation transcript:

4 ソフトウェア工学 Software Engineering 抽象データ型 ABSTRACT DATA TYPE

データ抽象 (data abstraction) ■目的:データ構造を(実装に依存せずに)抽象的に定義 ■方法:データにアクセス(read, write)する関数の仕様のみ      を記述 スタック(stack)の例 push(D, S) pop(S) top(S)=C

スタックの実装のいろいろ 4 C B A 5 C B A C 3 4 2 3 B 1 2 3 3 1 A 実装1 実装2 実装3 配列 配列 3 1 A スタック ポインタ 配列 スタック ポインタ 配列 結合リスト (array) (linked list) (stack pointer) 実装がいろいろある 複数の開発者間で再利用しにくい 実装は今後も変化する

インタフェースと実装の分離 API: Application Programming Interface (interface) スタック インタフェースを抽象的に理解 インタフェースと実装の分離 API: Application Programming Interface (interface) push(D, S) pop(S) top(S) インタフェース                       分   離 アプリケーション プログラマー 実  装 スタック (implementation) 実装は隠蔽 システムプログラマー

インタフェースと実装の分離の利点 情報隠蔽,抽象化:細部を隠してわかりやすくする 契約(インタフェース)を変えずに実装を進化させられる アプリケーション(利用者)とシステム(提供者)の分離

データ型の概念(初心者向け) プログラミングで使用される概念で、データの種類のこと. 文字型・整数型・実数型・文字列型などがある. 【例】 基本データ型 【例】 ユーザー定義型 boolean 真偽値( 1ビット) char 文字(16ビット) byte 整数( 8ビット) short 整数(16ビット) int 整数(32ビット) long 整数(64ビット) float 浮動小数点数(32ビット) double 浮動小数点数(64ビット) String 文字列 Stack スタック List リスト

古い考え方:データ型 = データの集合 int = 全32ビットデータの集合 ? このデータのデータ型は? 32ビット 01000001011000000000000000000000 int 1,097,072,640 ? float 14.25 ? String ”槍” とNULL文字 ? その他 白黒画像の一部 ? ●データを見ても,データ型はわからない  なぜならば: データを見ても,データ型の「集合」に属するか判断不可能 データ型は単なるデータの集合としては定義できない

現代的な考え方: データ型=データの集合+演算・処理の集合 01000001011000000000000000000000 int CPUが整数演算に使うなら 1,097,072,640 float CPUが実数演算に使うなら 14.25 String CPUが文字列処理に使うなら ”槍” とNULL文字 その他 CPUが画像処理に使うなら 白黒画像の一部 高級プログラミング言語では,データの種類ごとに 適用可能な演算や入出力等の処理を混在させず,固定 データ型=〈データの集合,演算や処理の集合〉 intの印字 int = 〈全32ビットデータの集合,{+, -, *, / , putIntなど}〉 float = 〈全32ビットデータの集合,{+, -, *, / , putFloatなど}〉 この考えがやがて オブジェクト に発展

抽象データ型の考え方:データ型=関数の集合 データ型の定義に現れるデータ,演算,処理は, 理論的には,すべて関数から定義できると考える ■ nat (自然数)型の例 4 は,s(s(s(s(0( ))))) で定義される データ 0( ) : 0 を返す関数(zero function) s(x) : xのつぎの数(x+1)を返す関数(successor function) 演算 plus(x, y): x+yを返す関数 times(x, y): x*yを返す関数 処理 putInt(x): xを10進数として印字する作用をもつ「関数」 データの具体的な表現を示さず,関数だけを示している ソフトウェアの複雑性を抑制する一般的な手段のひとつ 抽象データ型 (abstract data type)

代数的仕様記述 形式的仕様記述 代数的仕様記述 plus(0, y) = y plus(s(x), y) = s(plus(x, y)) ■ nat (自然数) の例 データ 0 : 0 を返す関数(zero function) s(x) : xのつぎの数(x+1)を返す関数(successor function) 演算 plus(x, y): x+yを返す関数 (0( ) を単に 0 と書くことにする) 関数の定義(仕様)を日本語で(非形式的に)記述 数学的(形式的)に記述 形式的仕様記述 (formal) (formal specification) 関数の間の関係(制約)を等式によって記述 代数的仕様記述 (形式的仕様記述の一種) plus(0, y) = y plus(s(x), y) = s(plus(x, y))

plus : nat nat  nat 関数の型の記述 アリティ = 1 → unary アリティ = 2 → binary アリティ (引数のデータ型) (arity) ソート (戻り値のデータ型) (sort) 関数の型 アリティ = 1 → unary アリティ = 2 → binary

自然数 (nat: natural number)の記述 仮想的な代数的仕様記述言語による定義 abstract data type nat 抽象データ型の名前 constructors  0 :     nat  s : nat  nat 構成子の型 (データを作り出す関数) operators  plus : nat nat  nat  times : nat nat  nat 演算子の型 equations  plus(0, y) = y  plus(s(x), y) = s(plus(x, y))  times(0, y) = 0  times(s(x), y) = plus(times(x, y), y) 演算子の定義

 plus(0, y) = y  plus(s(x), y) = s(plus(x, y))  times(0, y) = 0  times(s(x), y) = plus(times(x, y), y) 直接実行 プログラミング言語で実装する前に 仕様を用いた式の書換え(rewriting)で実行(シミュレーション) 2 * 2 = 4 を求めてみる  times(s(s(0)), s(s(0)))  = plus(times(s(0), s(s(0))), s(s(0)))  = plus(plus(times(0, s(s(0))), s(s(0))), s(s(0)))  = plus(plus(0, s(s(0))), s(s(0)))  = plus(s(s(0)), s(s(0)))  = s(plus(s(0), s(s(0))))  = s(s(plus(0, s(s(0)))))  = s(s(s(s(0)))) データの具体的な表現を決めなくても関数の計算ができた

記述の完全性 データ: nat 0 :  nat s(x) : nat  nat 0 は自然数 x が自然数ならば,s(x)は自然数 すべての自然数は,0 と s(x) を用いて構成できる 無矛盾(変な定義でない) 完全(抜けがない) 演算 : plus(x, y)  plus(0, y) = y  plus(s(x), y) = s(plus(x, y)) 無矛盾(変な定義でない) 完全(すべての引数の組合せを網羅) 第2引数は,すべての自然数を網羅. 第1引数は,自然数が 0 か 1以上か(必ずどちらかになっている)         に応じて,どちらかの等式で網羅.

抽象データ型の代数的仕様記述と実装 抽象(仕様) 具体(実装) ■抽象データ型を 無矛盾で完全な 代数的仕様記述 により定義 ■仕様を満たすように実装(プログラミング) ■ nat の例 データの集合={0, s(0), s(s(0)), s(s(s(0))), …} 抽象(仕様) 演算の集合 ={plus, times} (specification) ■ nat の実装 データの集合={0000, 0001, 0010, 0011, …} 具体(実装) 演算の集合 ={加算, 乗算} (implementation)

スタック (stack) push(D, S) pop(S) top(S)=C

自然数のスタック(natStack)の記述 abstract data type natStack constructors  empty_natStack :    natStack  push : nat natStack  natStack operators  pop : natStack  natStack  top : natStack  nat equations  pop(empty_natStack) = empty_natStack  pop(push(x, S)) = S  top(empty_natStack) = 0  top(push(x, S)) = x 本当は,エラーにしたいところ 記述が無矛盾で完全になっていることを確かめよう

リスト cons(H,T) H:T リスト: 任意の長さのデータの列 [A, B, C, …] を表現するデータ構造 [A,B,C,…] (list) 頭部 head 尾部 tail [A,B,C,…] [B,C,…] セル cell cons(H,T) で表現 A 簡易記法:  H:T

A: (B: (C: [ ])) = A: B: C: [ ] リスト 空リスト [ ] A: (B: (C: [ ])) = A: B: C: [ ] : は右結合演算子 = [A,B,C] (この簡易記法が使われることもある)

リストの演算 head(A : B : C : D : E : [ ]) = A tail(A : B : C : D : E : [ ]) = B : C : D : E : [ ] append(A : B : C : [ ], D : E : [ ]) = A : B : C : D : E : [ ] 結合 reverse(A : B : C : D : E : [ ]) = E : D : C : B : A : [ ] 反転

自然数のリスト(natList)の記述(1/2) abstract data type natList constructors  null :     natList  cons : nat natList  natList operators  head : natList  nat  tail : natList  natList  append : natList natList  natList  reverse : natList  natList

自然数のリスト(natList)の記述(2/2) equations  head([ ]) = null  head(x : L) = x  tail([ ]) = null  tail(x : L) = L  append([ ], L) = L  append(x : L1, L2) = x : append(L1, L2)  reverse([ ]) = [ ]  reverse(x: L) = append(reverse(L), x : [ ])

リスト処理の直接実行 append(1 : 2 : [ ], 3 : 4 : [ ])  append([ ], L) = L  append(x : L1, L2) = x : append(L1, L2)  reverse([ ]) = [ ]  reverse(x: L) = append(reverse(L), x : [ ]) リスト処理の直接実行 append(1 : 2 : [ ], 3 : 4 : [ ]) = 1 : append(2 : [ ], 3 : 4 : [ ]) = 1 : 2 : append([ ], 3 : 4 : [ ]) = 1 : 2 : 3 : 4 : [ ] reverse(3 : 2 : 1 : [ ]) = append(reverse(2 : 1 : [ ]), 3 : [ ]) = append(append(reverse(1 : [ ]), 2 : [ ]), 3 : [ ]) = append(append(append(reverse([ ]), 1 : [ ]), 2 : [ ]), 3 : [ ]) = append(append(append([ ], 1 : [ ]), 2 : [ ]), 3 : [ ]) = append(append(1 : [ ], 2 : [ ]), 3 : [ ]) = . . . . . = append(1 : 2 : [ ], 3 : [ ]) = . . . . . = 1 : 2 : 3 : [ ]

関数型プログラミング言語による実装 【Haskell による実装の例】 head :: [a]  a head (x : _) = x _ は 重要でない変数名を省略したもの tail :: [a]  [a] tail (_ : ys) = ys [a] と [a] を受け取り [a] を返す関数の型 append :: [a]  [a]  [a] append [ ] ys = ys append (x : xs) ys = x : (append xs ys) 関数の引数をくくるカッコを省略可 reverse :: [a]  [a] reverse [ ] = [ ] reverse (x : xs) = append (reverse xs) [x]

演習問題 4 (1) 自然数の代数的仕様 nat を拡張するために,減算 minus を等式で 定義しなさい. ただし,x < y のときには,minus(x, y) の値はゼロであると定義する. ヒント: 左辺が minus(0, y),minus(s(x), 0),minus(s(x),s(y)) からなる3つの等式を記述する. (2) 自然数のスタックの代数的仕様 natStack の直接実行を用いて, つぎの式の計算をしなさい. ただし,自然数 s(s(s(0))) を 3 などと略記してよい. top(pop(push(3, push(2, push(1, empty_natStack))))) (3) 自然数のリストの代数的仕様 natList を拡張するために, 関数 sum(L) を等式で定義しなさい. ただし,sum(L) は,演算 plus を用いて,自然数のリスト L の中の すべての自然数の総和を求めるものとする. 実行例: sum(s(s(0)) : 0 : s(0) : [ ]) = s(s(s(0))) ヒント: 左辺が sum([ ]),sum(x : L) からなる2つの等式を記述する.