TSSI 基盤技術研修コース— プログラミング言語 — ＃ 1
久野靖∗

2011.5.13

はじめに• (Java) Ken Arnold, James GoslingDevid Holmes 著,
柴田訳, プログラミング言語 Java 第 4 版, ピアソン,□ 本講座のタイトル→「プログラミング言語」

2007.

□ 本講座の目的→• (C++) Stenley B. Lippman, JOsee Lajoie, Barbara
E. Moo 著, 玉井訳, C++プライマー第 4 版, 翔泳社,

• (a) プログラミング言語の基本機能やメカニズム→

2006.

「言語の機能/仕様」• (b) それを用いてソフトウェアを組み立てる戦略・考 □ 上記でしんどい人は次のものなどどうでしょうか。ただえ方→「パラダイム」
し東芝様ではそんなに冊数は出せないそうなので自費ということになりますが。これらはざっと通読するのに向□ とくに「オブジェクト指向」に重点を置いている。

いています。

• 今日のソフトウェア開発において主流となっている• 久野 靖、久野禎子、Java によるプログラミング入門

パラダイム

第 2 版、共立、2011.

• 道具だて、言語仕様が複雑、込み入っている• 高橋麻奈、やさしい Java 第 3 版、ソフトバンク、2005.

• 「なぜそうなっているか」という理屈はちゃんとある• 高橋麻奈、やさしい C++第 3 版、ソフトバンク、• 「どういう理由で」「どうなっている」を学ぶ→使い2007.

こなし方が分かる□ ソフトウェア開発は結局「自分の頭でどれだけ考えられるか」の限界までしか作れない1

計算機言語とは…

• 同じことをするのに「考えやすい道具 (言語)」を使 □ まず計算機言語とは何かというお話から。
うと→• では計算機とは何をするもの?
• 同じ能力でより高度なものまで考える (作る) ことが可能になる□ プログラミング言語…計算機言語の一種。

• ではどうすれば「考えやすい」のか→言語の設計思 □ 計算機言語とはどういう言語?
想を学ぶ
□ 構成:

1.1

なぜ存在する?

計算機と人間の情報伝達• 第 1 回→プログラミング言語の諸概念+オブジェク□ 計算機…情報を取り扱うための装置
ト指向 (主として ADT) の導入• 第 2 回→オブジェクト指向言語の主要メカニズムと □ 人間と計算機はどうやって情報をやりとりするか?
□ ハードウェア…入出力装置

用法• 第 3 回→オブジェクト指向のさまざまなバリエーショ
ン (予定)• 入力→キーボード、マウス、マイク、…
• 出力→ディスプレイ、プリンタ、スピーカ、…

□ 参考書は毎回持参される必要はないです。講座の性質上、やや高度ですがきちんと解説されている本を選んでいま□ 具体的にどのような形で情報をやりとり?

す。疑問に思った点があれば参考書の該当個所を調べて □ 計算機に自然言語 (日本語) で命令できたら嬉しいと思うか? (Yes/No)
読むという形で利用してください。
∗ 筑波大学大学院経営システム科学専攻

1

1.2

人工言語

• SGML → HTML → XML、XHTML□ アプリケーションを動かし画面で見れば十分?

□ 人間どうしの情報交換→自然言語 (日本語、英語、…)• 「処理」したいときは言語になっている方がよい。

• 計算機とのやりとりには不向き• あいまいさ、処理が複雑• 人間を相手にするのと計算機を相手にするのでは違っ

1.6て当然 (?)□ 計算機言語とは、計算機で扱うための人工言語□ 簡単な規則に基づく人工言語→計算機の処理に向く• でも人間が読み書きしてもよい。

• 計算機で取り扱うために作った人工言語→計算機言語

1.3

この節のまとめ• 人間が考えるための「手段」でもある。
□ 代表はプログラミング言語だが、他の計算機言語もいろ

構文と意味

いろある。

□ 構文→言語に現われる語の並び方の規則性□ 意味→どのような構文であれば何を意味するか
犬です。
犬なの？

意味
＝伝えたい内容

プログラミング言語の諸側面

2□ プログラム→「人間が書くもの」だが、プログラム以前

犬です。
犬なの？

には無かったさまざまな特徴/特性がある• 個別のプログラミング言語についても、その特徴/特性についていろいろな選択肢がある→以下で検討

This is a pen.
dog.
Is this a dog?
構文＝ことばの規則

2.1□ 簡単な C プログラムを題材に…

□ 言語の定義→構文と意味のペア (計算機言語も同様)

int main() {int x, y;printf("input x> "); scanf("%d", &x);
printf("input y> "); scanf("%d", &y);if(x > y)printf("%d is larger.\n", x);
else
printf("%d is larger.\n", y);return 0;}• ほかに語彙 (単語) も必要だけど。

1.4

プログラミング言語□ プログラムを書くための計算機言語• プログラムとは?
□ プログラミング言語の用途?

• Q. このプログラムは何をするプログラムですか?

• 計算機に読み込ませる• Q. このプログラムにはどんな問題がありますか?

• 人間が読む (娯楽、仕事、…)• Q. あなたが認識した「問題」を解決する方法は?

• 自分が考えるための手段→でも動かした方が嬉しい

1.5

プログラムが持つ特徴□ 問題かどうかはあくまで目的 (仕様) に照らさなければ分からない

その他の計算機言語• 仕様: 「x と y が等しければ何が出力されてもよい」
だったら?

□ プログラミング言語でない計算機言語も多数ある□ 楽譜記述言語• 仕様: 「x と y に等しい数を入れてはならない」だったら?

□ 図形記述言語• 仕様: 「x と y に等しい数が入ることはあり得ない」
だったら?

• プリンタ→ページ記述言語→ PostScript…プログ
ラミング言語でもある。

□ 動作はきっちり決まっている□ 文書記述言語 (マークアップ言語)、データ記述言語• 言語仕様としてきちんと決まっている部分
2

• これにどのような「要素」「概念」「機能」が含まれている?

• 言語仕様で決まっていなくても、動かせば分かる(???)□ それが「プログラマの意図」さらには「ソフトの仕様」 □ たとえば…
と一致しているかどうかはまた全然別の問題□ 1 つの動作を行うプログラムは何通りも書ける• 順次実行 (A; B; C)• 制御構造 (if、while、…)/* A */
if(x > y)printf("%d is larger.\n", x);
else
printf("%d is larger.\n", y);• 変数• 型 (値の種別)• アドレス (ポインタ)• 関数 (サブルーチン)/* B */
if(x > y) z = x; else z = y;printf("%d is larger.\n", z);• ...
□ これらがどういう意味を持っているか考えて見よう…

/* C */z = (x > y) ? x : y;printf("%d is larger.\n", z);

2.3/* D */
printf("%d is larger.\n", (x>y)?x:y));

実行順序□ 「文が順次実行される」という考え方は「当然」か?

/* E */
z = x;
if(y > z) z = y;printf("%d is larger.\n", z);x = y * z;y = 3.1416 + 1;
z = 2.7183 + 2;

• どれがいいと思うか?

• どういう順番で実行されると「感じられる」?

それはなぜか?

□ もし 3 つの値 x、y、z の最大だったら?

□ 方程式であれば、「参照関係に従って」計算する。
□ 関数型言語などでも同様。

/* D’ */
printf("%d is larger.\n",(x>y)?((x>z)?x:z):((y>z)?y:z));double x() { return y() + z(); }double y() { return 3.1416 + 1; }double z() { return 2.7183 + 2; }/* E’ */
max = x;if(y > max) max = y;if(z > max) max = z;printf("%d is larger.\n", max);□ 再帰と組み合わせることで複雑な計算でも可能
double fact(n) {return (n < 1) ? 1 : n * fact(n-1);}• こんどはどれがいいか?
□ 1 つの動作を行うプログラムは何通りも書ける□ ではなぜ現在の多くの言語は「順次実行」なのか?

• そのどれがいいかは様々な側面を総合して決めるしかない• 手続き型言語のモデル←現在の計算機のモデル• 現在の CPU は「命令を順番に実行して行く」モデル• プログラミング言語の機能やデザインもまさにそう!

2.2に従っている (実際には全然順番に実行していないことが多いが…)

プログラミング言語が持つ「要素」「概念」「機能」□ また同じ C プログラムを題材に…

• プログラム言語でもこれにならった方が自然 (?)

2.4

int main() {int x, y;printf("input x> "); scanf("%d", &x);
printf("input y> "); scanf("%d", &y);if(x > y)printf("%d is larger.\n", x);
else
printf("%d is larger.\n", y);return 0;}

制御構造□ 「枝分かれ」「繰り返し」などの*実行順序*を表す機構
if(x > y) {z = x; x = y; y = z;}while(n > 0) {fact = fact * n; n = n - 1;}3

• 実はこのように「制御構造の中に文の集まりが入る」 □ 「if」と「switch」と「while/for」と「do-while」でいいのか? もっとあった方がいいのか? 減らした方がという考え方が一般的になったのは 1970 年代の「構いいのか?

造化プログラミング運動」の結果。

• それ以前はこんな便利な (?) ものはなかった。

(yes/no)□ 現在あるもので「一応」足りているが「まだ」あった方がよいかも。

□ Fotran 60 (JIS Fortran 5000/7000)• 定理:

IF(X .LE. Y) GOTO 10
Z = X
X = Y
Y = Z
10 CONTINUE

すべての (含むスパゲティ) プログラムは連接、分岐 (if)、反復 (while) の組合せに書き換えることができる。

• しかし「言語としての使いやすさ」が問題だからもっとあってもよい。

30 IF(N .LE. 0) GOTO 20
FACT = FACT * N
N = N * 1
GOTO 30
20 CONTINUE• 後述する「例外」などは新しい制御構造のバリエー
ション。

2.5

□ JIS 3000

変数

□ 変数とは、何ですか?

IF(X - Y) 20, 10, 10
20
Z = X
X = Y
Y = Z
10 CONTINUE(知らない人に説明するとした

ら?)□ 変数の 2 つのモデル (どっちがいい?)• 説明 A: 値を入れておく「箱」のようなもの。
• 説明 B: 値を表す名前 (値に名前をつけたもの)。

□ 構造化プログラミング運動□ 「箱」のモデル…手続き型言語のモデル• GOTO はスパゲティプログラムになるからやめよう• 手続き型言語…Fotran、COBOL、Pascal、C、C++、• 構造化構文 (今の if や while) の入れ子を使おう

Java、…

□ しかし「入れ子」がいいかどうかも疑問はある• 変数に代入して値を書き換えて行く

if(a[low] < a[high]) {for(i = 0; i < high-low; ++i)
for(j = low; j < high; ++j)if(a[j] > a[j+1]) {z = a[j]; a[j] = a[j+1]; a[j+1] = z;}
} else {for(i = 0; i < high-low; ++i)
for(j = high; j > low; --j)if(a[j-1] < a[j]) {z = a[j-1]; a[j-1] = a[j]; a[j] = z;}}

• 計算機ハードウェア (メモリ) の自然な抽象化になっている• 副作用ベース→分かりにくいバグの原因となることがある
while(i > 0) {fact = fact * i; i = i = 1;
}
箱モデル

x

1

y

2

z

3

＋

• このコードが何をしているか読めるか?
• 読めるようになったとすれば「どう考えた」から?

x:

1□ 「入れ子」構造は、ある部分 (たとえば「文」が入る部

y:2分) に、複雑な構造であっても 1 つの単位として互換性

z:

参照モデル

3

＋

があれば入れられる、という思想によっている。

• 計算機による処理は容易 (やり方が分っていれば)。 □ 「値を表す名前」のモデル…数学に近い感じ• しかし人間の頭はそういう風にはできていない。

• 関数型/論理型言語…ML、Haskell、Prolog、…

• →プログラムの書き手としては、「人間に扱えるよう• 変数には値が「束縛」されるに」書く方がよい。

• 一度束縛された値が書き変わることはない (単一代入とも言う)• →具体的にはどうする?

4• 例: C++の演算子定義→ユーザ定義の型でも演算子が使える。が、そのために言語的には複雑になって• ただし変数は複数のインカーネーション (実体) を持
つことも…

いる。

int fact(int n) {return (n < 1) ? 1 : n * fact(n-1);}

2.7

fact(n:5)↓ 5 * fact(n:4)↓ 5 * 4 * fact(n:3)↓ 5 * 4 * 3 * fact(n:2)↓ 5 * 4 * 3 * 2 * fact(n:1)↓ 5 * 4 * 3 * 2 * 1 * fact(n:0)↓5 * 4 * 3 * 2 * 1 * 1□ 結局:

さまざまな型□ 基本型…数値 (整数、実数)、論理値、文字値など。

• 定義?

「直接 CPU が演算できるような値の型」「マ

シンレジスタに載るような値の型」「それ以上分解できない値の型」(どれもよくない)

どちらのモデルも言語として成り立つ• 結局、言語仕様として「基本型に決めたから基本型」
みたいな…

• 優劣は一概に言えない (言語どうしで優劣が言えな
いのと同様)• 基本型の値を書く手段として、リテラル (定数) が用意されている。(基本型でない場合も一部用意されて2.6

いることはある。)

型 (= 値の種別)□ 複合型…基本型でない型すべて。複数の値を含む…かど□ 「int x」「double x」「char *x」

うか?

• 何のために「型」があるのか?

□ 配列型…もっとも古くからある複合型 (ベクトルや行列演算のため)□ さまざまな「型がある理由」(A)• 入れる「箱」の大きさが種別によって変わるから。

• 同じ型の値が連続して並んでいる• 種別ごとに演算 (演算命令) が違うから• 添字により「何番目」を指定してアクセスする• →これらは言語の設計次第でどうにでもなる。

• 実現上は単にメモリ上に必要なだけの領域を並べるのが基本 (だった)• →ただし型がある方が効率はよい

a[0]• 型のない言語 (弱い型の言語) → Lisp、Smalltalk、
Perl、…

a[1]

a[2]

a[N]

a:

□ さまざまな「型がある理由」(B)• しかしそれでは不便だと分かってきたので…(どう不便?) →連想配列• プログラマに「ここはどういう種類の値」という情報を書かせたい• おかしなデータの使い型をチェックして教えたい• 型としては「T 型の配列」という型→ T は「パラメタ」になっている。

• →「よりよくプログラムを書くための道具」として• さらに要素数 (ないし、添字の上限/下限) も型のパラメタとする言語もある (Pascal とか)の型• 強 い 型 の 言 語 → Pascal、C、C++、Java、ML、 □ レコード/可変レコード/ユニオン型
Haskell、…
• こちらは COBOL が元祖。複数の「違う型の」値を組• 型の明示 (変数宣言に必ず型を書く) と型推論 (「xみ合わせたもの= 10」なら x は int、のように使用関係に基づいて• 非常に基本的な型だった…はずが、オブジェクト指決定) とがある。
向のせいで影が薄く…
□ その他の区分• Java にはレコード型はない。C++ではクラスの特別なカタチ。

• 型には単純な型 (「整数」「実数」「文字」…基本型)と込み入った型 (「レコード」「配列」…複合型) が• 可変レコード…レコードで「途中からフィールドを取り換える」ようにした← Pascal

ある (後述)。

• 「標準で用意されている型」「ユーザ定義の型」とい• これを「取り換えずに並べるだけ」と「取り換えるだけ」に分離→ユニオン型← C

う区別があることもある。

• できるだけどんな型でも同じように区別なく使える方が望ましい。が。
5

2.8

ポインタ型

□ ポインタ型…とは?

関数型/関数へのポインタ型

2.9
C 言語の式「＆ｘ」の結果とは?

□ 関数を指すポインタ→複数の関数のどれを呼ぶか選択可
能に• 答 A: 変数 x の主記憶上の番地 (アドレス)• 答 B: 変数 x を「指す」(参照する) 何らかの値• 関数 (サブルーチン) は呼ぶ以外の使い方はできない→わざわざ「〜のポインタ」としなくてもいい→そ• 答 C: その他…

ういう言語もある。
• C では「関数名を自動的にその関数を参照するポイ
ンタ値に変換」□ 言語仕様的には「B」。

• 言語仕様に「番地」は出てこない (実装に依存しすぎる)。

int myfunc(int x) { return x*x; }int (*f)(int) = &myfunc;←○
int (*g)(int) = myfunc; ←これも○• int *p = …; … p + 1 … ←実際に足されるのは「1」ではない• C では型名や変数宣言の書き方がすごく分かりづらい• それはいいが、なぜこういうもの (ポインタないし…

参照) が必要なのか?

□ 引数の個数と型、および関数が返す型も関数型の一部□ ポインタは何のために必要?

(パラメタ)• 変数に値を書き込んで戻してもらうため←言語に参照渡しがない場合• 「proctype(int,int)returns(int)」← CLU 流の記法• 動的データ構造 (連結リスト、2 分木、グラフ、…)• 「(int(*)(int,int))」← C のキャスト等で書く型
指定…分かりづらい• 大きなデータを持ち回る非効率を避けたい場合• データを複数個所で共有したい場合 (cf.

広域変数)

□ ただし C では「int (*f)()」のようにパラメタ部分を□ データの共有は副作用を可能にする→注意が必要

書かないことで「引数の個数と型を指定しない」ことが□ 言語設計上の問題点

可能→安全でない• 任意の変数のポインタを取れるのは危険 (なくなった• または引数のどこかから後を「...」と指定できる

後も…)(可変引数)• C/C++→配列の添字式がポインタ演算 (「a[i]」は• 引数を「持たない」場合は「int (*f)(void)」と指「*(a+i)」と同じ意味)

定 ← 注記:C++ では「int (*f)()」でも同じ。C
では「int (*f)()」だと「パラメタは何でもよい」• a が配列、i が整数として「a[i]」でも「i[a]」でも同じって知ってます?

になってしまう。

• 非常に特異な言語設計、問題山積み (添字検査困難
とか)。

2.10

□ 型という点では「T 型へのポインタ」→パラメタつきの型。

強い型と弱い型□ ここまでおもに「強い型の言語」(=コンパイル時にチェッ• しかし値そのものは「レジスタに載るような値」

クする) について考えて来た。

• 型 T と型*T を使い分けるのは混乱のもと (C++だと• 弱い型の言語 (=実行時にチェックする) → Lisp、
Smalltalk、Perl、Ruby 他スクリプト言語

さらに＆Ｔもある)• すべての値をポインタとするのも 1 つの選択肢 (Java• 弱い型の言語はチェックが抜けることはない (実行時がこれに近い)に逐一チェックするから) →安全ではあるが、速度は犠牲になる□ 古い言語には「ポインタ型」が 1 種類しかない言語もあった (PL/I)。

• 型のエラーがあってもそこを実行してみるまで間違いがあることが分からない→製品を作る上では弱点• どんな型の変数でもアドレスを取ると同じ型→型安

• 強い型であれば、コンパイル時にチェックできる→実全でない。

行時には安全であることが保証される (ただし最近• しかし C でもキャストすれば何型のポインタにでもできる→安全でないことに変わりはない。ただ、キャのオブジェクト指向言語では実行時のチェックにたよる部分もある)。

ストを書くことで「分かっていてやっている」という意志表示にはなる。

6

2.11

この節のまとめ• さっきの「入れ子をどうするか」の答えも第 1 義的にはここにある。

□ プログラム言語/プログラムが持つ特徴• 「いちど関数を書いてしまえば、言語にそういう命• 厳密だが「何が正しいか」は難しい (仕様の問題)

令が増えたものとして扱える」• 1 つのことを何通りにも書ける□ 関数の利用が普及したのは構造化プログラミングの時期
以降 (1970〜)□ プログラム言語が持つ多くの概念• それ以前だと「1 万行のメインプログラムだけ」と• 順次実行、制御構造、変数、型、…

か珍しくなかった□ 型→データの種別、対応する演算の種別□ 関数の弱点→かたまりになるのはあくまでも「コード」• 型安全性→間違った操作が起こらない (突然死なない)

3だけという点→データの共有 (呼び側と呼ばれ側、呼ば• 強い型→コンパイル時にチェックできるようにする

れ側どうし) は?

命令型からオブジェクト指向へ• パラメタとして逐一渡す→繁雑、渡す側でもいじれてしまう• グローバル変数として共有→どこでもいじれるから□ 命令型言語 (imperative language) → C のような、順一層危険

次実行と変数の書き換えに基づくプログラミング言語• →結局、関数だけを「部品」として用いると複雑さ• オブジェクト指向言語はその発展形と考えてよいに限界。← 1 レベルであること、データの共有問題• どのような経緯でオブジェクト指向言語ができたか?
• 細かい話題は後まわしにして、ここでは全体的な概観

3.1

3.2

モジュール

□ モジュール以前の言語→手続きが単位関数 (サブルーチン)• 手続きが単位だと「カタマリ」が小さすぎる□ 関数 (サブルーチン) とは、何ですか?

• 複数の手続きが 1 つのデータ構造を共有する→よく□ 現象的には、「ある範囲のコードの先頭にジャンプし、そあるパターンのコードを実行し、終わったら元の (ジャンプする前の)ところに戻って来る」

関数• 実装的には戻り番地 (ジャンプした命令の次の命令の番地) を保存しておいてそこへ間接ジャンプで戻る

関数

データ

• しかしそれが関数 (サブルーチン) だと言われても釈関数

然としないでしょう?

サブルーチン
先頭□ そのデータ構造は「グローバル変数」じゃいけないの?
□ グローバルだと「よそからいじられる」可能性• データ構造→通常、「こういう性質を維持」という条

戻り命令件がある (整合性条件) →これが壊れるとそれを前提としているコードも破綻□ プログラマから見ると→• グローバルだとそれを分からずにいじられてしまう• ひとまとまりのコードに名前をつけたもの□ モジュール機構があれば、データ構造は特定のコードか• その名前を指定して呼び出せるらだけしかいじれなくできる• それぞれが完結した何らかの仕事をしてくれる (そ• 整合性条件の維持→そのデータをいじるコードが限定されていると、そこだけきちんとすれば確実に大の細部は呼び側では知らなくてもよい)

丈夫□ 要するに「抽象化の手段」→最も重要な機能

7

• さらに、外から直接呼ばれたくない関数も同様にして限定できるとよい。
□ どうやって実装するか?

gw->line[lincount].y2 = y2;++gw->linecount;}
int gw_update(GraphWin *gw) {int i;
for(i = 0; i < gw->linecount; ++i) 線 i を描く;}...

言語にモジュールがあれば簡単だが、C 言語でも「ファイル内の static」を使ってある程度できる□ 例:「図形描画できるウィンドウ」□ なぜこれが「抽象データ型」かというと…GraphWin 型---GraphWin.c--#include <なんとか.h>static Window *win;
static struct { int x1,y1,x2,y2 } line[1000];
static int linecount;static int initialized = 0;
static int init() {win = ...
linecount = 0;}
int addline(int x1, int y1, int x2, int y2) {if(!initialized) init();line[lincount].x1 = x1; line[lincount].y1 = y1;line[lincount].x2 = x2; line[lincount].y2 = y2;++linecount;}
int update() {int i;
if(!initialized) init();for(i = 0; i < linecount; ++i) 線 i を描く;}...

というデータ型を新しく作っていると考えられる。たとえば利用側は:---GraphWin.h--typedef void *GraphWin;
GraphWin create_gw();
int gw_addline(GraphWin gw, int x1, int y1, int x2, int y2);
int gw_update(GraphpWin gw);...
---アプリケーション.c--include <GraphWin.h>
int main() {GraphWin gw = create_gw();...
gw_addline(gw, ...);...
gw_update(gw);...
}□ この「2 回ずつ言う」あたりを何とかしたのがオブジェ□ このコードでは「どういう整合条件」があると思うか?

クト指向の「メッセージ送信記法」と考えてもらえればよい。

□ こうしておくと他にどういういいことがあるか?
□ 結局、モジュールの機能は「何もしなければアクセスできるものを隠す」こと→カプセル化 (encapsulation)

3.4• 「わざわざ不自由にすること」が実は重要

3.3□ いちいち上のようにヘッダファイルを使い分けたり、第1 引数でデータ構造のポインタを渡したりすると煩雑→
間違い→破綻。

抽象データ型□ これを言語の方で自動的にやってくれると嬉しい□ モジュールでは隠す対象は「データ一式」□ では窓を沢山作りたければどうする?

オブジェクト指向

class GraphWin {Window win;Line[] line = new Line[100];
int linecount;public GraphWin() {win = ...;
linecount = 0;}
public addLine(int x1, int y1, int x2, int y2) {line[linecount++] = new Line(x1,y1,x2,y2);}public upate() {for(int i = 0; i < linecount; ++i) 線を描く…
}...
class Line {int x1, y1, x2, y2;public Line(int px1, py1, px2, py2) {x1 = px1; y1 = py1; x2 = px2; y2 = py2;}

→抽象データ型---GraphWin.c--#include <なんとか.h>typedef struct {Window *win;
struct { int x1,y1,x2,y2 } line[1000];
int linecount; } GraphWin;
GrappWin* create_gw() {GraphWin *gw = (GraphWin*)malloc(siezof(GraphWin));gw->win = ...
gw->linecount = 0;
return gw;}
int gw_addline(GraphWin *gw,
int x1, int y1, int x2, int y2) {gw->line[lincount].x1 = x1;
gw->line[lincount].y1 = y1;
gw->line[lincount].x2 = x2;

8

3.6

public int getX1() { return x1; }public int getY1() { return y1; }public int getX2() { return x2; }public int getY2() { return y2; }この節のまとめ□ プログラム言語の重要な機能→「抽象化」• ひらたくいえば「かたまり」の作り方}}• 関数 (サブルーチン) →データはかたまりにできない
のでいまいち□ 使う側では次のようになる• モジュール→関数+データのパッケージ。より大きなGraphWin gw = new GraphWin();...
gw.addLine(...);...
gw.update();かたまり• 抽象データ型 (ADT) →データ「型」として沢山使える• オブジェクト指向→ ADT+メッセージ送信記法+α• 初期設定とかポインタとかに煩わされなくなった□ ところで、上の例は C++ですか?
□ 答:

オブジェクト指向言語入門

4• メッセージ送信記法→「馬から落馬」がなくなった□ 本講座では実際にオブジェクト指向言語を使ってレポー

Java ですか?

トをやって頂きたい→具体的な言語の紹介も必要

上の例は Java。

• 言語として「C++」「Java」の両方を取り上げる• C++では new はポインタを返すが、こういう場合はポインタを使う必要はない• C++では「その場に」(ローカル変数ならスタック上などに) オブジェクトが作れるように工夫されている• 例題・課題ともできるだけ両方を対比させて用意する• ただし題材によってはどちらか片方だけになるものもある

Line[] line = new Line[100]; //JavaLine line[100]; //C++、ただし vector<Line>とか
の方がよい

4.1

GraphWin gw = new GraphWin(); //Java
GraphWin gw = GraphWin(); //C++

C++入門□ C++ --- Bjane Stroustrupに よって 、「C withclass」として始まった• 少し後で C++と Java を題材にもう少し具体的にやり• C からスムーズに移行できるオブジェクト指向言語として普及ますがその前に…

• C に (ほぼ) 上位互換な言語。型検査とかは厳しくなっ

3.5

抽象データ型再訪ている□ 抽象データ型 (Abstract Data Types, ADT) とは…

• +αされているもの…「オブジェクト指向」「例外」「テンプレート」など• 「型」とそれが持つ「操作群」を定義する。

• C の「裸のマシンがそのまま使える」に「よい構造化• その型の「内部構造」や「操作の実現」は外部からは隠蔽ができる」を追加• たとえば「＋」や「− 」などの演算を持つ「整数型」□ C++の設計思想…

のようなもの (例:ベクトル型とか) を自前で定義で• フルスピードで動く (C に負けない)きる。

• そのために足枷となるものは言語に導入しない• 初期の ADT 言語:• どのようなプログラミングスタイルでもサポートする

CLU、Alphard、…

• プログラマが分かっていてやることは禁止しない□ ADT の何が重要か?

• ユーザ定義型も組み込み型と同様な見た目で使える• 内部構造や実現を外部から見せない (カプセル化) →□ 例題:

モジュール間相互の依存関係を軽減。

• オブジェクト指向言語も「単純な」クラス (上に挙げた例のようなもの) は単なる ADT を実現している。

ADT 的な考え方は重要 (それなりに慣れが必要)。

2 つの数を読み込んで足す#include <iostream>using namespace std;
int main(void) {int x; cout << "x? "; cin >> x;
int y; cout << "y? "; cin >> y;cout << "x+y = " << (x+y) << ’\n’;}

9• 「cin」「cout」は入出力ストリーム

int y = sc.nextInt();
System.out.println("x+y = " + (x+y));• 「>>」「<<」(もともとはシフト演算子) を演算子定義により入出力演算として使っている}}• 「>>」の結果はまたストリームなので連続して使える• 多重定義により、「<<」を文字列用、整数用、実数用と多数用意している

4.3

オブジェクト指向言語□ オブジェクト指向言語とは?→いろいろな定義があると□ C++の処理系は…

思うがここでは一応次のように考える。

• Windows 用 …Borland C++、MS Visulal C++な ど
色々ある• 「オブジェクト指向」→プログラムが扱う対象をそれぞれ自律的な/完結した「もの」(オブジェクト) で• フリーなもの…GNU C++ (G++)。Unix (Linux) にあるとする「考え方」

は普通ついている。Windows なら Cygwin を入れれ• 「オブジェクト指向言語」→オブジェクト指向の考え方をサポートするようなプログラミング言語

ばそこに一緒にいれて動かせる (資料末尾も参照)□ 抽象的でよく分からない?

4.2

Java 入門

outstream ostream = ...;
outstream_println(ostream, "Hello, World.\n");↑関数名が長い↑操作対象も引数 : 従来のスタイル□ Java --- 「C++よりも安全なオブジェクト指向言語」として SunMicrosystems 社で開発された

outstream ostream = ...;
ostream.println("Hello, World.\n");↑オブジェクト. メソッド (引数…) : オブジェクト指向ぽい• 当初 Web ブラウザの上で動く「アプレット」のための言語として普及• この場合の「安全」…プログラムが悪さをできない
ような防壁がある (例:

まあそうだと思いますが…

• 同じことを 2 度言わなくて済む感じ

ファイルの読み書きができ• 同じことには同じ名前が使えるない、勝手にネットワーク接続ができない、など)• 名前空間の節約• 現在では通常のソフトウェア開発用言語として使われている□ あと、とりあえず関連する用語を紹介しておく• C(や C++) とは制御構造の構文が似ているだけであ□ 「クラス」→オブジェクトの種類に対応する構文要素。

とは別物• 新しい種類のオブジェクトを定義したければ、クラ□ Java の設計思想…

スを書く。

• C++の危険さ、複雑さを減少させ単純さを求める• C や C++との互換性は捨てたので言語仕様は単純化できた□ クラス定義に含まれるもの…

• オブジェクトはどういう動作 (メソッド、メンバ関
数) を持っているか• 安全さを第一とし、危険なことはどうやってもでき• オブジェクトはどういう属性を持っているか←インない

スタンス変数、メンバ変数• すべてのオブジェクトはヒープ上に割り当て、ごみ集めを行う• そのほかクラスはモジュールとしての役割りも←ク
ラスメソッド変数、static メソッド/変数• Smalltalk 的、伝統的なオブジェクト指向言語ふう• これらの代償として見た目は長く、動作は遅くなり □ 「インスタンス」→クラス定義に基づいて作り出されたがち「もの」(オブジェクト)□ 例題:

2 つの数を読み込んで足す□ 逆に見ると→「新しいモノ」を定義できるのがオブジェ
クト指向

import java.util.*;public class Sample1 {public static void main(String[] args) throws Exception {Scanner sc = new Scanner(System.in);
System.out.print("x? ");int x = sc.nextInt();
System.out.print("y? ");10• 世の中はさまざまなモノから成り立っている→それをそのまま言語に移せる (プログラムだから現実世
界よりは杓子定規だけど…) →考えやすい (考えやすさは重要)□ どういうモノを作るか考える→オブジェクト指向におけcase ’g’: cout << buf.get() << ’\n’; break;
}
}
}

る設計

• (1) どんなモノを作るか• (2) それぞれのモノはどういう操作/インタフェースを持つか□ クラス定義部分• 「class クラス名 ｛ … ｝;」がクラスを定義 (最後に「;」があることに注意!)• (3) その実装はどうするか←ある意味どうでもいい/
別建て• public:• (上記は ADT の考え方と言った方が正確← ADT は重要)

4.4

ADT の保護整数を出し入れするバッファ• 変数:「この型のデータは何と何を組みにしたものか」を表す□ 非常に簡潔な「もの」を例として取り上げて具体例を見てみましょう。

• public:

込むことで型検査が可能にのを作ります。

• クラス名と同名のメソッド→「コンストラクタ」(初• 数を入れる→「buf.put(値)」期化用の特別なメソッド)• 数を取り出す→「x = buf.get()」□ メソッド定義部分□ Mybuf1 は次の規則により保持するものとします。

• 実際に各メソッド (メンバ関数) 本体のコードを書く。

• 「最近に入れた 2 つの数を保持する (初期値は 0)。1
回取り出すと、一番最後に入れた値が出て来る。も• 各クラスに属する名前 (変数、メソッド) は「クラス
名::名前」が正式な (他と厳密に区別できる) 名前にう 1 回以上取り出すと、それより 1 つ前に入れた値なる ← 複数のクラスが同名のメソッドや変数を持が出て来る」。

てるから当然□ C++版の Mybuf1

class Mybuf1 { // memorize last two
int val1, val2;
public:Mybuf1();
void put(int x);
int get();};以下にメソッド (メンバ関数) のインタ

フェースを書く→これをヘッダファイルとして取り□ 数を「ある決まった規則で」保持するバッファというも#include <iostream>#include <cstdlib>
using namespace std;より前は private 部分→クラス外からは見えない (コンパイラは見るけれど) →カプセル化、• 各クラスのコード中では「クラス名::」は省略できる□ メイン部分• 1 文字読み取ってその値 (コマンド) に従って put、
get、終了のいずれかの動作を行う□ Java 版の Mybuf1
import java.util.*;public class MybufTest1 {public static void main(String[] args) {Mybuf1 buf = new Mybuf1();
Scanner sc = new Scanner(System.in);while(true) {System.out.print("? ");char c = sc.next().charAt(0);switch(c) {case ’q’: return;case ’p’: buf.put(sc.nextInt()); break;case ’g’: System.out.println(buf.get());}}}}

Mybuf1::Mybuf1() {val1 = val2 = 0;}
void Mybuf1::put(int x) {val2 = val1; val1 = x;}
int Mybuf1::get() {int x = val1; val1 = val2;
return x;}
int main(void) {Mybuf1 buf;
while(true) {char c; int v;
cout << "? "; cin >> c;switch(c) {case ’q’: return 0;case ’p’: cin >> v; buf.put(v); break;class Mybuf1 {int val1, val2;
public Mybuf1() {val1 = val2 = 0;}11

public void put(int x) {val2 = val1; val1 = x;}public int get() {int x = val1; val1 = val2;
return x;}

int Mybuf2::get() {(x に取り出す動作を記述)
return x;}(main は同様なので省略)□ Java はこういう感じ}(冒頭+main 部分省略)□ メイン部分

class Mybuf2 {(インスタンス変数の定義)• Scanner オブジェクトは入力時に便利な機能を提供• sc.next() は「1 単語」読む。その先頭の文字をpublic Mybuf1() {(初期化動作を記述)}
public void put(int x) {(入れる動作を記述)}
public int get() {(x に取り出す動作を記述)
return x;}

charAt(0) で取り出し、使用する。(Scanner には1 文字だけ読むというメソッドがないためちょっと不便。)□ クラス定義部分• Java ではヘッダと本体を分けるみたいな面倒くささはないし、前方参照も OK(やはり設計が新しい…)}• 「class クラス名 ｛ … ｝」の内側に変数もメソッドも書いてそれでおしまい• コンストラクタがクラスと同名というのは C++と同じ• クラス外から取れるものはそれぞれ public 修飾子

4.5□ 分数をあらわすクラスを用意する。方針としては 2 つの数 a、b で分数 a/b を表す。まず宣言部。

をつける□ では練習問題をやってみましょう。C++でも Java でも好きな方で (ただしできるだけ普段使っていない言語で)作成してください。

• 練 習 1: 「 最 近 3 つ の 値 を 覚 え る バッファ」をMybuf2 という名前で作りなさい。
• 練習 2: 「最後に入れた値を覚えるが、取り出すごとに覚えている値が 1 つずつ減るバッファ」を Mybuf3という名前で作りなさい。

• 練習 3:

分数電卓:C++版「入れた値が累計され、取り出すとその累計値が取れるバッファ」を Mybuf4 という名前で作りclass Rational { // a/b
int a, b;
int gcd(int x, int y);
public:Rational(int x);
Rational(int x, int y);
Rational operator+(const Rational& r) const;
Rational operator-(const Rational& r) const;
Rational operator*(const Rational& r) const;
Rational operator/(const Rational& r) const;
friend ostream& operator<<(ostream& o, Rational& r);
friend istream& operator>>(istream& i, Rational& r);};• private 部分には変数と補助関数 GCD(最大公約数)。

なさい。

• コンストラクタは値 1 つ (分母 1) と 2 つ。

□ C++はこういう感じ• 四則は演算子。「operator △」というメソッドを定義するとそれは「△」という演算子として呼べる。

(冒頭省略)• 今度は個々のオブジェクトは「有理数」というものをclass Mybuf2 {(インスタンス変数をここで定義)
public:Mybuf2();
void put(int x);
int get();};表すので、その状態は変化しない→演算においてパラ
メタもオブジェクト自身も変化しないことを const
で表している• 分母 0 になる場合は NaN (Not a Number)• 入出力は単独関数として定義した演算子「>>」と「<<」。(なぜ単独関数かというと、クラス istream や

Mybuf2::Mybuf2() {(初期化動作を記述)}
void Mybuf2::put(int x) {(入れる動作を記述)}

ostream に後からメソッドの追加はできないから。)
第 1 引数は入出力ストリーム。firend 宣言は「この関数は特別にこのクラス定義の中身 (private 部分)にアクセスできる」という意味。

12□ 実装部分。

cout << r << ’\n’;}}
int Rational::gcd(int x, int y) {while(x != y) if(x > y) x -= y; else y -= x;• 1 文字読んでコマンド文字によって動作を切替え。例:
return x;}? = 1 3 ← 1/3 を入れる
Rational::Rational(int x) { a = x; b = 1; }1/3
←現在値
Rational::Rational(int x, int y) {? + 1 6 ← 1/6 を足す
if(y == 0) { a = b = 0; return; }1/2←現在値
if(x == 0) { a = 0; b = 1; return; }q←おしまい
if(y < 0) { x = -x; y = -y; }if(x == 0) {a = x; b = y;
4.6 分数:Java 版} else if(x > 0) {a = x / gcd(x,y); b = y / gcd(x,y);□ C++版と同じ方針で実装。こちらはメイン側から。
} else {a = x / gcd(-x,y); b = y / gcd(-x,y);
import java.util.*;}}Rational Rational::operator+(const Rational& r) const { public class Sample3 {public static void main(String[] args)
return Rational(a*r.b + r.a*b, r.b*b);throws Exception {}Scanner sc = new Scanner(System.in);
Rational Rational::operator-(const Rational& r) const {Rational r = new Rational(1);return Rational(a*r.b - r.a*b, r.b*b);while(true) {}System.out.print("? ");Rational Rational::operator*(const Rational& r) const {String s = sc.nextLine();return Rational(a*r.a, r.b*b);if(s.charAt(0) == ’q’) return;}
Rational x = new Rational(s.substring(1));
Rational Rational::operator/(const Rational& r) const {switch(s.charAt(0)) {return Rational(a*r.b, r.a*b);
case ’=’: r = x; break;}case ’+’: r = r.add(x); break;ostream& operator<<(ostream& o, Rational& r) {case ’-’: r = r.sub(x); break;if(r.b == 0) o << "NaN";
case ’*’: r = r.mul(x); break;else o << r.a << ’/’ << r.b;case ’/’: r = r.div(x); break;return o;default: continue;}}
istream& operator>>(istream& i, Rational& r) {System.out.println(": " + r);
int a, b; i >> a >> b;}
r = Rational(a, b); return i;}}}• 面倒な「約分」の計算はコンストラクタで。

• こちらはコンストラクタに文字列を渡せるように作った。文字列から 1 つまたは 2 つの数値を読み取るの□ メイン部分。
#include <iostream>#include <string>#include <cstdlib>
using namespace std;に Scanner を使用• 動かし方は C++版と同じ。
□ Rational クラスは内容的にはほぼ同じ。

// ここにヘッダ部分// ここに実装部分

class Rational {int a = 0, b = 0; // b == 0 -> Not a Number
public Rational(String s) {try {Scanner sc = new Scanner(s);a = sc.nextInt();if(sc.hasNextInt()) b = sc.nextInt();else
b = 1;} catch(Exception ex) { }}public Rational(int x) { a = x; b = 1; }public Rational(int x, int y) {if(y == 0) { return; }if(x == 0) { a = 0; b = 1; return; }if(y < 0) { x = -x; y = -y; }if(x == 0) {a = x; b = y;

int main(void) {Rational r(1), x(0);while(true) {char c; cout << "? "; cin >> c;
if(c == ’q’) return 0;
switch(c) {case ’q’: return 0;case ’=’: cin>>x; r = x; break;case ’+’: cin>>x; r = r + x; break;case ’-’: cin>>x; r = r - x; break;case ’*’: cin>>x; r = r * x; break;case ’/’: cin>>x; r = r / x; break;default: continue;}

13能性があるかも検討すること。(実際に速くしてみられ} else if(x > 0) {a = x / gcd(x,y); b = y / gcd(x,y);} else {a = x / gcd(-x,y); b = y / gcd(-x,y);}

るとなおよいです。)• ヒ ン ト: C++で 時 間 計 測 を す る に は 標 準 関 数
clock() を利用するとよい。

}#include <ctime>
public Rational add(Rational r) {...
return new Rational(a*r.b + r.a*b, r.b*b);clock_t t1 = clock();}
計測したい処理public Rational sub(Rational r) {clock_t t2 = clock();
return new Rational(a*r.b - r.a*b, r.b*b);
double sec = double(t2-t1)/CLOCKS_PER_SEC;}↑秒単位に換算
public Rational mul(Rational r) {return new Rational(a*r.a, r.b*b);• ヒント: Java の時間計測には System クラスのク}
ラスメソッド System.currentTimeMillis() を利用
public Rational div(Rational r) {するとよい。
return new Rational(a*r.b, r.a*b);
long t1 = System.currentTimeMillis();}
計測したい処理public String toString() {long t2 = System.currentTimeMillis();if(b == 0) return "Nan"; else return a + "/" + b;
long dt = t2 - t1; ←所要時間 (msec)}
private int gcd(int x, int y) {while(x != y) if(x > y) x -= y; else y -= x;□ (3) Rational オブジェクトの例題を動かし、その「足return x;
し算」などの演算が int や double などの数値の演算の}何倍遅いか計測してみよ。計測する前に予測し、予測が}どれくらい合っていたか検討すること。(予測はあてずっ• 演算子定義はないので適当なメソッド名をつける。

ぽうではなく、何らかの根拠が必要)。

• 出力も演算子はないので toString() で文字列への変換方法を定める (文字列が必要なときは自動的に□ (4) Rational オブジェクトの実装にはいろいろと「無駄」がある。どこに無駄があるかを検討し、そこを改良これが呼ばれる)。

して速くなったかどうか計測し確認してみなさい。どれだけ速くなるかきちんと予測すること。

5

第 1 回レポート課題

6□ 下記 (1)〜(4) のうちから 1 つ以上課題を選んで製作や実習のための言語処理系

実験を行い、結果をレポートせよ。やったことの説明や □ Java --- Sun からフリーでダウンロードできます。
書いたコードだけでなく、計測の場合は方法まで説明し、きちんと考察まで書くこと。言語は特に指定していない
場合、C++でも Java でもよい (両方でやって比べてみるとなおよい)。期限はリーダから指定。

http://java.sun.com/javase/ja/6/download.html

ここから「JDK 6 Update 20」を取得してください。

Windows、Solaris、Linux 用があります。NetBeans は□ (1) Mybuf の練習問題について、さらに次のものから興別にいらないです (統合開発環境なので入れてみたけれ味を持ったものを 1 つ以上作ってみなさい。

ばどうぞ)。

• a. 最近 10 個のものを覚えている。取り出すごと □ C++ --- Visual C++とかは売りものですが、Linux や
Mac OS-X なら g++が入っています。また、Windows でもに 1 つ前の値に遡ることができる (10 回戻るとそれ
cygwin を入れて、その時「Devel」(developer) パッ
ケージを選択しておけば入ります。入れる時の操作の解以上は同じ値になる)。
説はここが分かりやすいです。
• b. 入れた値のうち最近 10 個の値の合計を覚えている。取り出すごとに 1 つ前の時点での 10 個の合計

http://sohda.net/cygwin/setup.htmlに戻ることができる (10 回戻るとそれ以上は同じ値□ JavaScript --- IE に「互換品」である JScript が付属していますが、プログラムを動かすときは FireFox のJavaScript コンソール (Error コンソール) があると
• c-d. 上の a と b の「10」それぞれの代わりにサイ便利ですので、ぜひこの際入れてみてください。
ズ N をコンストラクタで指定可能にしたもの。
になる)。

□ (2) 課題 (1) で作成した容量 10 ないし N のバージョンについて、演習問題でやった単純なものと比べてどれだけ速度が遅いか検討しなさい。それより速くできる可14

http://www.mozilla.org/