高知大学教育学部の情報数学のテキスト
1
チューリング・マシン
チューリング・マシンは計算模型のひとつで計算機を数学的に議論するための、単純化・理想化 された仮想機械である。1936 年にイギリスの数学者アラン・チューリングの論文「計算可能数に ついて──決定問題への応用」で発表された。 チューリング・マシンは 1 本のテープと、このテープ上の記号を読み書きするためのヘッドを一 つ持つだけの仮想機械である。チューリング・マシンには有限個の「状態」があって、各時刻でそ のうち何れか一つの状態をとることができる。 チューリング・マシンのプログラムは次の五つの記号で表される命令のリストで表現されます。 一組の命令は <現在の状態、読み込んだカラー、書き込むカラー、新しい状態、動く方向> の形で表されます。 このチューリング・マシンではスペース(または空白)のカラーはBで表 現します。また、動く方向はLとRで示します。 チューリング・マシンの処理は、次の六つのス テップの繰り返しです。 ■ ヘッドの現在いるセルの記号を読む ■ プログラムの全体を調べて、現在の状態である<現在の状態、読み込んだカラー>で始ま る命令セットを見つけだす。該当する命令セットがなければ、停止する。見つかったら、その命令 セットの<書き込むカラー、新しい状態、動く方向>を確認する。 ■ ヘッドの状態を<新しい状態>にする。 ■ 現在のセルの内容を<書き込むカラー>の記号に変える。 ■ <動く方向>によって指定された位置にヘッドを移動する。 ■ 最初のステップに戻る。 TURING.EXE でプログラミングしてみましょう。 TURING.EXE を立ち上げます。セルとヘッドの初期状態はメニューの set の table で指定します。メニューの set の table をク リックします。
このダイアログボックスで、初期状態と初期データを入力します。通常、初期状態は 0 ですの で、デフォルトのままで良いです。初期データは、1行に一文字入力します。例えば、次のように,、 001101011 と入力します。
OK のボタンを押すと次のようになります。
上図のような初期データが与えられたとき 0 をすべて 1 に変えるプログラムを考えて見ます。 先頭から右に一つずつ調べて、0 があれば 1 に変え、1 であればそのままにします。一つ処理し た後も同じことをすればいいので、状態を変える必要はありません。従って、 0 0 1 0 R 0 1 1 0 R とプログラミングすればいいです。大して知的なことをしていないので、プログラムも簡単です。 このプログラムを左側のメモにプログラムを打ち込みます。プログラムは直接編集することも保存 することも読み込むこともできます。命令リストの最後は何も書き込まないで下さい。改行だけの 行を作らないという意味です。 Start のボタンを押せば、チューリング・マシンが動き出します。 すべての0が1になりました。 同様に、0 と 1 をすべて入れ替えるプログラムは、 0 0 1 0 R 0 1 0 0 R
とプログラミングすればいいです。初期データを入力し直して、実行してみて下さい。 <例題> SAMPLE DATA : XXXXX が与えられた時、XXXXXX のように、最後に一つ X を 追加するプログラムを作れ。 解答例: 0 X X 0 R 0 B X 1 R これを 0 X X 0 R 0 B X 0 R とすると何が起こるか確かめなさい。 問題:例えば SAMPLE DATA : XXXXX が与えられた時、XXXXXXX のように、最後に2つ X を追加するプログラムを作れ。 <例題> SAMPLE DATA : XXX が与えられた時、BBBBXXX のように、X 達を移動するプ ログラムを作れ。 解答例: 0 X B 1 R 1 X X 1 R 1 B B 2 R 2 B X 3 L 3 B B 4 L 4 X X 4 L 4 B B 0 R 2 X X 2 R 3 X X 3 L これは次のように考えればよい。初期状態 0 で X を読めば、空白 (B) を書き込み、「X を抱えて 置く場所を見つけるために区切りの空白を見つけようと右に移動している状態 1 」として、右に 移動する。状態 1 で X を読めば、X を読み飛ばして、同じ状態のまま右に移動する。状態 1 で B (空白) を読めば、B (空白) を読み飛ばして、「X を抱えて置く場所を見つけるために最初の空白 を見つけようと右に移動している状態 2 」として、右に移動する。状態 2 で B (空白) を読めば、 X を書き込み、「次の X を見つけるために区切りの空白を見つけようと左に移動している状態 3 」 として、左に移動する。状態 3 で B (空白) を読めば、B (空白) を読み飛ばして、「次の X を見 つけるために最初の空白を見つけようと左に移動している状態 4 」として、左に移動する。状態 4 で X を読めば、X を読み飛ばして、同じ状態のまま左移動する。状態 4 で B (空白) を読めば、 B (空白) を読み飛ばして、状態を 0 にして右に移動する。 0 X B 1 R 1 X X 1 R 1 B B 2 R 2 B X 3 L 3 B B 4 L
4 X X 4 L 4 B B 0 R これでプログラムが出来たと思って実行してみると と、「X を抱えて置く場所を見つけるために最初の空白を見つけようと右に移動している状態 2 」 で X を読んだところで停止します。すでに移動した X がある場合の考察を忘れていました。状態 2 で X を読めば、X を読み飛ばして、同じ状態のまま右移動するという命令 2 X X 2 R を追加して、再び実行します。 今度は、「次の X を見つけるために区切りの空白を見つけようと左に移動している状態 3 」で、X を読んだところで停止します。ここでも、すでに移動した X がある場合の考察を忘れていました。 3 X X 3 L を追加して、再び実行します。
今度は旨くいきました。このようにプログラムを作るときは、最初からすべてが完全に出来ること はまずないです。何度も何度も失敗し、あらゆる失敗を経験した後、プログラムが作れるようにな ります。以下で学ぶどのプログラミング言語を使う場合でも、プログラミングが出来るようになる かどうかは、何度も何度も失敗し、あらゆる失敗を経験するまで我慢出来るかどうかです。 問題: SAMPLE DATA : XXX が与えられた時、BBBBXXXXXX のように、X の個数の2倍 の X を描くプログラムを作れ。 例えば SAMPLE DATA : BXXXX が与えられた時、 0 B B 1 R 1 X X 1 R 1 B B 2 L 2 X X 2 L 2 B B 1 R のプログラムを実行すると何が起こるか確かめなさい。ここで、B は空白を示しています。 例えば SAMPLE DATA : BXXXX が与えられた時、 0 B B 1 R 1 X X 1 R 1 B X 2 L 2 X X 2 L 2 B B 1 R のプログラムを実行すると何が起こるか確かめなさい。ここで、B は空白を示しています。 <例題> SAMPLE DATA : ()(()()) や (()(() が与えられた時、括弧がうまく対応しているかど うか判定できるプログラムを作れ。 解答例: 0 ( Y 1 R 0 X X 0 R 0 ) Z 3 R 0 B B 5 R 1 ( ( 1 R
1 ) X 2 L 1 X X 1 R 1 B B 4 R 2 ( X 1 R 2 X X 2 L 2 Y Z 0 R 2 Z Z 3 R 5 B O 6 R 6 B K 7 R 3 ( ( 3 R 3 ) ) 3 R 3 B B 8 R 8 B N 9 R 9 B O 10 R 4 B B 8 R 実行すると となります。 少し長いですが、2 進数の足し算をするプログラムを作ってみます。
0 0 Z 1 R 0 1 W 1 R 1 0 0 1 R 1 1 1 1 R 1 + + 1 R 1 = = 2 L 2 0 Y 4 L 2 1 Y 5 L 3 0 Y 5 L 3 1 Y 6 L 4 + + 7 L 5 + + 8 L 6 + + 9 L 7 X X 7 L 8 X X 8 L 9 X X 9 L 7 0 X 10 R 7 1 X 11 R 8 0 X 11 R 8 1 X 12 R 9 0 X 12 R 9 1 X 13 R 10 0 0 10 R 10 1 1 10 R 10 + + 10 R 10 = = 10 R 10 X X 10 R 10 Y Y 10 R 11 0 0 11 R 11 1 1 11 R 11 + + 11 R 11 = = 11 R 11 X X 11 R 11 Y Y 11 R 12 0 0 12 R 12 1 1 12 R 12 + + 12 R 12 = = 12 R 12 X X 12 R 12 Y Y 12 R 13 0 0 13 R 13 1 1 13 R 13 + + 13 R
13 = = 13 R 13 X X 13 R 13 Y Y 13 R 10 B B 14 L 11 B B 15 L 12 B B 16 L 13 B B 17 L 14 0 B 18 R 14 1 B 19 R 15 0 B 20 R 15 1 B 21 R 16 0 B 22 R 16 1 B 23 R 17 0 B 24 R 17 1 B 25 R 18 B 0 14 L 19 B 1 14 L 20 B 0 15 L 21 B 1 15 L 22 B 0 16 L 23 B 1 16 L 24 B 0 17 L 25 B 1 17 L 14 B B 14 L 15 B B 15 L 16 B B 16 L 17 B B 17 L 14 = = 26 R 15 = = 27 R 16 = = 28 R 17 = = 29 R 26 B 0 2 L 27 B 1 2 L 28 B 0 3 L 29 B 1 3 L 2 = = 2 L 3 = = 3 L 2 Y Y 2 L 3 Y Y 3 L 2 + + 30 L 3 + + 31 L 30 X X 30 L 31 X X 31 L
30 Z X 32 R 30 W X 33 R 31 Z X 33 R 31 W X 34 R 7 Z X 32 R 7 W X 33 R 8 Z X 33 R 8 W X 34 R 9 Z X 34 R 9 W X 35 R 32 X X 32 R 32 + + 32 R 32 Y Y 32 R 32 = = 32 R 32 0 0 32 R 32 1 1 32 R 33 X X 33 R 33 + + 33 R 33 Y Y 33 R 33 = = 33 R 33 0 0 33 R 33 1 1 33 R 34 X X 34 R 34 + + 34 R 34 Y Y 34 R 34 = = 34 R 34 0 0 34 R 34 1 1 34 R 35 X X 35 R 35 + + 35 R 35 Y Y 35 R 35 = = 35 R 35 0 0 35 R 35 1 1 35 R 32 B B 36 L 33 B B 37 L 34 B B 38 L 35 B B 39 L 36 0 B 40 R 36 1 B 41 R 37 0 B 42 R 37 1 B 43 R 38 0 B 44 R
38 1 B 45 R 39 0 B 46 R 39 1 B 47 R 40 B 0 36 L 41 B 1 36 L 42 B 0 37 L 43 B 1 37 L 44 B 0 38 L 45 B 1 38 L 46 B 0 39 L 47 B 1 39 L 36 B B 36 L 37 B B 37 L 38 B B 38 L 39 B B 39 L 36 = = 48 R 37 = = 49 R 38 = = 50 R 39 = = 51 R 48 B 0 -1 R 49 B 1 -1 R 50 B 0 52 R 51 B 1 52 R 52 0 0 52 R 52 1 1 52 R 52 B B 53 L 53 0 B 54 R 53 1 B 55 R 54 B 0 53 L 55 B 1 53 L 53 B B 53 L 53 = = 56 R 56 B 1 -1 R 30 0 X 57 R 30 1 X 58 R 31 0 X 58 R 31 1 X 59 R 57 X X 57 R 57 + + 57 R 57 Y Y 57 R 57 = = 57 R 57 0 0 57 R 57 1 1 57 R
58 X X 58 R 58 + + 58 R 58 Y Y 58 R 58 = = 58 R 58 0 0 58 R 58 1 1 58 R 59 X X 59 R 59 + + 59 R 59 Y Y 59 R 59 = = 59 R 59 0 0 59 R 59 1 1 59 R 57 B B 60 L 58 B B 61 L 59 B B 62 L 60 0 B 63 R 60 1 B 64 R 61 0 B 65 R 61 1 B 66 R 62 0 B 67 R 62 1 B 68 R 63 B 0 60 L 64 B 1 60 L 65 B 0 61 L 66 B 1 61 L 67 B 0 62 L 68 B 1 62 L 60 B B 60 L 61 B B 61 L 62 B B 62 L 60 = = 69 R 61 = = 70 R 62 = = 71 R 69 B 0 2 L 70 B 1 2 L 71 B 0 3 L 4 0 0 72 L 4 1 1 72 L 5 0 0 73 L 5 1 1 73 L 6 0 0 74 L 6 1 1 74 L 72 0 0 72 L
72 1 1 72 L 73 0 0 73 L 73 1 1 73 L 74 0 0 74 L 74 1 1 74 L 72 + + 75 L 73 + + 76 L 74 + + 77 L 75 X X 75 L 76 X X 76 L 77 X X 77 L 75 0 X 10 R 75 1 X 11 R 76 0 X 11 R 76 1 X 12 R 77 0 X 12 R 77 1 X 13 R 75 Z X 78 R 75 W X 79 R 76 Z X 79 R 76 W X 80 R 77 Z X 80 R 77 W X 81 R 78 X X 78 R 78 + + 78 R 78 0 0 78 R 78 1 1 78 R 78 Y Y 78 R 78 = = 78 R 79 X X 79 R 79 + + 79 R 79 0 0 79 R 79 1 1 79 R 79 Y Y 79 R 79 = = 79 R 80 X X 80 R 80 + + 80 R 80 0 0 80 R 80 1 1 80 R 80 Y Y 80 R 80 = = 80 R 81 X X 81 R 81 + + 81 R
81 0 0 81 R 81 1 1 81 R 81 Y Y 81 R 81 = = 81 R 78 B B 82 L 79 B B 83 L 80 B B 84 L 81 B B 85 L 82 0 B 86 R 82 1 B 87 R 83 0 B 88 R 83 1 B 89 R 84 0 B 90 R 84 1 B 91 R 85 0 B 92 R 85 1 B 93 R 86 B 0 82 L 87 B 1 82 L 88 B 0 83 L 89 B 1 83 L 90 B 0 84 L 91 B 1 84 L 92 B 0 85 L 93 B 1 85 L 82 B B 82 L 83 B B 83 L 84 B B 84 L 85 B B 85 L 82 = = 94 R 83 = = 95 R 84 = = 96 R 85 = = 97 R 94 B 0 98 L 95 B 1 98 L 96 B 0 99 L 97 B 1 99 L 98 = = 98 L 99 = = 99 L 98 Y Y 98 L 99 Y Y 99 L 98 0 Y 78 R 98 1 Y 79 R 99 0 Y 79 R
99 1 Y 80 R 98 + + -1 R 99 + + 100 R 100 Y Y 100 R 100 = = 100 R 100 0 0 100 R 100 1 1 100 R 100 B B 101 L 101 0 B 102 R 101 1 B 103 R 102 B 0 101 L 103 B 1 101 L 101 B B 101 L 101 = = 104 R 104 B 1 -1 R このプログラムをコピーします。 初期データをセットします。
実行すると
となります。
実行すると となります。 このように数値計算も努力すればプログラミングできます。しかし複雑なプログラムを作るのは 大変です。私は大きな模造紙にプログラムの流れを描いて、チューリング・マシンの動きを図示し ながら、丸一日かかってこのプログラムを作り上げました。チューリング・マシンは実際にプログ ラミングする機械ではなく、計算機の原理を示す機械です。 問題: 2 進数の 2 の補数を計算するプログラムを作れ。 次は現実の計算機を考察します。
2
機械語
和田秀男著「計算数学」朝倉書店 第二章「機械語」に載っている単純なコンピュータの仕組み をとうして、機械語とアセンブリ言語を見てみよう。 このコンピュータにはメモリ(記憶装置)として8192語(1語=16ビット)があり、演算 装置があり、演算装置で計算した結果を記憶する16ビットからなるアキュムレータと次にメモリ の何処を実行するかを示す16ビットからなるプログラムカウンタがある。上のソフトには演算装 置は表示されていない。アキュムレータとプログラムカウンタが左側に、メモリが中央にある。 メモリにはコンピュータのプログラムとデータを入れる。コンピュータのプログラムをメモリに 入れる方式がプログラム内蔵方式といわれ、フォン・ノイマンが考案したと言われている。 このコンピュータの命令はオペコードと呼ばれる命令の種類を表す数とオペランドと呼ばれるメ モリの番地を示す数の組で表される。 例えば15と−7の和をこのコンピュータで計算したいときは次のようにする。 TinyCom を起ち上げる。 「メモリーの修正」ボタンを使って、メモリにデータをセットしていく。「メモリーの修正」ボ タンの右のエディットに「0000:0010000000000100」と入力する。ここで、: の左の4桁が10進 数で番地を示し、: の右の16桁が2進数でその番地の内容を示している。「メモリーの修正」ボタンをクリックする。
メモリの一番上番地が「0000:0010000000000100」と変化した。ここで、: の左の4桁が10進数 で番地を示し、: の右の16桁が2進数でその番地の内容を示している。
「メモリーの修正」ボタンの右のエディットに「0001:0110000000000101」と入力する。
メモリの2番目番地が「0001:0110000000000101」と変化した。 この操作を繰り返し、メモリが 0000:0010000000000100 0001:0110000000000101 0002:0100000000000110 0003:1010000000000000 0004:0000000000001111 0005:1111111111111001 0006:0000000000000000 となるようにする。 最初の4個が命令の列で、残りがデータです。データは16ビットで整数を表し、正の整数は最 初のビットを 0 とし残り15ビットを使って2進数で表します。負の整数は2の補数表示という 方式を使っていて、例えば -7 を表すには次のように変形して表現を求めます。 0000000000000111 7 を2進数表示する 1111111111111000 0 と 1 を入れ替える 1111111111111001 1 を足す
負の整数はこの操作で表現を求めます。これを2の補数表示といいます。2の補数表示の利点は 数の表示が一意になることです。つまり、最初のビットが0なら正、最初のビットが1なら負と し、残りの15ビットで絶対値を表現することにすると0の表現がの表現が 0000000000000000 と 1000000000000000 と二通り出来てしまい不便です。 このプログラムの意味は 0000:0010000000000100 4 番地のデータをアキュムレータに入れる 0001:0110000000000101 5 番地のデータをアキュムレータに加える 0002:0100000000000110 6 番地にアキュムレータの内容を保存 0003:1010000000000000 停止する 0004:0000000000001111 15 を表す 0005:1111111111111001 -7 を表す 0006:0000000000000000 0 を表す です。 このプログラムを実行するには、プログラムカウンタが 0000000000000000 になっていることを 確かめて、スタートのボタンをクリックします。 6 番地が 0000000000001000 となりました。すなわち、15 +−7 = 8 です。プログラムカウンタ が 0000000000000011 になりました。停止しろという命令で止まりました。 6 番地を 0000000000000000 にもどし、プログラムカウンタも 0000000000000000 に戻し、
ステップのボタンをクリックします。 アキュムレータが 0000000000001111 になり、プログラムカウンタが 0000000000000001 になりま した。0番地の命令が実行されました。ステップのボタンをクリックするとプログラムカウンタが 指している番地の命令が1つ実行されます。 このコンピュータの命令は7つしかないから1から7までの通し番号が付けられていて、2進法 で表せば3ビットで表せ、メモリの番地は0から8191(= 213− 1)までなので13ビットで 表せる。それで、1語16ビットのうち左3ビットに命令の番号を入れ、右の残り13ビットにメ モリの番地を入れることにより、1つの命令を1語で表現している訳です。 コンピュータにとってはこれでよいが、人間には2進数の数字の羅列を読むのは辛いものがある ので、人間に分かり易い言語としてアセンブリ言語が作られた。オペコードやオペランドを英単語 や文字で表します。オペコードをアセンブリ言語で表したのが次の表の load, store, add, subtract, stop, jump および jump minus である。番地は文字や文字列で表す。
このコンピュータのオペコードは7つで、次の表の様になっている。 10進数 2進数 アセンブリコード 説明 1 001 load load M で M 番地の内容をアキュムレータにコピー 2 010 store store M でアキュムレータの内容と M 番地にコピー 3 011 add add M で M 番地の内容とアキュムレータの内容を 加え、結果をアキュムレータにコピー 4 100 subtract subtract M でアキュムレータの内容から M 番地 の内容を引き、結果をアキュムレータにコピー 5 101 stop stop でコンピュータの動作は停止する 6 110 jump jump M で無条件で M の番地へ移動
7 111 jump minus jump minus M でアキュムレータが負の時
M の番地へ移動 アセンブリ言語では上のプログラムを次のように表現する。
ラベル オペコード オペランド 対応する機械語 load A 0000:0010000000000100 add B 0001:0110000000000101 store C 0002:0100000000000110 stop 0003:1010000000000000 A 15 0004:0000000000001111 B -7 0005:1111111111111001 C 0 0006:0000000000000000 アセンブリ言語の設計はある程度の自由度があるが、和田秀男著「計算数学」朝倉書店 第二章 「機械語」に載っている単純なコンピュータのアセンブリ言語、および TinyCom のアセンブリ言 語の命令は ラベル オペコード オペランド からなり、ラベルやオペランドは空白のこともある。ラベルが空白でもラベル欄には半角スペース を入れる。ラベルを記入するときはその前に空白を入れてはいけません!!! TinyCom を起ち上げ、 右側のメモを見るとアセンブリ言語のプログラムらしきものが書いてある。これはこのコンピュー タの7つの命令をすべて使ったプログラムを書いているだけで無意味なプログラムです。これを消 して、上のアセンブリ言語のプログラムを打ち込む。文字も空白もすべて半角文字や半角スペース を使います。ラベルを記入するときはその前に空白を入れてはいけません!!!
右下にある「アセンブル」ボタンをクリックします。 メモリに右側のアセンブリ言語のプログラムを機械語に翻訳されたプログラムがセットされまし た。アセンブリ言語のプログラムを機械語に翻訳するプログラムをアセンブラと言います。コン ピュータを使うのが随分楽になりました。今後は TinyCom のアセンブリ言語のプログラムを調べ ます。 足し算をしましたが、引き算をするには subtract 命令を使います。subutract M と命令すれば、 アキュムレータから M とラベル付けされた番地の内容を引き、その結果をアキュムレータに入れ ます。例えば、A + B− C を D に入れる (D ← A + B − C) にはアセンブリ言語で次のようにプ ログラミングします。
ラベル オペコード オペランド 対応する機械語 load A 0000:0010000000000101 add B 0001:0110000000000110 subtract C 0002:1000000000000111 store D 0003:0100000000001000 stop 0004:1010000000000000 A 6 0005:0000000000000110 B 9 0006:0000000000001001 C -8 0007:1111111111111000 D 0 0008:0000000000000000 ラベル A, B, C, D を付けられた番地には好きな値を入れて下さい。今の場合は、6 + 9− (−8) を計算しています。アセンブルし スタートボタンをクリックすると ラベル D で指示された8番地が 0000000000010111 になりました。6 + 9− (−8) = 23 を計算し ました。 問題:A← 2A − B − C のプログラムを作れ。 A番地の内容を16倍して、その結果を A 番地に保存するには次のようにプログラミングします。
ラベル オペコード オペランド load A add A store A add A store A add A store A add A store A stop A -3 A には好きな数字を入れます。add を15回使うよりはプログラムが短くなっています。 A 番地の内容と B 番地の内容を入れ替えるには補助の番地 C を使って、次のようにします。 ラベル オペコード オペランド load A store C load B store A load C store B stop A 27 B -18 C 0 A, B, C には好きな数字を入れます。これを ラベル オペコード オペランド load A store B load B store A stop A 27 B -18 としては駄目です。 問題:A← B ← C ← A のプログラムを作れ。つまり、3つの値を入れ替えるプログラムを 作れ。 jump minus M という命令は、アキュムレータが負の時、プログラムの行の先頭に M と書か れているところ(ラベルが M である命令の行)を次に実行しなさいと言う、条件分岐命令です。
jump M という命令は、無条件にプログラムの行の先頭に M と書かれているところ(ラベルが M である命令の行)を次に実行しなさいと言う、無条件分岐命令です。これらの命令を使うと A 番 地の内容の絶対値を B 番地に保存する(B ← |A|)プログラムは次のように書くことが出来ます。 ラベル オペコード オペランド load A jump minus L0 L1 store B stop L0 load Zero subtract A jump L1 Zero 0 A -95 B 0 ラベル Zero 番地は 0 でなければなりませんが、A, B 番地は好きな値で良いです。 A番地の内容が -95 の様に負の数であれば、load A を実行すれば、アキュムレータに負の数が セットされるので次の jump minus L0 の命令で、L0 とラベル付けされた番地の命令 load Zero を 次に実行します。一方、A番地の内容が 95 の様に正の数であれば、load A を実行すれば、アキュ ムレータに正の数がセットされるので次の jump minus L0 の命令は無視され、次の store B の命 令が次に実行されます。また jump L1 の命令を実行すると次は L1 とラベル付けされた番地の命 令 store B が次に実行されます。このように jump minus と jump だけがプログラムの流れを変え ます。 A 番地の値と B 番地の値の小さい方を C 番地に保存するプログラムは次のようになります。 ラベル オペコード オペランド load A subtract B jump minus L0 load B L1 store C stop L0 load A jump L1 A -9 B -12 問題:A 番地の値と B 番地の値の大きい方を C 番地に保存するプログラムを作れ。 このように色々なことが出来ます。もっと複雑なプログラミングの例が知りたければ和田秀男著 「計算数学」朝倉書店 第二章「機械語」を読んで下さい。かけ算や割り算やユークリッドの互除 法による最大公約数を求めるプログラムやサブルーチンのプログラムまで載っています。
現実のコンピュータは構造が非常に複雑です。Knuth の THE ART OF COMPUTER PRO-GRAMMING で説明されている仮想のコンピュータは次の図の様になっています。
このコンピュータで 500 個の素数を求めるプログラムは * 500 個の素数の表の印刷 PRIME.PRO * L EQU 500 これから探す素数の個数 PRINTER EQU 18 印字機の機番 PRIME EQU -1 素数表の記憶領域
BUF0 EQU 800 BUFFER[0] の記憶領域
BUF1 EQU BUF0+25 BUFFER[1] の記憶領域
ORIG 600
START IOC 0(PRINTER) 改頁
LD1 =1-L= P1. 表の作成開始. J<--1. LD2 =3= N<--3. 2H INC1 1 P2. N は素数, J<--J+1. ST2 PRIME+L,1 PRIME[J]<--N. J1Z 2F P3. すでに 500 個見つけたか. 4H INC2 2 P4. N を進める. ENT3 2 P5. K<--2. 6H ENTA 0 P6. PRIME[K] | N か. ENTX 0,2 DIV PRIME,3 JXZ 4B R=0 か.
CMPA PRIME,3 P7. PRIME[K] の方が大きいか.
INC3 1 P8. K を進める.
JG 6B Q>PRIME[K] なら飛び越し,
JMP 2B さもなければ N は素数.
2H OUT TITLE(PRINTER) P9. 見出しの印刷
ENT4 BUF1+10 Set B<--1.
ENT5 -50 Set M<--0.
2H INC5 L+1 M を進める.
CHAR STX 0,4(1:4) DEC4 1 DEC5 50 (正の範囲で rI5 の内容が 50 個ずつ減る) J5P 4B OUT 0,4(PRINTER) P11. 一行印刷. LD4 24,4 緩衝領域の切替. J5N 2B rI5=0 なら完了. HLT
* INITIAL CONTENTS OF TABLES AND BUFFERS ORIG PRIME+1
CON 2 最初の 2.
ORIG BUF0-5
TITLE ALF FIRST 表題の文字情報.
ALF FIVE ALF HUND ALF RED P ALF RIMES ORIG BUF0+24 CON BUF1+10 各緩衝領域が他の緩衝領域を参照. ORIG BUF1+24 CON BUF0+10 END START 終わり. となります。これを MIX にロードし アセンブルし
実行し
Printer を表示すると
となります。別のプログラムの例は ORIG 0
X CON 24 Y CON 16 ORIG 20 BUFFER CON 0 ORIG 100 START IOC 0(18) L1 ENTA 0 LDX X DIV Y JXZ L2 LDA Y STA X STX Y JMP L1 L2 LDA Y CHAR STX BUFFER OUT BUFFER(18) HLT END START です。これは24と16の最大公約数を計算しています。 とプログラムを打ち込み、
アセンブルし、
実行し、Printer を表示すると
となります。
これも今となっては古いコンピュータです。MIX のプログラミングは、Donard E. Knuth 「The Art of Computer Programming Volumes 1-4A」 にあります。この本はコンピュータの世界では
最も有名な本です。翻訳もあります。アルゴリズムを集大成した本で、計算機科学者のバイブルで す。Donard E. Knuth は、世界中の人が使っている複雑な数式や化学式を含む文章や楽譜や色々 な言語の文章を作るためのソフト TeX の作者としても有名です。 現実に作られたパソコンの CPU の解説は、「はじめて読む8086」アスキー出版局。198 7や「はじめて読む486」アスキー出版局。1994がありますが、これも古いです。昔はこん な本を読んで、私もコンピュータの勉強をしましたが、今では機械語やアセンブリ言語の勉強をす る必要はほとんどなくなりました。唯、機械語を知っていれば、C 言語のポインタやコアやスレッ ドの仕組みがそれなりに理解できます。
次は、昔私が作った Borland の Turbo Assembler TASM 5.0J による 8 人の女王の問題を解 くプログラムです。左側が 8086 の機械語で右側がアセンブリ言語によるプログラムです (勿論 PENTIUM でも同じアセンブリ言語のプログラムで動きます) 。対称性は考慮していません。 8 人の女王の問題を拡張した N 人の女王の問題を 8 人以下で解けます。 1 0000 .MODEL SMALL 2 0000 .STACK 200H 3 0000 .DATA 4 0000 09*(??) MM DB 9 DUP(?) 5 0009 09*(00) JJ DB 9 DUP(0) 6 0012 10*(00) KK DB 16 DUP(0) 7 0022 10*(00) LL DB 16 DUP(0) 8 0032 ???? I DW ? 9 0034 ???? N DW ? 10 0036 ???? M DW ? 11 0038 .CODE 12 0000 QUEENSSTART: 13 0000 B8 0000s MOV AX,@DATA 14 0003 8E D8 MOV DS,AX 15 0005 B4 02 MOV AH,2 16 0007 B2 3F MOV DL,’?’ 17 0009 CD 21 INT 21H 18 000B B4 01 MOV AH,1 19 000D CD 21 INT 21H 20 000F 2C 30 SUB AL,’0’ 21 0011 B4 00 MOV AH,0 22 0013 A3 0036r MOV [M],AX 23 0016 B4 02 MOV AH,2 24 0018 B2 0D MOV DL,0DH 25 001A CD 21 INT 21H 26 001C B2 0A MOV DL,0AH 27 001E CD 21 INT 21H 28 0020 C7 06 0032r 0001 MOV [I],1 29 0026 C7 06 0034r 0000 L3: MOV [N],0 30 002C FF 06 0034r L4: INC [N]
31 0030 A1 0034r MOV AX,[N] 32 0033 3B 06 0036r CMP AX,[M] 33 0037 77 7F JA L6 34 0039 BB 0009r MOV BX,OFFSET JJ 35 003C 03 1E 0034r ADD BX,[N] 36 0040 80 3F 01 CMP BYTE PTR [BX],1 37 0043 74 E7 JE L4 38 0045 BB 0012r MOV BX,OFFSET KK 39 0048 03 1E 0032r ADD BX,[I] 40 004C 2B 1E 0034r SUB BX,[N] 41 0050 03 1E 0036r ADD BX,[M] 42 0054 80 3F 01 CMP BYTE PTR [BX],1 43 0057 74 D3 JE L4 44 0059 BB 0022r MOV BX,OFFSET LL 45 005C 03 1E 0032r ADD BX,[I] 46 0060 03 1E 0034r ADD BX,[N] 47 0064 4B DEC BX 48 0065 80 3F 01 CMP BYTE PTR [BX],1 49 0068 74 C2 JE L4
50 006A A1 0034r MOV AX,[N]
51 006D BB 0000r MOV BX,OFFSET MM 52 0070 03 1E 0032r ADD BX,[I] 53 0074 88 07 MOV [BX],AL 54 0076 BB 0009r MOV BX,OFFSET JJ 55 0079 03 1E 0034r ADD BX,[N] 56 007D C6 07 01 MOV BYTE PTR [BX],1 57 0080 BB 0012r MOV BX,OFFSET KK 58 0083 03 1E 0032r ADD BX,[I] 59 0087 2B 1E 0034r SUB BX,[N] 60 008B 03 1E 0036r ADD BX,[M] 61 008F C6 07 01 MOV BYTE PTR [BX],1 62 0092 BB 0022r MOV BX,OFFSET LL 63 0095 03 1E 0032r ADD BX,[I] 64 0099 03 1E 0034r ADD BX,[N] 65 009D 4B DEC BX
66 009E C6 07 01 MOV BYTE PTR [BX],1
67 00A1 FF 06 0032r INC [I]
68 00A5 A1 0032r MOV AX,[I]
69 00A8 3B 06 0036r CMP AX,[M]
70 00AC 77 03 JA L5
71 00AE E9 FF75 JMP L3
72 00B1 8B 0E 0036r L5: MOV CX,[M]
74 00B8 FF 0E 0032r L6: DEC [I]
75 00BC 83 3E 0032r 00 CMP [I],0
76 00C1 74 3C JE ENDQUEENS
77 00C3 BB 0000r MOV BX,OFFSET MM
78 00C6 03 1E 0032r ADD BX,[I]
79 00CA 8A 07 MOV AL,[BX]
80 00CC 32 E4 XOR AH,AH
81 00CE A3 0034r MOV [N],AX
82 00D1 BB 0009r MOV BX,OFFSET JJ
83 00D4 03 1E 0034r ADD BX,[N]
84 00D8 C6 07 00 MOV BYTE PTR [BX],0
85 00DB BB 0012r MOV BX,OFFSET KK
86 00DE 03 1E 0032r ADD BX,[I]
87 00E2 2B 1E 0034r SUB BX,[N]
88 00E6 03 1E 0036r ADD BX,[M]
89 00EA C6 07 00 MOV BYTE PTR [BX],0
90 00ED BB 0022r MOV BX,OFFSET LL
91 00F0 03 1E 0032r ADD BX,[I] 92 00F4 03 1E 0034r ADD BX,[N] 93 00F8 4B DEC BX 94 00F9 C6 07 00 MOV BYTE PTR [BX],0 95 00FC E9 FF2D JMP L4 96 00FF ENDQUEENS: 97 00FF B4 4C MOV AH,4CH 98 0101 CD 21 INT 21H 99 0103 PRINTRESULT PROC 100 0103 BB 0000r MOV BX,OFFSET MM 101 0106 PRINTLOOP: 102 0106 43 INC BX 103 0107 B4 02 MOV AH,2 104 0109 8A 17 MOV DL,[BX] 105 010B 80 C2 30 ADD DL,’0’ 106 010E CD 21 INT 21H 107 0110 B2 20 MOV DL,’ ’ 108 0112 CD 21 INT 21H 109 0114 E2 F0 LOOP PRINTLOOP 110 0116 B4 02 MOV AH,2 111 0118 B2 0D MOV DL,0DH 112 011A CD 21 INT 21H 113 011C B2 0A MOV DL,0AH 114 011E CD 21 INT 21H 115 0120 C3 RET 116 0121 PRINTRESULT ENDP
117 END QUEENSSTART プログラミングの歴史を「闘うプログラマー」[上] G ・パスカル・ザカりー著山岡洋一訳 日経 BP 出版センターから引用してみよう。 「初期のコンピュータ・プログラムは、特定のコンピュータを補助するものにすぎなかった。 第二次世界大戦以前には、機械式の計算機がほとんどで、スイッチを切り替えたり、配線を変えた り、歯車をうごかすのがプログラミングの仕事にあたっていた。1930 年代には、当時の最強の機 械式計算機、微分解析機で新しい問題を解く準備をするのに、何日もかかった。それから十年たっ て、デジタル・コンピュータができたが、むずかしい問題を解くには、セットアップに何日もか かった。 パンチ・カードや紙テープに記録された要求を読みとれるコンピュータができたが、カー ドやテープは、人間の手で機械に送り込まなければならなかった。プログラミングはまだ初期の段 階にあり、進歩が必要だった。 画期的な進歩がおとずれたのは、1944 年であった。ハンガリー生 まれのアメリカの数学者、ジョン・フォン・ノイマンが、プログラム記憶方式という考えを発展さ せた。プログラムを記憶させるという考え方自体は、この分野ではだれでも考えていたことだが、 ノイマンはとくにその重要性をはっきりととらえていた。プログラム記憶方式では、コンピュータ への命令は、コンピュータ自体のメモリーに保存され、データとおなじようにあつかわれる。こ うすれば、ごく短時間でプログラムを立ち上げられ、プログラムを修正したり、いくつかのプログ ラムを切り替えるのも簡単になる。 プログラム記憶方式は、初期のコンピュータ文化の中に広が り、プログラミングが爆発的に増え、この方式を支持する人が急増した。しかし、道のりはけわ しかった。デジタル・コンピュータには、オンかオフの二つの状態しかないため、 1 (オン)と 0 (オフ)でできた二進法のメッセージにしか応答できない。 プログラムはすべて、この二つの 数字だけで表さなければならないので、 ごく普通の数式が、すぐに、目がくらむほど複雑になる。 1940 年代後半には、 コンピュータのプログラミングは「気が狂うほどむずかしい」と言われた。 まもなく、二進法の文字列を、もっと簡単につくる方法が考え出された。最初に発明されたのは、 二進コードを自動的に打ち出す特殊なタイプライターだ。つぎに、もっと応用範囲の広い「アセ ンブリー」言語ができた。これは、文字や記号で 0 と 1 を表すものだ。アセンブリーでプログラ ムを書くようになったのは進歩だが、それでも、融通のきかないコンピュータの命令セットに忠実 に従って書く必要がある。アセンブリー・コードを書きこなすには、命令セットを完璧に知ってい なければならない。それに、命令セットは、プロセッサーの設計に依存するため、コンピュータの 機種によってちがっている。つまり、苦労して覚えたアセンブリー言語の知識も、そのコンピュー タが使われなくなれば、無駄になる可能性がある。 1950 年代初期には、コンピュータの利用が多 い組織、とくにアメリカの陸海空軍は、ソフトウエアが頭痛のタネであり、コストがかかることに 気づくようになった。一流のプログラマーは、プログラムを書きやすくする方法を模索していた。 1951 年、アメリカ海軍予備役補給局の数学者、グレース・マレー・ホッパーが、コンパイラを開 発した。コンパイラは、プログラマーの命令を、コンピュータを制御する 1 と 0 の文字列、つま りマシン語に翻訳するプログラムである。コンパイラで、ハードウエアの束縛や頭の痛い二進コー ドから、プログラマは解放されるようになった。 ホッパーが突破口を開いた後、プログラミング を簡単にするために、さまざまな努力が行われるようになった。その中でおそらくもっとも重要な ものは、 IBM が開発したコンパイラ、FORTRAN だろう。FORTRAN には、PUNCH, READ DRUM, IF DIVIDE CHECK など三十二種類の命令があり、それぞれに、コンピュータに必要な 正確なマシン語が対応している。1950 年代後半、FORTRAN の影響力は絶大であった。あるコン ピュータ史研究家はこう述べている。「論理的に考えられる人なら、だれでも、やる気があればコ
ンピュータのプログラミングを学べるようになった。コンピュータの内部構造やむずかしいアセン ブリー言語を熟知したスペシャリストである必要はなくなった。FORTRAN の簡単なコマンドだ けを使えば、コンピュータを命令どおりにうごかせる。コンパイラーが自動的に、命令を正しい マシン・コードに翻訳してくれる」 FORTRAN によって、おなじ命令セットを使って、何種類も のコンピュータ用にプログラミングできるようになったが、種類のちがうマシンで使うには、プロ グラムを変更しなければならないことが多かった。それに、FORTRAN は科学技術の問題を解決 する事を目的としている。他の用途のために、COBOL などの言語が開発された。やがて、コン ピュータ言語のジャングルが、プログラマにとって、新たな頭痛のタネになった。どの言語を得意 とするかによって、キャリアが決まるようになった。」 私がコンピュータを勉強しだした40年ぐらい前は、コンピュータを勉強することは FORTRAN (FORmulaTRANslater)を勉強することでした。最初に実用化されたプログラミング言語は FOR-TRAN ですが、これは数値計算のための言語で、私が最初に勉強し、20 年間使っていた言語です が、今では古い言語です。宇宙開発のソフトなど開発している「真のプログラマ」は FORTRAN とアセンブリ言語であらゆるプログラムを作るといいます。 FORTRAN は科学技術計算に使われ、事務計算には COBOL という言語が使われていました。 LISP (LISt Processor) は 1960 年ごろ J.McCarthy によって開発されたリストを基本データ構造と する関数型の言語です。LISP は人工知能のためのアセンブラといわれリスト処理に優れています。 論理型のプログラミング言語 PROLOG (PROgramming in LOGic) は 1972 年に A. Colmerauer によってフランスで開発されます。他の言語のように手続きを記述するのではなく、事実(物事の 論理的関係)を記号論理に基づいて記述し、パターン・マチィングとバックトラッキングでプログ ラムを実行します。LISP と同じくリスト処理に優れ人工知能のための言語で、日本の第五世代コ ンピュータの開発用言語としても使われました。しばらく忘れ去られていましたが、Bruce A. Tate 著「7 つの言語 7 つの世界」 Ruby, Io, Prolog, Scala, Erlang, Clojure, and Haskell 平成 23 年 3200 円の影響で再び注目されています。 1975 年 1 月、米「ポピュラー・エレクトロニクス」誌で紹介された8ビット・マイコンキットで ある「アルテア」が経営難に陥っていた MITS 社から通信販売されます。397 ドルのキットに購入 申し込みが殺到し、1年分と見込んだ個数を初日で突破します。非常に高価で、専門教育を受けた 一部のエリートしか触ることのできなかったコンピュータを個人が我が手にすることができるとい うことで、医者や弁護士といった人たちが競って購入したそうです。若き日のビル・ゲイツと友人 のポール・アレンがこのアルテア用の BASIC インタープリタを開発します。これが圧倒的人気を 得ます。これより一般の人がマイコン(当時はパソコンのことをマイコンと言っていました)で使 うプログラミング言語は BASIC になりました。高等学校の数学 A,B,C の教科書にも BASIC のプログラムが大量に載っていましたが、BASIC の処理系もなくなり、センター試験のコンピュー タの問題も実際の BASIC の処理系でなく、 JIS 規格を基準として問題を作るようになり、情報と いう教科の導入も相まって、指導要領が改訂され、数学の教科書から BASIC は無くなりました。 BASIC の次にマイコンのプログラミング言語として出てきたのは、GOTO 文有害論が出て、そ れの対策として開発された構造的プログラミング言語 PASCAL のマイコン版 PASCAL(TURBO PASCAL) です。大学では、PASCAL でプログラミング教育をしていた時代もありますが、純粋 な PASCAL の処理系も現在では無く(FREE PASCAL と言うのがありますが、オブジェクト指 向言語の DELPHI の代用品です)、ワークステーションの言語として生まれ、すべてのコンピュー タに普及した C を最初の言語として教えているところが日本の大学では多いです。アメリカの大 学では、例えば、 MIT では LISP の方言である Scheme で教育しています。構造的プログラミン グ言語の次に、オブジェクト指向言語と言われる SMALLTALK や C++ や Java が出てきます。
計算機でできることはすべて C または C++ でプログラミングできますが、コンピュータの性能 が良くなったので、インターネットがらみで、Perl や Ruby や Python や Erlang や Clojure や Scala や Haskell といた言語ができます。 この授業では、中学校・高等学校の数学の先生になるためのプログラミング教育なので、本格的 なプログラミング教育ではなく、代表的なプログラミング言語達の紹介と数学 A, B, C の教科書 に載っていた BASIC のプログラムのアルゴリズムが理解でき、どれかのプログラミング言語でプ ログラミングできることを目指します。面白いプログラミング言語が見つかれば自分で勉強して下 さい。 現在の主流のプログラミング言語には手続き型プログラミング言語、関数型プログラミング言 語、論理型プログラミング言語、オブジェクト指向プログラミング言語があります。オブジェクト 指向プログラミング言語は難しいので、手続き型プログラミング言語の代表として C 言語、関数 型プログラミング言語の代表として Scheme、論理型プログラミング言語の代表として Prolog の 基本を学びます。 昔、ネットワークについて話を聞いたり、Java によるネットワークプログラミングなどを勉強 したときは、いくらやっても全然頭に勉強したことが残りませんでした。そのときには、私の環 境では物理的に複数のコンピュータをネットワークに繋げるわけでもなく、大学のコンピュータで ホームページを作っても極端に制限された使い方しかできなかったので、ネットワークの勉強は私 には本当には必要なかったからだと思います。最近、私のパソコンがマルチスレッドに対応しだし (CPU が i3, i5 や i7 になった)、家のネットワーク環境にルータが導入され、複数のパソコンを有 線や無線でインターネットに繋げるように知らない間になっていて(pikara がつながるようにし てくれていた)、やっとマルチスレッドやネットワークのプログラミングを実際に実験しながら勉 強する物理的な勉強の環境が揃ってきました。金高堂でたまたま手に取った「C 言語逆引きハンド ブック」という本にマルチスレッドやネットワークのプログラミングの基本が書いてありました。 何の勉強でもそうですが、特にコンピュータの勉強は、唯本を読むだけの抽象的な勉強ではなく、 「実際にプログラムを打ち込み、動かし、失敗し、何処が間違いか調べ、修正し、実際にプログラ ムを打ち込み、動かし、失敗し、何処が間違いか調べ、修正し、・・・」の試行錯誤を繰り返しなが ら、集中的に時間を掛けて勉強する必要があります。そうすれば、次に何を勉強すればよいか自然 に分かります。現在は色々なことを勉強するのに本当に良い時代になりました。ただ、猛烈な勢い で世の中変わっています。勉強をし続けないと取り残されてしまいます。
3
Windows
フォーム・アプリケーション
通常の C++ の解説と異なり、結果が正しいかどうか見れば分かる、関数のグラフを描くことか ら始めます。
まず、Microsoft Visual Studio 2010 を立ち上げる。
スタートページがなければ、「表示」のメニューでスタートページをクリックすればよい。 上図は Professional 版であることを示していますが、それは私が幾何学の研究の為のプログ ラムや数独など各種パズルを解いたり問題を作ったりするプログラムやオセロや囲碁などの各種 ゲームのプログラムを作るためにメモリーを沢山使う64ビットのプログラミングがしたいから、 Professional 版を使っているからです。学生さんはアカデミック版を購入できますから、1 万円位で 生協で Professional 版を購入出来ますが、以下のプログラムは Professional 版固有の機能は使って いないので、学生さんが勉強のために、以下のプログラムを実行するだけなら、無料の Microsoft Visual Studio 2010 Express 版で十分です。
新しいプロジェクト・・・をクリックする。
Windows フォームアプリケーションをクリックし、図では場所 (L):が D:\jyugyou\となっていま す(自分で適当なディレクトリ、例えば D:\jyugyou\を作り、ここを指定すれば、プログラムが D:\jyugyou\に作られます)が、何処にプログラムが保存されるか気にしなければ、そのままで良 いですので、名前 (N): のエディットボックスに例えば en2 と入力し、
OK ボタンをクリックする。
マウスで Form1 を適当な大きさにする。
ツールボックスを選択して、ツールボックスを表示する。
ツールボックスにある PictureBox をクリックし、マウスを Form1 に移動し、ドラッグして、Form1 の中央に Picture Box を配置する。
または、ツールボックスの欄の右端のアイコンを押して
で、プロパティを探し、プロパティを表示する。
一番下にある paint をダブルクリックする。
必要なら Form1.h をドラッグして、左側に移す。
private: System::Void pictureBox1_Paint(System::Object^ sender, System::Windows::Forms::PaintEventArgs^ e) { } の { と } の間に、次のようになるようキーボードから打ち込む。 これは picturebox1 のイベント paint が発生したとき、どのように反応すべきかを指示するプロ グラムを作ることになる。
private: System::Void pictureBox1_Paint(System::Object^ sender, System::Windows::Forms::PaintEventArgs^ e) {
Graphics^ g = e->Graphics;
Pen^ pen = gcnew Pen(Color::Blue); double pi = Math::PI;
g->DrawEllipse(pen, 50, 50, 260, 260); for (double t=0; t<2*pi; t += pi/80) {
g->DrawLine(pen, (int)(180+130*cos(t)), (int)(180-130*sin(t)), (int)(180+130*cos(2*t)), (int)(180-130*sin(2*t))); }
Form1.h ファイルの先頭
#include <math.h> を追加する。
となる。図が欠けていれば、Form1.h[デザイン] のタブをクリックし、Form や PictureBox を 適当な大きさに修正し、再度 Debug の左の赤い三角形のアイコンをクリックする.。
別のプログラムを作ってみます。
Microsoft Visual Studio 2010 を立ち上げ、上と同じように適当な名前でプロジェクトを作り、 Form に PictureBox と Button 2個を配置する。
Form1.h ファイルの先頭に #include <math.h>
を追加する。
button1 をダブルクリックし、
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {
Graphics^ g = pictureBox1->CreateGraphics(); Brush^ brush = gcnew SolidBrush(Color::White); g->FillRectangle(brush, 0, 0, 360, 360); Pen^ pen = gcnew Pen(Color::Blue, 1); g->DrawEllipse(pen, 50, 50, 260, 260); double pi = Math::PI;
for (double t=0; t<2*pi; t += pi/80) {
g->DrawLine(pen, (int)(180+130*cos(t)), (int)(180-130*sin(t)), (int)(180+130*cos(2*t)), (int)(180-130*sin(2*t))); }
}
と打ち込む。さらに、button2 をダブルクリックし、
private: System::Void button2_Click(System::Object^ sender, System::EventArgs^ e) {
Graphics^ g = pictureBox1->CreateGraphics(); Brush^ brush = gcnew SolidBrush(Color::Yellow);
g->FillRectangle(brush, 0, 0, 360, 360); Pen^ pen = gcnew Pen(Color::Red, 1); g->DrawEllipse(pen, 50, 50, 260, 260); double pi = Math::PI;
for (double t=0; t<2*pi; t += pi/80) {
g->DrawLine(pen, (int)(180+130*cos(t)), (int)(180-130*sin(t)), (int)(180+130*cos(3*t)), (int)(180-130*sin(3*t))); } } と打ち込む。実行すると となります。button1 をクリックすると となり、button2 をクリックすると
となります。 例題:y = x sin x (−5π ≤ x ≤ 5π) のグラフを描け。 Form の中央に PictureBox を配置する。 Form1.h ファイルの先頭に #include <math.h> を追加する。 picturebox1 のイベント paint を次のように定義する。
private: System::Void pictureBox1_Paint(System::Object^ sender, System::Windows::Forms::PaintEventArgs^ e) {
Graphics^ g = e->Graphics;
Pen^ pen = gcnew Pen(Color::Blue); double pi = Math::PI;
g->DrawLine(pen, 0, 180, 360, 180); g->DrawLine(pen, 180, 0, 180, 360); int ox = (int)(180-180.0*5*pi/15.7079); int oy = (int)(180-10*(-5*pi)*sin(-5*pi));
for (double t=-5*pi; t<=5*pi; t += pi/80) { g->DrawLine(pen, ox, oy,
(int)(180+180.0*t/15.7079), (int)(180-10*t*sin(t))); ox = (int)(180+180.0*t/15.7079); oy = (int)(180-10*t*sin(t)); } } コンパイルし、実行すると となる。 例題:(1/2, 0) を中心とする直径1の円上の点を動点として、直径 OP の円群を描け。輪郭線に カージオイドが浮かび上がる。 Form の中央に PictureBox を配置する。 Form1.h ファイルの先頭に #include <math.h> を追加する。 picturebox1 のイベント paint を次のように定義する。
private: System::Void pictureBox1_Paint(System::Object^ sender, System::Windows::Forms::PaintEventArgs^ e) {
Graphics^ g = e->Graphics;
Pen^ pen1 = gcnew Pen(Color::Black); g->DrawLine(pen1, 0, 180, 360, 180); g->DrawLine(pen1, 180, 0, 180, 360); double pi = Math::PI;
int K = 150;
for (double t = 0; t<=2*pi; t += pi/20) { double x = 0.25+cos(t)/4.0; double y = sin(t)/4.0; int x1 = (int)(K*(x-sqrt(0.5+cos(t)/2.0)/2.0)+180); int y1 = (int)(K*(-y-sqrt(0.5+cos(t)/2.0)/2.0)+180); int x2 = (int)(K*sqrt(0.5+cos(t)/2.0)); int y2 = (int)(K*sqrt(0.5+cos(t)/2.0)); g->DrawEllipse(pen1, x1, y1, x2, y2); } } コンパイルし、実行すると となる。 例題:原点を中心とし半径が a の円 O の外側を半径が b の円 C が円 O に外接しながら滑るこ となく転がるとき、円 C 上の点 P の軌跡を考える。ただし、点 P のはじめの位置は、円 O と x 軸の正の部分との交点 A とする。 円 C が転がるとき、動径 OC が表す角を θ とし、そのときの外接する点を Q、点 P の座標を (x,y) とすると、P の軌跡は θ を媒介変数として x = (a + b) cos θ− b cosa + b b θ, y = (a + b) sin θ− b sin a + b b θ で表される。この曲線を エピサイクロイド という。エピサイクロイドを描くプログラムを作れ。 Form に PictureBox と Button と Label 2個と textBox 2個を配置する。
Form1.h ファイルの先頭に #include <math.h>
を追加する。
button1 をダブルクリックし、
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) { Graphics^ g = pictureBox1->CreateGraphics(); double a = System::Convert::ToDouble(textBox1->Text); double b; Double::TryParse(textBox2->Text, b); double K = 170/(a+2*b);
Brush^ brush = gcnew SolidBrush(Color::White); g->FillRectangle(brush, 0, 0, 360, 360); Pen^ pen2 = gcnew Pen(Color::Red, 2);
g->DrawEllipse(pen2, 180-(int)(K*a), 180-(int)(K*a), (int)(2*K*a), (int)(2*K*a)); Pen^ pen = gcnew Pen(Color::Blue, 2);
double pi = Math::PI;
int ox = (int)(180+K*((a+b)*cos(0.0)-b*cos(0.0))); int oy = (int)(180-K*((a+b)*sin(0.0)-b*sin(0.0))); for (double t=0; t<=2*pi*b; t += pi/80) {
int nx = (int)(180+K*((a+b)*cos(t)-b*cos((a+b)*t/b))); int ny = (int)(180-K*((a+b)*sin(t)-b*sin((a+b)*t/b))); g->DrawLine(pen, ox, oy, nx, ny);
ox = nx; oy = ny; }
コンパイルし、実行すると となる。button1 をクリックすると となる。 レポート問題:原点を中心とし半径が a の円 O の内側を半径が b a > b > 0) の円 C が円 O に内接しながら滑ることなく転がるとき、円 C 上の点 P の軌跡を考える。ただし、点 P のはじ めの位置は、円 O と x 軸の正の部分との交点 A とする。 円 C が転がるとき、動径 OC が表す角を θ とし、そのときの外接する点を Q、点 P の座標 を (x,y) とすると、P の軌跡は θ を媒介変数として x = (a− b) cos θ + b cosa− b b θ, y = (a− b) sin θ − b sin a− b b θ で表される。この曲線を ハイポサイクロイド という。ハイポサイクロイドを描くプログラムを 作れ。
レポート問題:極方程式 r = a + b cos θ で表される曲線を リマソン という。特に、a = b のとき、極方程式 r = a(1 + cos θ) で表される曲線を カージオイド という。リマソンを表示するように、上のプログラムを変更せよ。 レポート問題: x = (a + b) cos θ− c cosa + b b θ, y = (a + b) sin θ− c sin a + b b θ で表される曲線を描くプログラムを作れ。 レポート問題: x = (a− b) cos θ + c cosa− b b θ, y = (a− b) sin θ − c sin a− b b θ で表される曲線を描くプログラムを作れ。
4
コンソール・プログラミング
自分のためにちょこちょこと計算するには、コンソールアプリケーションを使います。 プログラムの作成と実行 手順は以下の通り 1. 新しいプロジェクトを作成する。 2. C++ ソースファイルをプロジェクトに追加する。 3. ソースコードを入力する。 4. 実行可能ファイルをビルドする。 5. プログラムを実行する。 6. プログラムを保存する。 新しいプロジェクトの作成1. Visual Studio アイコンをクリックして Visual C++ IDE を開く。
2. 「ファイル」メニューを開き、「新規作成」→ 「プロジェクト」をクリックする。 3. 「プロジェクトの種類」で「Visual C++」を選択する。 4. 「テンプレート」または「インストールされたテンプレート」セクションで「Win32 コン ソールアプリケーション」を選択する。 5. 「プロジェクト名」または「名前」テキストボックスにプロジェクトの名前(例えば hello) を入力する。 6. プロジェクトのディレクトリを選択する。通常はデフォルトでよい。 7. 「OK」をクリックする。 8. 「Win32 アプリケーションウィザード」が起動する。 9. ページの左側で「アプリケーションの設定」をクリックするか、または「次へ」をクリック する。 10. 「追加のオプション」から「空のプロジェクト」を選択する。 11. 「完了」をクリックする。これで、すべてのコンパイラ設定がコンソールプロジェクトのた めに初期化されるはずです。 プロジェクトへの C++ ソースファイルの追加 プログラムには、ソースファイルが少なくとも1つ(たいていは複数)必要です。 1. メニューバーの「新しい項目の追加」アイコン(通常は左から二つ目のアイコン)をクリッ クする。「新しい項目の追加」ダイアログボックスが表示される。 2. 「Visual C++」カテゴリから「コード」を選択する。
3. テンプレートセクションで「C++ ファイル (.cpp)」アイコンを選択する。 4. 「ファイル名」テキストボックスにプログラムファイルの名前(hello)を入力し、「追加」を クリックする。 これで、空のソースコードファイルが作成され、ソースコードプログラムを入力する準備が整った。 ソースコードの入力 この時点で、ソースコードを IDE に直接入力するか、別のソースからコピーして貼り付けるか を選択できる。 実行可能プログラムのビルド プログラムのソースコードを正しく入力できたという自信がある場合は、「ビルド」メニューか ら「ソリューションのビルド」を選択するか、IDE ウィンドウの上部にあるアイコンリストで右 向き三角形アイコンをクリックする。そうすると、IDE がプログラムのコンパイルとリンクを試 みる。それらが正常終了した場合は、「出力」ウィンドウに次のメッセージが表示される。 ビルド:1 正常終了、0 失敗、0 更新不要、0 スキップ 正常終了しなかった場合は、エラーメッセージが表示される。プログラムをデバッグしてエラー を修正し、再び「ソリューションのビルド」を実行する。 右向き三角形アイコンを使用した場合、エラーがなければプログラムが実行を自動的に開始す る。「ソリューションのビルド」メニュー項目を使用した場合は、プログラムを自分で開始する必 要がある。 プログラムの実行 すべてのエラーを取り除いたら、「デバッグ」メニューで「デバッグなしで開始」を選択し、プ ログラムを実行する。 プログラムの保存 「ファイル」メニューの「すべてを保存」をクリックする。保存するのを忘れて IDE を閉じよ うとすると、IDE がそれを知らせてくれる。 ごちゃごちゃ書いていますが無視してともかくやってみます。 まず、Microsoft Visual Studio 2010 を立ち上げる。
このようにならなければ、「表示」のメニューで「スタートページ」をクリックします。
Win32 コンソール アプリケーションをクリックし、図では場所 (L):が D:\jyugyou\となって います(自分で適当なディレクトリ、例えば D:\jyugyou\を作り、ここを指定すれば、プログラム が D:\jyugyou\に作られます)が、何処にプログラムが保存されるか気にしなければ、そのまま で良いですので、名前 (N): のエディットボックスに例えば hello と入力し、 OK ボタンをクリックする。 次へのボタンをクリックし、空のプロジェクトをチェックする。
完了ボタンをクリックする。
クラスビューになってなければ、クラスビューをクリックする。
C++ ファイルをクリックし、名前の欄に hello と打ち込む。
追加のボタンをクリックする。
#include <iostream> #include <stdio.h>
using namespace std;
int main(int argc, char *argv[]) {
cout << "Hello, world" << endl; getchar(); return 0; } コンパイルし、実行する(Debug の横の赤い三角をクリックする)と と Hello,world と表示する。 以下では、このプログラムを雛形として cout << "Hello, world" << endl; の部分を作りかえて、プログラミングしていきます。
4.1
式と計算
いろいろな計算を行うには、変数と呼ばれるデータの記憶場所に数値を保存させる。変数は名前 (変数名)を付けて区別する。変数名はアルファベット、数字、_(アンダーバー)からなる文字 列で、先頭の文字には数字は使えない。大文字と小文字は区別するので abc ABC Abc AbC はす べて異なる変数を表す。次の識別子(名前)はキーワード(予約語)として使われるので、別の用 途に使えない。予約語は文字がゴチックになるので、すぐわかります。
auto double int struct break else long switch
case enum register typedef char extern return union
const float short unsigned continue for signed void
default goto sizeof volatile do if static while
asm _cs _ds _es _ss cdecl far huge
interrupt near pascal
C++ では更に次の識別子もキーワードです。
class inline overload public friend new virtual operator this delete
次は三つの数を入力し、その和と平均を出力する C のプログラムです。
Microsoft Visual Studio 2010 で、 C のプログラムを作るには次のようにします。基本的には C++ のプログラミングと同じですが、Microsoft Visual Studio 2010 を起ち上げます。
のような画面になることもあります。上でやったように、「表示」のメニューで「スタートページ」
をクリックし、新しいプロジェクト・・・をクリックしても良いですが、次のようにすることも出来
Win32 コンソール アプリケーションをクリックし、名前 (N): のエディットボックスに例えば heikin3 と入力し、
OK ボタンをクリックする。
完了ボタンをクリックする。
クラスビューになってなければ、クラスビューをクリックし、一段目の左から二つ目のアイコ ン(新しい項目の追加)をクリックし、C++ ファイルをクリックし、名前の欄に heikin.c と打ち 込む。
heikin.c のファイルが作られ、開いている。 #include <stdio.h>
int main() {
double a, b, c, wa, heikin;
printf("a b c = ");
scanf("%lf %lf %lf", &a, &b, &c); wa = a + b + c; heikin = wa / 3; printf("和 = %lf\n", wa); printf("平均 = %lf\n", heikin); getchar(); return 0; } と入力する。 C のプログラムも C++ のプログラムも main() 関数から実行する。 int main(int argc, char *argv[])
{ } になっていたり、 int main() { } となっているが、あまり気にしなくて良い。上の場合は、実行プログラムに引数を与えて、引数に 応じて、プログラムの実行を変化したいときに使うが、上のようにして、引数を与えなくても良い。 #include <stdio.h>
は、
printf("a b c = ");
scanf("%lf %lf %lf", &a, &b, &c); printf("和 = %lf\n", wa);
printf("平均 = %lf\n", heikin); getchar();
で使われているライブラリ関数 printf(), scanf(), getchar() を使うために必要なインクルードファイ ルです。ライブラリ関数とは、メーカーが作って、ライブラリに納められている関数です。printf(), scanf(), getchar() のプロトタイプ(どのような引数を取り、どのような値を戻すか)が stdio.h に 書かれています。これを include 命令で取り込んでいます。関数は使う前にプロトタイプ(どのよ うな引数を取り、どのような値を戻すか)を書いておかなくてはなりません。
double a, b, c, wa, heikin;
は、使用する変数の宣言です。プログラムで使用する変数は記憶するための場所を確保するために、 前もって宣言する必要がある。C では変数の宣言は関数定義の先頭で行う。double は基本データ 型の一つ倍精度浮動小数点数型を示す。
double a, b, c, wa, heikin;
は変数 a, b, c, wa, heikin は倍精度浮動小数点数のための変数であると宣言している。double は 4Byte で +- 2.225073858507201e-308 から+-1.797693134862316e+308 の範囲の実数を記憶する ことができます。よく使う他の基本データ型には char と int と float がある。
printf("a b c = ");
の printf(”a b c = ”) は、コンソール(画面)に a b c = と表示します。printf() 関数は ” と ” で 囲われた文字列を一部の例外を除いてそのまま表示します。
scanf("%lf %lf %lf", &a, &b, &c);
の scanf() は標準ライブラリ関数で書式付きでキーボードからデータを入力します。scanf() 関数 の最初の引数が書式用の文字列です。%lf がキーボードから入力された数値(文字列)を倍精度浮 動小数点数に変換することを示します。したがって、この場合はスペースで区切られた数値を倍精 度浮動小数点数として三個入力することを指示しています。scanf() 関数はキーボードから文字を 読み込み、書式で指定された形式に従ってそれを解釈し、残りの引数へ結果を格納する。二番目以 降の引数は、それに対応する変換入力を格納できるようにポインタでなければならない。ポインタ にするためには変数名の前に & を付ければよい。今の場合、変数 a, b, c にこの順で倍精度浮動小 数点数が入力される。ポインタは説明するのがやっかいなので、気になって仕方がなければ、自分 でインターネットで調べるか、C または C++ の入門書を読んで下さい。ここでは、scanf() では 変数の頭に & を付ければよいと覚えておけばいいです。C++ では、以下で見るようにもっと簡単 です。 wa = a + b + c;
は代入文です。代入文は 変数=式; の形式で式の値を計算し、その値を変数に代入します。もと 在った変数の値はなくなり新しい値で置き換わります。今の場合、変数 a, b, c の値の和を取りそ れを変数 wa の値とします。 heikin = wa / 3; は変数 wa の値を3で割り変数 heikin に代入します。+ と / は算術演算子です。よく使う算術演 算子には +, -, *, /, % があります。+ は加法を、 - は単項のマイナス演算子と減法を、 * は 乗法を、/ は除法(a / b は a を b で割った値になります。ただし、整数の除法は小数点以下切り 捨てて整数値にします)を、また % は整数の余り (a % b は a を b で割った余りですが、a が負の 数のときには、負の余りが計算されます) を求めるのに使われる。+ - を加法演算子* / % を乗法 演算子といい、乗法演算子の方が加法演算子より先に計算される。乗法演算子、加法演算子同士の 場合は左から右に順に計算される。 最後に printf("和 = %lf\n", wa); printf("平均 = %lf\n", heikin);
の printf() 関数で wa と heikin の値が出力される。printf() も最初の引数が書式で %lf の所に wa および heikin の値が代入されて出力される。 wa および heikin が倍精度浮動小数点数型なので %lf が使われます。 %\n は改行を指示します。
getchar();
は、これがなければ、Microsoft Visual Studio 2010 の統合開発環境では、直ちにコンソール画面 が閉じますから、これを実行して、何か文字が入力されるまで待ちます。例えば、エンターキーを 押すとプログラムが終了します。 return 0; は int main() と main() 関数が int (整数値)を戻す関数と定義されているので、正常に終了した事を示す値 0 を返しています。 C++ では、次のようにプログラミングする事もできます。 #include <iostream.h> using namespace std; int main() {
double a, b, c, wa, heikin;
cout << "a b c = "; cin >> a >> b >>c; wa = a + b + c; heikin = wa / 3;
cout << "和 = " << wa << "\n"; cout << "平均 = " << heikin << endl; getchar(); getchar(); return 0; } このプラグムの #include <iostream.h> は cout << "a b c = "; cin >> a >> b >>c; cout << "和 = " << wa << "\n"; cout << "平均 = " << heikin << endl; getchar(); getchar();
の cout, cin, getchar() のために必要なインクルードファイルです。 using namespace std;
は、 cout, cin などが std という namespace で宣言、定義されているので、std という namespace を使いますという宣言です。即ち、cin と cout は名前が重複しないように、 namespace の std と いうところで宣言されているので、
using namespace std;
とするか、std::cin と std::cout の形で使う必要があります。std::cin と std::cout とするのはめん どくさいので、 using namespace std; としています。 C++ ではいつでも #include <iostream.h> using namespace std; または #include <iostream> using namespace std; を最初に書くものだと思って下さい。 cout << "a b c = "; で、コンソールに ”a b c = ” と表示します。 cin >> a >> b >>c;