計算機言語 I 第 8 回 関数 ( その 2)
この資料: http://www.math.u-ryukyu.ac.jp/~suga/gengo/2023a/08.pdf
Firefox の cache( キャッシュ ) のクリア
情報基盤統括センターでは, 各個人が作成できるファイルの総量を規制してます(Disk quota あるいは
Storage quotaという). センターが用意できる2次記憶装置の容量に限りがあるので, それを, 使用者全員で
分け合わなければならないからです.
ソフトウェアには,動作の途中でファイルを保存していくものがあります.Firefoxのようなウェブブラウ ザでは,アクセスしたWeb サイトのデータ(html ファイルや画像ファイル)を cache(キャッシュ)という形 で保存しています.例えば,「戻る」というボタンがウェブブラウザにありますが,戻るボタンが押された際に は,ネットのデータにアクセスするより,キャッシュにあるデータにアクセスします. ネットからデータを読む より,ディスクデータにアクセスする方が,遥かに速くデータが得られるからです
このウェブブラウザのキャッシュのせいで,上で述べた個人のDisk quota を越えるという現象が起きてい るようです. Geditで「ファイルが保存できない」とのメッセージが出た場合, Disk quotaの制限のためです. 次の手順で, Firefoxのキャッシュを消去して下さい.
1. Firefoxを起動する.
2. Firefoxの上部右側(URLを入力する欄があるところの右端)のメニューボタンをクリック
3. 現れるメニューから「設定」を選ぶ.
4. 左側の「プライバシーとセキュリティ」欄を選ぶ.
5. 中心となるウィンドウの中の「Cookieとサイトデータ」のところにある,「データを消去」ボタンを押 す.(これでキャッシュデータの消去ができます.)
6. 次の Firefoxを閉じたときに Cookieとサイトデータを消去するにチェックを入れておくと, Firefox
が原因で, Disk quotaが問題になることは無くなります.
レポートへのツッコミ
Cのプログラミングで関数を利用する理由は,次のふたつです.
• プログラムの中で何度も同じ処理をする場合,それを一箇所にまとめて,バグの発生を減らす.
• プログラムの処理を適当な大きさに分割する事により,プログラムの読み書きを容易にする.
割と難しい事が,「関数の仕様策定」です. 同様のことをできる仕様が複数考えられるわけですが, その中で どれを採用するかとういう問題です. 中でも「エラー処理」をどうするかが,難しくなります.
レポート問題では, ヘロンの公式を利用した関数を書いていただきましたが,エラー処理は,「面積の値とし てはあり得ない, 0以下の数を返す」ということで実現する仕様にしたわけです.
他にも関数設計の考え方はあり,例えばエラー処理を平方根を取る関数sqrtに任せるというのも, ひとつ の考え方です. ちなみに, 数学関数ライブラリのsqrt 関数は, 負の値を受け取ったときには, NaN (Not a
Number, double型の値として定義されている)という値を返します. ただし, NaNは0以下の数とは定義さ
れていません.
レポート問題に対する私が考えた模範解答は, 次です. 下のプログラムでは, Sは3角形を成さない正の 3 つの数を入力すると, 0以下になること(証明は各自考えること)を利用しています(すなわち, 3角不等式の チェックが不要.).
「return文があると,そこで関数は値を返して処理を終了する」は覚えておいて下さい. double heron(double a, double b, double c)
{
double s, S;
if ( a<=0 || b<=0 || c<=0){/* 辺の長さは正 */
return 0.0;
}
s=(a+b+c)/2.0;
S=s*(s-a)*(s-b)*(s-c);
if (S <=0) { /* このとき 3角形にならない */
return S;
}
else { /* 面積は, S の平方根 */
return sqrt(S);
} }
レポートに対するツッコミ
• 3角不等式でエラーチェックするには, 3通りのチェックが必要です.
• 3角不等式のエラーチェックをmainとheronの両方で行うプログラムがありましたが,片方で十分で あることに気づいてください.
• 辺の値が負の数になったときにチェックをした人は,少数でした.
• sqrtが返す値は, 0以上かNaNで,負の数を返すことはありません(数理の3年になってこの常識が 持てないのは辛い).
• コンパイラーを通らないプログラムがありました. この講義では,実際にプログラムを書いて実行し,正 しく動作するかをチェックしてからレポートを提出してください. 間違いをチェックできる環境を利用 しないという態度は,大学生としてあり得ません.
教科書 6.4 節の補足
「Cの関数の引数は値渡し」は,いわゆる「構造化プログラミング」を実現するための言語仕様です. すなわ ち,データを変形できる範囲をできる限り狭くすることで,値の変化を人間が追いやすくするためです. ただ し,値渡しだけでプログラムを書くのは, CPUの仕組みが理由で, 効率性を犠牲にすることもあるので,ポイ ンタを利用した参照渡しも可能にしてあるのです. このあたりの詳しい考え方は, 次回のポインタの時に解説 します.
教科書 p.92, 6.6 節の補足
ヘッダ(header)ファイルに対する解説がありますが,少し補足しておきます.
#includeは,cpp(Cプリプロセッサ)への指示で,そのあとに続くファイルの内容をプログラムソースに取 り込みます. ほとんどの場合,教科書にあるようにヘッダファイルと呼ばれるものを取り込みますが,それ以外 のファイルを取り込まないわけではありません. C のソースファイルを取り込んでも構わないし,プログラム で利用するデータを取り込んでも構わないのです.
この講義で利用しているstdio.h,math.hはヘッダファイルと呼ばれます. 多くの場合,ヘッダファイルに は,次の2つを記述します.
• cppのマクロ置換を利用した定数の定義
• プログラムソースに記述する関数のプロトタイプ宣言
大きなプログラムの作成では,ヘッダファイルに上のような関数のプロトタイプと定数定義を記述し,プロ グラムソース(hogehoge.c)は,「関数の実装」を書くようにすることが多いようです. このようにすることで, ヘッダファイルを読むだけで,どのようなプログラムを作成しているかの概要がわかるからです. つまり,概要 をプログラム本体を読むことなく知ることができるのです.
教科書 p.93, 6.7 節 , 再帰 (recursion) の補足
再帰とは, 関数の処理の途中で自分自身を関数呼び出しすることです. 再び自分自身に帰ってくるので再帰 と呼ばれます.
計算機概論Iのmapleの講義の回で,再帰の話を1度しました. その際にも述べたことですが,再帰の利点, 欠点は次になります.
• プログラムの記述が簡潔で分かりやすく(読みやすく)なる.
• 関数呼び出しの度にスタック領域と呼ばれるメモリを消費するので,処理によってはメモリが足りなく なる.
• 関数呼び出しという動作が数多く起こるため,プログラムの動作が遅くなる(今回のレポート問題).
今回のレポート問題にある様に,再帰を繰り返し処理で書けるのであれば,実行時間を考えると,繰り返し処 理で記述した方が望ましいのがほとんどです.
ただし, 教科書のHanoiの塔の様に再帰でしか書けないような処理もありますし, 繰り返し処理に変換する
のが大変な場合もあります. 後者の例としては,プログラム言語処理系の作成が良く取り上げられます. プログラム言語処理系では,次の動作をして実際にコンピュータで動作するプログラムを作り上げます. 字句解析 プログラムのソースコードを「単語(word)」に分解して,それがキーワードであるか, 数値である
か,識別子であるか,演算子であるかなどを調べる.
構文解析 上の単語の並びから,実際にコンピュータでしなければならない動作を確定させる.
例えば, Cを例にとると,「数から始まる文字列」があれば,それは数値に変換されますし,アルファベットか ら始まる文字列なら,それは,キーワードか識別子のどちらかをチェックします(字句解析). また,(x+y)*zの 様な式があれば,x+yを計算してその結果に zを掛けるという動作をさせるように機械語を生成します(構 文解析).
このような動作ができるようにプログラミング言語の文法が決まっているわけですが, それを記述するのに よく用いられるのが,拡張バッカスナウア記法(Extended Backus–Naur form, EBNF)です(その詳細は書く と大変なので,ここでは述べない). EBNFで記述されたプログラム言語の処理系を作成しようとすると,次の ような複数の関数にまたがる再帰処理を書くのが最も自然です(書く方も書きやすいし,読む方も読みやすい).
func1() {
...
func2();
}
func2() {
...
func3();
}
...
funcN() {
...
func1();
}
レポート問題
締切: 6月12日(月) 10:00(JST)
a0=a1= 1,an+1=an+an−1, (n≥1) で定義されるFibonacci(フィボナッチ)の数列について, 次の2 つのプログラムを書き,そのプログラムを提出するとともに, 3番目の結果をレポートせよ. ただし,どちらの 関数も,unsigned long long型(符号無し64bit整数)の変数を利用して,a50の正しい値が表示できるよう にすること. printfでのunsigned long longの出力の指定方法は,%llu. (man 3 printf を参照).
1. 繰り返し (for でも while でも良い) を用いて, 自然数 nの入力に対して, an を出力する関数 fibonacci(int n)を書き,main()では,a50を出力するプログラム.
件名: gengo2023 report 8-1.
2. 再帰を利用して, 自然数nの入力に対して,anを出力する関数fibonacci(int n)を書き,main()で は,a50を出力するプログラム.
件名: gengo2023 report 8-2
3. 上の2のプログラムの実行は終了するか? 2のプログラムが終了するなら,上の1, 2のプログラムにつ いてtimeコマンドでレポートされる内容を提出せよ.
件名: gengo2023 report 8-3.
timeコマンドは,time ./a.outで,a.outの実行に対して要したCPUの使用時間をレポートします.