next up previous contents index
: glib について : 制御構造 : 条件判断と繰り返し   目次   索引

プログラム例

例 5.1   まずは読み書きそろばんプログラムから.
ファイル名 cond1.rr

1:  def main(A,B) {
2:       print(A+B);
3:       print("A kakeru B=",0);
4:       print(A*B);
5:  }     
6:  end$

実行例

[0] load("cond1.rr");
[1] main(43,900)$
943
A kakeru B=38700
    
左のプログラムはあたらえられた二つの数の和と積を出力するプログラムである. 関数の引数として数 A, B を読み込む. 2, 3 行目で和と積を計算して出力する. { と } かこんだものが ひとかたまりの単位である. 実行は自前の関数 main() に数字をいれて評価すればよい. 字さげ(インデントという)をしてわかりやすく書いていることに注意.

なお, cond1.rr のロードが失敗する場合は, load("./cond1.rr"); と入力する(第1.7.3節). Windows では ``ファイル $\rightarrow$ 開く'' を用いて読み込む.


例 5.2   次に if をつかってみよう.
プログラム

/* cond2.rr */
def main(A,B) {
      if ( A > B ) {
         C=A;
      }else{
         C=B;
      }
      print(C);
}
end$

出力結果

[0] load("cond2.rr");
[1] = main(2,-54354)$
2
    
main(A,B)AB を比較して大きい方を 印刷する. 次のように自前の関数の戻り値として, 大きい方を戻してもよい. 詳しくは, 8 章で説明するが, printreturn は違う. print は画面に値を印刷するのに対して, return は関数の値を戻す働きを持つ. return の値が画面に印刷されるのは, 下で説明しているように ${\tt ;}$ の副作用である.
プログラム

/* cond2.rr */
def main(A,B) {
      if ( A > B ) {
         C=A;
      }else{
         C=B;
      }
      return(C);
}
end$

出力結果

[0] load("cond2.rr");
[1] = main(2,-54354);
2
    
main のあとに ; (セミコロン) をつけて戻り値を印刷するように していることに注意. セミコロンの代わりに $ を用いると, 戻り値をプリントしない.

字下げ (インデント) の仕方にも気をつけよう. iffor を用いるときは, 読みやすいように インデントをおこなうべきである. たとえば, 左のプログラムを

def main(A,B){if(A>
    B){C=A;}else{
C=B;}return(C);}
のように書いてもよいが読みにくい.

/* */ でかこまれた部分はコメントであり, プログラムとはみなされない. プログラム全体で利用される定数は, define 文で宣言しておくとよい. たとえば, #define AAA 10 と書いておくと, 以下 AAA があらわれるとすべて 10 でおきかえられる. この置き換えは, プログラムのロード時におこなわれるので, AAA=10 とするより実行速度の点で有利である.

例 5.3   次に for を使って繰り返しをやってみよう.
プログラム

/* cond3.rr */
def main() {
      Result = 0;
      for (K = 1; K<= 10; K++) {
          Result = Result + K^2;
      }
      print(Result);
}
end$

実行例

[0] load("cond3.rr");
[1] main()$
  385
    
左のプログラムは $ \sum_{k=1}^{10} k^2 $ を計算するプログラムである.

問題 5.1   [05]      $ \sum_{k=1}^{n} k^2 $ の表を $n = 1$ より $100$ に対して作れ.


例 5.4   上のプログラムでは, $\sum_{k=1}^n k^2$ の計算をいろいろな $n$ に対して 計算するのにいちいちプログラムを書き換えないと いけない. この問題点は関数の引数というものを使うと 簡単に解決できる. 関数とその引数については 8章で詳しく議論するが, この例に関しては下の例で十分了解可能であろう.
プログラム

/* cond3a.rr */
def main(N) {
      Result = 0;
      for (K = 1; K<= N; K++) {
          Result = Result + K^2;
      }
      print(Result);
}
end$

実行例

[0] load("cond3a.rr");
[1] main(10)$
  385
[2] main(20)$
 2870
    
左のプログラムは $ \sum_{k=1}^{{\tt N}} k^2 $ を計算するプログラムである.

N の実際の値は, 例のように main の後に 与えればよい.


例 5.5   つぎのプログラムは break による for ループからの脱出の例.
プログラム

def main() {
  for (X=-990; X<=990; X++) {
    if (X^2-89*X-990 == 0) {
       print(X);
       break;
    }
  }
}
main()$
end$

    
このプログラムは 2 次方程式 $x^2-89x-990 = (x-99)(x+10)$ の整数解を一つしらみつぶし探索で 探すプログラムである. つまり, $x$ の値を順番に変えて, 方程式を実際にみたすか調べている. 解 $-10$ が発見された時点で, break コマンドで for ループを抜け出してプログラムを終了する.

例 5.6   つぎのプログラムは線のひきかたと・の打ち方の解説.
プログラム

/* cond4.m */
load("glib");
def main0() {
  glib_open();
  glib_window(0,0,1,1);
  glib_putpixel(0.8,0.5);
  glib_line(0,0,1,0.5);
  glib_line(0,0,1,1);
}
end$

    
繰り返しをつかってグラフィックスを書く前にちょっとトレーニングを. 左のプログラムは・をうって, 線分を二本か書くプログラムである. glib_window では, グラフィックを表示する座標系の設定をしている. 引数の 0,0,1,1 は 座標 (0,0) を画面の 左上に, 座標 (1,1) を画面の右下にせよという意味である. glib で始まる関数については, 本章末の 5.3 節の解説を参照.

例 5.7   つぎに, グラフィック画面でfor の働きをみてみよう.
/* cond5.rr */
load("glib")$
def main() {
  glib_open();
  glib_window(0,0,100,100);
  for (K=1; K<=100; K = K+8) {
     glib_line(0,0,70,K);
  }
}
end$

    
プログラムを実行するには, ロードしたあと, main(); と入力する. 左のプログラムは傾きが段々おおきくなっていく線分達を描く.

K = K+8 K += 8 と書いた方が簡潔である.


例 5.8   次はグラフを何枚かつづけて書くプログラム.
/* cond7.rr */
load("glib")$
def main() {
  for (A=0.0; A<=2; A += 0.8) {
     plot(sin(5*x)+A*sin(2*x),
          [x,0,10*3.14]);
  }
}
end$

    
プログラムを実行するには, ロードしたあと, main(); と入力する. このプログラムは

\begin{displaymath}\sin 5x + a \sin 2x \end{displaymath}

のグラフを $ a = 0, 0.8, 1.6 $ について三枚描くプログラムである.

例 5.9   Taylor 展開は関数を多項式で近似する方法である. $n$ 次の Taylor 展開のグラフを描くプログラムを 書いて, Taylor 展開がもとの関数に収束して いく様子を観察してみよう.
/* taylor.rr */
load("glib")$
def taylor(N) {
  glib_open();
  glib_window(-5,-2,5,2);
  glib_clear();
  F = 0;
  for (I=0; I<=N; I++) {
    F=F+
     (-1)^I*x^(2*I+1)/fac(2*I+1);
  }
  print("sin(x) の Taylor 展開 :",0);
  print(2*N+1,0);
  print(" 次までは ");
  glib_line(-5,0,5,0);  
  glib_line(0,-5,0,5);
  print(F);
  for (K=-5; K<=5; K = K+0.03) {
     glib_putpixel(K,subst(F,x,K));
  }
}
print("Type in, for example, 
      taylor(2);taylor(4);")$
end$

    
taylor(N); と入力すると, このプログラムは

\begin{displaymath}\sin x \end{displaymath}

のテイラー展開

\begin{displaymath}\sin x = \sum_{n=0}^\infty (-1)^{n} \frac{x^{2n+1}}{(2n+1)!} \end{displaymath}

2*N+1 次まで計算して, グラフを描く. たとえば taylor(4); と入力してみよう. subst(F,x,K) は 式 F の中の x を 数 K で置き換えた結果を戻す.

例 5.10   Fourier 展開は関数を三角関数で近似する方法である. $n$ 次の Fourier 展開のグラフを描くプログラムを 書いて, Fourier 展開がもとの関数に収束して いく様子を観察してみよう. Fourier 展開は, JPEG 画像の処理などに使われている.
load("glib")$
def fourier(N) {
  glib_open();
  glib_window(-5,-5,5,5);
  glib_clear();
  F = 0;
  for (I=1; I<=N; I++) {
     F=F+(-1)^(I+1)*sin(I*x)/I;
  }
  F = 2*F;
  print("x の Fourier 展開 :",0);
  print(N,0);
  print(" 次までは ");
  glib_line(-5,0,5,0);  
  glib_line(0,-5,0,5);
  glib_line(deval(-@pi),deval(-@pi),
            deval(-@pi),deval(@pi));
  glib_line(deval(@pi),deval(-@pi),
            deval(@pi),deval(@pi));
  print(F);
  for (K=-5; K<=5; K = K+0.03) {
     glib_putpixel(
      K,deval(subst(F,x,K)));
  }
}
print("Type in, for example, 
       fourier(4); fourier(10);")$
end$

    
fourier(N); と入力すると, このプログラムは

\begin{displaymath}x \end{displaymath}

の Fourier 展開

\begin{displaymath}x = 2 \sum_{n=1}^\infty (-1)^{n+1} \frac{\sin (nx)}{n} \end{displaymath}

N 次まで計算して, グラフを描く. deval(F)F を double の精度で (11 章を見よ), 数値計算する. subst(F,x,K) では 2*sin(0.5)-sin(1.0) みたいな 式に変形されるだけなので, deval による評価が必要である.

例 5.11   次にforの 2 重ループを作ってみよう.
プログラム

/* cond6.m */
def main() {
   for (I=1; I<=3; I++) {
      print("<<<");
      for(J=1;J<=2;J++) {
         print("I=",0);
         print(I);
         print("J=",0);
         print(J);
      }
      print("  >>>");
   }
}
end$

    
for の中に for を入れることもできる. 実行例をよくみて I, J の値がどう変わっていっているか 見て欲しい.

実行例
main();
<<<
I=1
J=1
I=1
J=2
  >>>   つづきは右
    
<<<
I=2
J=1
I=2
J=2
  >>>
<<<
I=3
J=1
I=3
J=2
  >>>

例 5.12   次に不定方程式 $ x^2 + y^2 = z^2 $ の整数解を for を用いたしらみつぶし法で探してみよう.
プログラム

/* cond77.rr */
def main() {
  for (X=1; X<10; X++) {
    for (Y=1; Y<10; Y++) {
      for (Z=1; Z<10; Z++) {
          if (X^2+Y^2 == Z^2) {
             print([X,Y,Z]);
          }
       }
    }
  }
}
end$

実行例

[346] load("cond77.rr");
1
[349] main();
[3,4,5]
[4,3,5]
0
    
$ 1 \leq x < 10, 1 \leq y < 10, 1 \leq z < 10$ の範囲で, 全部のくみあわせをしらみつぶしに調べてみることにより, 整数解を探そうというプログラムである.

問題 5.2   [15]      もっと早く整数解を見つけられるようにプログラムを改良せよ. ちなみに, この方程式の解は理論的によくわかっているので, その結果を使うのは反則.


問題 5.3        引数 $N$ の階乗を返す関数を作れ.

問題 5.4        引数 $N$, $I$ に対し, 2 項係数 ${N \choose I}$ を返す関数を作れ. .

問題 5.5        引数 $N$ が素数ならば 1, 合成数なら 0 を返す関数を作れ. (整数 $I$ に対し, isqrt($I$)$\sqrt{I}$ を越えない最大の整数を 返す. )

問題 5.6       

def main() {
  for (K=0; K<5; K++) {
    print(K);
  }
}

この関数を while を使って書き換えよ.

問題 5.7        for (K=1;K<=100;K++) { X=12; Y = X*X;}
for (K=1;K<=100;K++) { X=12345678790123456789; Y = X*X;} の早さを cputime(1);を用いて比べなさい. 早さが違うときはその理由も考えなさい.


next up previous contents index
: glib について : 制御構造 : 条件判断と繰り返し   目次   索引
Nobuki Takayama 平成15年9月12日