Software Engineering Lecture 5/29
Menu Menu来週 6/5 は休講にします。
先週の復習
型 type型の整合性
テンプレート
タイプ変数の使われ方の一つとして、配列の扱いがある。配列にオブジェクトを格納することを考えよう。
void push(stack,obj) {
stack[stack_ptr++] = obj; // stack bound should be checked.
}
pop(stack) {
if (stack_ptr)
return stack[--stack_ptr];
else
return NULL;
}
これでは、型が無いので、これに型を付けるとする。そうすると、
void push(stack,char obj); char pop(stack);
void push(stack,int obj); int pop(stack);
void push(stack,TYPE1 obj); TYPE1 pop(stack);
....
などとたくさんコードを書く必要がある。その中身は基本的には、最初のコードど同じであるのに。Java では、
void push(stack[],object obj) {
stack[stack_ptr++] = obj;
}
として、予備だし側で、push(stack,(object) obj) などと、cast (型変換)してやるのが普通である。しかし、これは、型整合という点から見ると望ましくない。そこで、型変数T
を導入して、
template <class T>
void push(stack stack,T obj) {
stack[stack_ptr++] = obj; // stack bound should be checked.
}
T pop(stack) {
if (stack_ptr)
return stack[--stack_ptr];
else
return NULL;
}
などとする。実際には、使用する側で、
stack <int> s;
みたいな形で型を明示する。これが(コンパイル時に)呼ばれた時に、このテンプレートが実際のコードに展開される。
テンプレートの利点と欠点
テンプレートは、型整合で増えるコード作成の量を軽減してくれる。しかし、基本的にマクロなので、自分の見た形と実際に実行される形式との差が大きくなる。特に、template はヘッダファイルなので、ファイルの読み込み順序に影響されやすい。
template をデバッガでサポートするのは、それほど難しくはない、やさしくもない。
コンパイル時にコード生成するので、コードの量が大きくなりがちである。
テンプレートを使ったGeneric Programming
テンプレートとオペレータオーバライドの組合せは強力である。例えば、複雑なデータ構造をあたかも通常の配列のように for 文取り扱うことができる。例 (int) また、このデータ構造を別なデータ型を扱うものとして使えることにもできる。
例 (string) ここで、begin などは、データ構造の巡回を指示するので、iterator と呼ばれる。
Generic Programming は、データ構造のAPIの定義を統一する
これらのライブラリは、STL (Standard Template Library)という形で、C++ の標準となっている。この構成を、list の場合について調べてみよう。
Generic Programming の例 一つの方法である。これは、オブジェクト指向とは若干異なる。オブジェクト指向では、データ構造を中心として API の定義を行なう。
Perl の持つデータ構造
perldata を読もう。
変数と配列
変数は、値が一つ。スカラ (scalar) と呼ばれる。
配列は、値が複数。アレイ (array) と呼ばれる。
更に、添え字が文字などだと連想配列となる。これはハッシュ (hash)と呼ばれる。
(おまけ) 何故、ハッシュと呼ばれるのか?
連想配列は、文字列=>ランダムな数値を、決まった値で生成するハッシュ関数を使って実現されているから。
$hash{'test'}
は、
$hash[ &hash('test') ]
とのような感じで配列として実現されている。(実際には、もう少し下位の実装で実現されている)hash 関数は Perl の内部で実装されている。
Perlの参照
変数の値と名前を区別しよう。
$a というのは、値を入れる箱
箱に中に入っているのが値
代入とは、箱の中の値を書き換えること。
$a = 3;
Reference in Perl
Perlのデータは、配列と[1,2,3]と連想配列{'a',1,'b',2} を使うことができる。
pw071: {1} % perl5 -de 0
Loading DB routines from perl5db.pl version 1
Emacs support available.
Enter h or `h h' for help.
main::(-e:1): 0
DB<1> @a = (1,2,3,4,5)
DB<2> p "@a"
1 2 3 4 5
DB<3> @b = @a
DB<4> p "@b"
1 2 3 4 5
DB<5> $a[2]="kono"
DB<6> p "@a"
1 2 kono 4 5
DB<7> p "@b"
1 2 3 4 5
DB<8> $a = [1,2,3,4,5]
DB<9> p $a
ARRAY(0x805b3b4)
DB<10> p @$a
12345
DB<11> p "@$a"
1 2 3 4 5
DB<12> $$a[1] = "kono"
DB<13> p "@$a"
1 kono 3 4 5
DB<14> $b = $a
DB<15> $$b[2] = "kakazu"
DB<16> p "@$b"
1 kono kakazu 4 5
DB<17> p $b
ARRAY(0x805b3b4)
DB<18> p $b->[2] = "higa"
higa
DB<19> p "@$b"
1 kono higa 4 5
DB<20> $c = {}
DB<21> $c->{'kono'} = 'teacher'
DB<22> $c->{'higa'} = 'student'
DB<23> p %$c
higastudentkonoteacher
DB<24> $c{'kono'} = 1
DB<25> $c{'higa'} = 2
DB<26> p keys %c
higakono
DB<27> p $c
HASH(0x8134cc0)
プログラムの中に現れるPerlのデータは、\ を付けることによって参照を取ることができる。
DB<28> $d = \%c
DB<29> p $d
HASH(0x8152b00)
DB<30> $d->{'shimabukuro'} = 'student'
DB<31> p keys %c
higashimabukurokono
DB<32>
問題
以下の例題を動かしてみて、Perlの参照の振る舞いを理解すること。出力を確認し、結果を提出する事。
$a=[1,2,3,4];
$a->[5]=3;
print "@$a, $a->[2]\n";
$b={};
$b->{First}='Cat';
$b->{Second}='Dog';
$b->{Third}='Pig';
foreach $key ( keys %$b) { print "$key is $b->{$key}\n" }
@a = ( 1,2,3,4);
$refa = \@a;
$refa->[5]=3;
print "@$refa, $refa->[2]\n";
Package / Module
大規模なプログラムでは、変数や手続き名の数は数百以上になる。良く使われる語彙は500程度と言われているので、必然的に同じ名前を使うか、無意味な識別名を使う必要に迫られる。一方で、iとかjとかは一時変数として便利なので、良く使うことが多い。しかし一時変数などは、データフローを持つ構造化技法にはなじまない。そこで名前空間というのを考える。名前空間が異なる場合は、そこでは同じ同じ字面でも異なる名前をさすことになる。名前空間は、パッケージ(Package)または、モジュール(Module)と呼ばれる。C では、static というキーワードによりファイルに局所的な名前を作成することができる。これにより疑似的な名前空間を作ることができる。
名前空間では、他の名前空間の名前を輸入(import) したり、自分の名前を公共的なものと宣言する(export)する機能がついていることが多い。しかし、名前空間と、その中の名前の対でアクセスすることでも十分である。これらの名前空間はセキュリティなどとからめて使われることも多いが (Java などはそうである)本来は、セキュリティと名前空間の概念は直交している。
Perl のpackage は、package Hogehoe; と記述することによって作られる。package 形式になったファイルは、hogehoge.pm という(Perl Module)拡張子をつける。これにより、use hogehoge; という形で Module を使用することができる。(通常のファイルは do "filename.pl"; というのを使う) ( perlmod を参照すること)
Object
オブジェクトのIDは、オブジェクトへのポインタあるいはユニークな識別子であり、データベースでのキーと同じ役目を果たす。しかし、オブジェクトのIDは普通はプログラマが直接指定することはない。オブジェクトの間の関係は、
- そのオブジェクトが他のオブジェクトを知っている / もっている
- そのオブジェクトが他のオブジェクトから参照されている
- お互いに参照しあっている
オブジェクトの中身を直接いじることなく、オブジェクトIDを通してオブジェクトにアクセスすることにより、インタフェースと実装の分離を安全におこなうことができる。しかし、その分、実行は遅くなる。オブジェクトIDからメッソドを探すという操作が必要だからである。もし、メソッドを呼び出す時に、そのオブジェクトが何かを指定してやれば、そういうデメリットはない。C++などは、そういう発想で作られている。
継承関係は、あるオブジェクトからすべての属性を引き継ぎ、さらに何んらかの属性をつけ加えることを意味する。実際には、属性だけでなく、 オブジェクトの振る舞いもなんからの形で引き継いでいることをさす。ただし、今の所を、属性以外の条件はそれほど明確ではない。継承するオブジェクトのプログラム理論的な意味をすべて引き継ぐという考え方もあるが、それを厳密に行うのは実はかなりやっかいなことだし、実用的でもない。
データベースといっても、オブジェクトには一般的な検索方法は存在しない。もしデータベースが欲しければ、それは自分で実装しなくてはならない。(あるいは買ってきてもいいね)
Object in Perl
Perlのオブジェクトは、Package と無名データを結び付けることによって作られる。( perlobj Perl のオブジェクト を参照すること、さらに、 perlbot Bag'o Object Tricks For Perl5 (the BOT) も見る方が良い。)無名データをオブジェクトの持つ内部構造として、これにオブジェクトの操作方法を記述した Package を対応させると Perl のオブジェクトができあがる。この二つを結び付けるのが bless という演算子だある。
例えば猫のオブジェクトをPerlで記述すると、cat.pl のようになる。
package Mammal;
sub new {
my $type = shift;
my %params = @_;
my $self = {};
$self->{'Name'} = $params{'Name'};
$self->{'Sex'} = $params{'Sex'};
$self->{'Weight'} = $params{'Weight'};
$self->{'Color'} = $params{'Color'};
bless $self, $type;
}
package Cat;
@ISA = qw( Mammal );
sub new {
my $type = shift;
my %params = @_;
my $self = Mammal->new(@_);
$self->{'Character'} = $params{'Character'};
bless $self, $type;
}
# method の書き方
sub miyao { my ($self,$arg) = @_; $self->{Name} = $arg; }
sub name_print { my ($self) = @_; print $self->{Name}; print "\n";}
# 使い方
$a = Cat->new(Name=> Jackey, Sex=>F);
print $a->{Name};print "\n";
$a->name_print;
のようになる。
問題
上に出てきた猫に、methodを付けてみよ。method は package で宣言されるsubroutine で、インスタンス変数を変更したり、印刷したりする。まず、猫をたたくと怒るようにしてみよう。そして、怒ると鳴き声が変わるようにしてみよう。
カード
Perl5 で書いたカードの例題を見てみよう。SimpleCard.pm SimpleCardPile.pm これらは、たとえば以下のようの使う。
foreach $suit ('s','h','d','c') {
for($i=1;$i<14;$i++) {
push(@card, CardPlay::SimpleCard->new(-suit=>$suit,-number=>$i));
}
}
$pile = CardPlay::SimpleCardPile->new(-contents=>\@card);
$pile->print;
$pile->shuffle;
$pile->print;
$pile->first->print;
$pile->last->print;
問題
SimpleCardPile.pm を使って、カードを配布して、五枚づつ、5人のユーザに配るプログラムを作成せよ。ポーカーの役の判定のプログラムをSimpleCardPile.pm に付加してみよ。
宿題
以上は宿題とします。宿題は、メールでSubjectを
Subject: Report on Software Engineering Lecture 5/29
として提出すること。この講義の問題を提出する時には、
Subject: Practice on Software Engineering Lecture 5/29
とすること。