これにより、ある程度複雑なプログラムがどのように動くかということを視覚的に理解することにしたい。
program graphics_sample(input, output);
#include <xgraph.h>
var gdriver, gmode : integer;
xcen, ycen : integer;
begin
gdriver := detect; グラフィックス使用の準備
initgraph(gdriver, gmode, ''); グラフィックス使用の準備
cleardevice; 画面のクリア
clrscr; 文字画面のクリア
xcen := getmaxx div 2; 画面中心の x 座標の計算
ycen := getmaxy div 2; 画面中心の y 座標の計算
circle(xcen,ycen, 100); 円の表示
circle(xcen,ycen, 150); 円の表示
line(xcen-150,ycen-150,xcen+150,ycen-150); 線分の表示
line(xcen+150,ycen-150,xcen+150,ycen+150); 線分の表示
line(xcen+150,ycen+150,xcen-150,ycen+150); 線分の表示
line(xcen-150,ycen+150,xcen-150,ycen-150); 線分の表示
readln;
closegraph; グラフィックス使用の終了
end.
この例では、最初の #include 何とかというのは、グラフィックス関数を使うということを宣言している。この #include というのは、PASCAL 言語の機能ではなく、C という別の言語のコンパイラの機能であるが、とにかく指定したファイルの中身をそこに読み込んでくる。実際のファイルは /usr/include/xgraph.h のはずである。実行部の先頭の2行は、実際に絵を書くための 準備をする手続きを呼んでいる。これを呼ぶと新しいウインドウができる。
このウインドウは、 800 x 800 の点からできている。座標系は、 慣習により画面の左上が (0,0), 右下が (799,799) となっている。
このグラフィックスの手続きは、 IBM のパソコンでつかわれていた Turbo PASCAL (商品名)というコンパイラと(大体)同じように使えるようになっている。言語が違ったりすると、グラフィックスの手続きの細かいところはいろいろちがってくるが、まあ、線を書く、円を書く、文字を書く、色を変えるといったものがあるというのはどれも同じようなものである。
グラフィックス手続きについては、簡 単なまとめをつくっておいた。
なお、日本語で説明を書いたところもそのままコピーしてはコンパイル出来ないので注意。そこは消すこと。
上のプログラムでは、circle と line が実際に円を書く、あるいは直線を引く手続きである。従って、これらを追加したり、指定する数(引数)を変えたりすれば画面に出る絵が変わる。なお、手続きや関数で引数を2個以上必要とする場合(例えば circle では x 座標、 y 座標、 半径の3個)3個の数(あるいは変数、式)をコンマで区切って並べる。並べたものの何番目がどれか(たとえばx、y 座標 と半径のうち)は、あらかじめ決まっている。このような方式を、位置パラメータといい、現在のほとんどの計算機言語で使われている。
このやり方の代わりに、例えば circle(x_pos=xcen, y_pos=ycen,radius=100) というように指定することも考えられる。この方がわかりやすいが、プログラムが繁雑になるのを嫌うためかこの形式が使える計算機言語は少ない。(アメリカ国防総省の指定言語 Ada は、この形が使える)
なお、このプログラムを打ち込んで、そのままコンパイルしようとしてもうま くできない。これは、グラフィックスを使う関数がどこにあるか指定されてい ないからである。これは以下のように指定する。
pc graph1.pas -o graph1 -lXtc -lX11ここで、最後の2つの -l 何とかというのが、グラフィックスを使うためのおまじないである。コマンドやファイル名には大文字と小文字の区別がある。例えば -lxtc とか -lXTc とかではうまく動かないので注意して欲しい。
program graphics_loop(input, output);
#include <xgraph.h>
var gdriver, gmode : integer;
xsize, ysize : integer;
n, i : integer;
dx, dy : real;
begin
write('How many lines? ');
readln(n);
gdriver := detect;
initgraph(gdriver, gmode, '');
cleardevice;
clrscr;
xsize := getmaxx ;
ysize := getmaxy ;
dx := xsize/n;
dy := ysize/n;
for i := 0 to n do line(0,round(i*dy), round(i*dx),ysize);
readln;
closegraph;
end.
for 変数 := 最初の値 to 最後の値 do 文;
は、まず変数に最初の値を入れて文を実行し、次に変数を1増やしてまた文を実行し、以下同様に繰り返して変数が最後の値になったらおしまいにするということになる(変数が最後の値になったときも文は実行される)。このような繰り返し処理をループ処理という。上の場合では始点、終点が違う線分を指定した本数だけ引いてくれる。なお、 round は実数型の値を四捨五入した整数に変換する関数である。線の代わりに丸をたくさん書いてみる。
program graphics_loop2(input, output);
#include <xgraph.h>
var gdriver, gmode : integer;
xcen, ycen : integer;
n,i,x,y : integer;
r1, r2, theta : real;
begin
write('How many circles ');
readln(n);
write('Enter r1, r2: ');
readln(r1, r2);
gdriver := detect;
initgraph(gdriver, gmode, '');
cleardevice;
clrscr;
xcen := getmaxx div 2;
ycen := getmaxy div 2;
theta := 3.142592*2/n;
for i := 0 to n do begin
x := round(r1*cos(i*theta))+xcen;
y := round(r1*sin(i*theta))+ycen;
circle(x,y, round(r2));
end;
readln;
closegraph;
end.
このプログラムでは、上のプログラムに比べて1つ新しいことをしている。 for で繰り返す中身が一文ではなく複数の文のつながりであることである。
ループの中でいくつかの文のつながりを実行するためには、
for 変数 := 最初の値 to 最後の値 do begin
文;
......
文;
end;
というふうに、いくつかの文を begin と endで囲んでやる。
program graphics_sample2(input, output);
#include <xgraph.h>
const rad = 30;
var gdriver, gmode : integer;
x,y,vx,vy : real;
i,j : integer;
xmax, xmin,ymin,ymax : real;
begin
write('Enter x, y, vx, vy:');
readln(x, y, vx, vy);
initgraph(gdriver, gmode, '');
cleardevice;
clrscr;
xmin := rad;
xmax := getmaxx-rad;
ymin := rad;
ymax := getmaxy-rad;
for i := 0 to 10000 do begin
setcolor(Black);
circle(round(x), round(y), rad);
if x+vx > xmax then vx := -vx;
if x+vx < xmin then vx := -vx;
if y+vy > ymax then vy := -vy;
if y+vy < ymin then vy := -vy;
x := x + vx;
y := y + vy;
setcolor(White);
circle(round(x), round(y), rad);
for j := 1 to 10000 do ;
end;
readln;
closegraph;
end.
if 条件 do 文1
else 文2;
という構造は、条件が成り立っていれば文1を、そうでなければ文2を実行せよという意味になる。文2がない(条件が成り立っている時はなにかするがそうでなければ何もしない)ときには
if 条件 do 文1;
だけでいい。なお、 for の中に書いた、 begin 文; 文; ... end
というまとまりのことを複文といい、一般に文が書けるところには複文も書ける。このため、 if ... then begin ..... end else begin .... end; というような風にすれば条件によって違ういくつかの処理をまとめてできる。
条件は、数値同士の比較式(大小、等しい)と、複数の比較式からできる論理式などが書ける。
画面のプリントアウト
グラフィックス画面は gcopy というコマンドを実行するとプリンターに送られる。これは、絵が出ているうちに行なう必要があるので、左上の小さいウインドウで入力する。
なお、xv で保存したものをプリントすることも出来る。これには、 xv で保存する時にファイル形式を postscript にする。これで出来たファイルはそのままプリンターに送ることが出来る。
画面をファイルに保存する
自分で作った絵を保存するには、 xv を使えばいい。
反復(その2)
お絵書きばかりでもなんなので、少し計算らしいこともしてみよう。
program loop_example2(input, output);
var interest,amount: real;
year_end, year: integer;
begin
write(errout,'Enter year to calculate the amount:');
readln(year_end);
write(errout,'Enter annual interest:');
readln(interest);
amount := 1;
for year := 1 to year_end do begin
amount := amount * (1 + interest);
writeln('year: ', year:2, ', amount:', amount);
end;
end.
\end{verbatim}
これは複利(等比数列)を計算し、出力する。等比数列なので、前の年の値に 1
+ 利息 を掛けて、その答を書き出すという処理を繰り返している。このために
実行例
Enter year to calculate the amount:5
Enter annual interest:0.0525
year: 1, amount: 1.05250000000000e+00
year: 2, amount: 1.10775625000000e+00
year: 3, amount: 1.16591345312500e+00
year: 4, amount: 1.22712390941406e+00
year: 5, amount: 1.29154791465830e+00
条件付反復
for ループを使う時には、あらかじめ繰り返しの回数がわかっている必要がある。しかし、場合によってはそもそも計算機で計算してはじめて繰り返しの回数がわかることもある。そのような時はどうプログラムすればいいだろうか。
ローンを借りて、毎年一定額ずつ返済していくとして、毎年残金がいくらかを書きだし、残金が0(またはマイナス)になったらやめるという処理を考えて見よう。
program while_example(input, output);
var interest,amount,payment: real;
year: integer;
begin
write(errout,'Enter annual interest and your payment:');
readln(interest, payment);
amount := 1;
year := 0;
while amount > 0.0 do begin
amount := amount * (1 + interest);
amount := amount - payment;
year := year + 1;
writeln('year: ', year:2, ', amount:', amount);
end;
end.
このプログラムで使っている、
while 条件 do begin
文;
......
文;
end;
という構造は、 begin と end にかこまれた処理を条件が成り立っているあいだ繰り返せということになる。条件は、数値同士の比較式(大小、等しい)と、複数の比較式からできる論理式などが書ける。
次週予告
今週は、あらかじめグラフィックス用に準備された手続きを使ってみたが、次週は手続きを自分で作ってみることにする。