講義第8回:Pascal の基礎 --- グラフィックスと繰り返し、判断

今日はグラフィックスの機能を使い、画面にいろいろ絵(主に線画)を書いて みる。さらに、繰り返し処理などで複雑な図形を書くことを考える。

これにより、ある程度複雑なプログラムがどのように動くかということを視覚 的に理解することにしたい。

2回めのレポートについて

提出状況が TAの近藤さんの作ったリスト に出ているので、自分が出したものがある かどうか確認して、出したはずなのにないって人はとりあえず 近藤さんに申 し出て下さい。 なお、

グラフィックス

この講義で使っている PASCAL では、あらかじめ準備されている様々な手続き、 関数を使うことで画面に絵を書くことができる。関数とは、普通の数学関数の ように、数を返してくれるものである。手続きとは、例えば「指定した座標を 中心として指定した半径で円を書く」という一連の仕事をやってくれるもので ある。

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) というように指定することも考えられる。この方がわかりやす いが、プログラムが繁雑になるのを嫌うためかこの形式が使える計算機言語は 少ない。

なお、このプログラムを打ち込んで、そのままコンパイルしようとしてもうま くできない。これは、グラフィックスを使う関数がどこにあるか指定されてい ないからである。これは以下のように指定する。

pc graph1.pas -o graph1 -lXtc -lX11
ここで、最後の2つの -l 何とかというのが、グラフィックスを使うた めのおまじないである。コマンドやファイル名には大文字と小文字の 区別がある。例えば -lxtc とか -lXTc とかではうまく動かない ので注意して欲しい。

反復と判断

さて、計算機のプログラムで絵を書くといっても、線を1本引くのにいちいち line ..... と書くのであれば tgif でも使うか、そもそも紙に 手で書いた方が早い。プログラムを書くといいことは、例えば線を引く位置を 変えながらたくさん引くとかいうのができることである。
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;
    dx, dy   : real;
begin
    write('How many points? ');
    readln(n);
    write('Enter dx, dy: ');
    readln(dx, dy);
    gdriver := detect;             
    initgraph(gdriver, gmode, ''); 
    cleardevice;                   
    clrscr;                        
    xcen := getmaxx div 2;            
    ycen := getmaxy div 2;
    for i := 0 to n do begin
        x := round(200*cos(i*dx))+xcen;
        y := round(200*sin(i*dy))+ycen;
        if i = 0 then
           moveto(x,y)
        else
           lineto(x,y);
    end;
    readln;
    closegraph;
end.
これはリサージュ図形(x方向とy方向が周期の違う単振動をする時にできる図 形)を描く。このプログラムでは、上のプログラムに比べて3つ新しいことを している。一つは、 for で繰り返す中身が一文ではなく複数の文 のつながりであること、もう一つは i の値によって違うことをす るために if ... then ... else ... というものを使っているこ と、最後に line の代わりに moveto lineto を使っていることである。

ループの中でいくつかの文のつながりを実行するためには、

for 変数 := 最初の値 to 最後の値 do begin
文;
......
文;
end;
というふうに、いくつかの文を beginendで囲んでやる。

if 条件 do 文1
else 文2;

という構造は、 条件が成り立っていれば文1を、そうでなければ文2を実行せよという意味にな る。文2がない(条件が成り立っている時はなにかするがそうでなければ何も しない)ときには

if 条件 do 文1;

だけでいい。なお、 for の中に書いた、 begin 文; 文; ... end というまとまりのことを複文といい、一般に文が書けるところには複文も書け る。このため、 if ... then begin ..... end else begin .... end; というような風にすれば条件によって違ういくつかの処理をまとめてできる。

条件は、数値同士の比較式(大小、等 しい)と、複数の比較式からできる論理式などが書ける。

さて、繰り返し処理には for の他にもう一つの方法がある。それは、 「ある条件が成り立っているうちは繰り返す」というものである。

while 条件 do begin
文;
......
文;
end;

という構造は、 begin endにかこまれた処理を条件が成り立っ ているあいだ繰り返せということになる。

program graphics_loop3(input, output);
#include <xgraph.h>
var gdriver, gmode : integer;         
    xcen, ycen     : integer;
    x,y, xmin,xmax : real;
    dx, dy         : real;
    ix, iy, i      : integer;
begin
    write('xmax, dx ? ');
    readln(xmax, dx);
    xmin := -xmax;
    gdriver := detect;             
    initgraph(gdriver, gmode, ''); 
    cleardevice;                   
    clrscr;                        
    xcen := getmaxx div 2;            
    ycen := getmaxy div 2;
    x := xmin;
    i := 0;
    while x <= xmax do begin
       y := exp(-x*x) * sin(10*x);
       ix := round(xcen + x*xcen/xmax);
       iy := round(ycen - y*ycen/xmax);
       if i = 0 then
          moveto(ix,iy)
       else
          lineto(ix,iy);
       x := x + dx;
       i := i + 1;
    end;
    readln;
    closegraph;
end.

画面のプリントアウト

グラフィックス画面は gcopy というコマンドを実行するとプリンター に送られる。これは、絵が出ているうちに行なう必要があるので、左上の小さ いウインドウで入力する。

なお、xv で保存したものをプリントすることも出来る。これには、 xv で保 存する時にファイル形式を postscript にする。これで出来たファイルはその ままプリンターに送ることが出来る。

画面をファイルに保存する

自分で作った絵を保存するには、 xv を使えばいい。

次週予告

今週は、あらかじめグラフィックス用に準備された手続きを使ってみたが、次 週は手続きを自分で作ってみることにする。