わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

レポート課題を振り返る

プログラミングの講義で,2回のレポート課題を出しています.出題意図など,学生に十分に説明できていないので,ここで整理することにします.
2つの課題に共通しているのは,こちらは紙に打ち出したソースファイルを用意し,学生にはそれを演習室または自宅などで正しく入力して,それから小問を解いてもらうことです.ソースにコメントは一切なし.「打ち込む」だけでなく「読み解く」ことも問われます.その一方で,動かさないと(ソースを眺めているだけでは)解けない問題も入れています.それと今年度は,「ニュートン法」「マルチバイト文字」といった,語句を調査する問題を取り入れてみました.
では個別に.
1回目は10月末に提示しました.授業は4回実施で,識別子・型・制御文・演算子までを説明したところです.ということで配列・ポインタなどを使えない段階ですが,それでも,繰り返し処理(反復)で何かの数学的な解を求めるには十分です.昨年度は四分円の面積の近似値を求める課題でした.今年は,ニュートン法で,2の4乗根の近似値を求めるプログラムにしました.
ニュートン法というと,初期値に注意ですね.初期値が解から遠いと,反復回数が多くなりますし,除算があるので,0で割ることが起こってはいけません.そういう背景で,0になる入力で実行するとどうなるかという問題を出してみました.double型の値をprintfで出力させてみると,自分の環境ではinfやnanが出てきました.
小問の最後は,等価変換.乗法演算子を減らしてもらいます.いくつも方法があるので,一つを挙げてくれれば丸をつけます.数学の式変形を意図していましたが,答案の多数は,べき乗にライブラリ関数のpowを使うというものでした.そして多くの学生が,コンパイル時に「-lm」オプションをつけることも指摘していました.こちらで手法をパターン化し*1,ついでに,すべての乗法演算子を消した代替コードを作って掲示しました.
2回目の課題は,冬休みの宿題です.授業は11回やっているので,配列・ポインタ・関数・再帰・標準ライブラリ関数・構造体まで使えます.授業で取り上げていないのは,前処理指令*2と入出力くらいです.
今年は,2次元離散平面上での経路探索にしました.再帰を使ってバックトラックをするものです.技術的には,「1次元配列のデータ構造を2次元的にアクセス」「一つの整数値にビット単位で複数の情報を与えること」「上下左右の方向を配列に持たせてforで回す*3」「マルチバイト文字で出力」を盛り込みました.C入門の範囲を超えているところもありますが,ともあれ打ち込んで動かすのであれば,別段難しくありません.150行ほど,写経*4してもらいました.
小問では,コンパイルエラーに対する簡単なデバッグ(関数プロトタイプのセミコロン抜け,変数宣言漏れ)から入ります.例年通りなのですが,コードを見るだけで気づく人もいれば,打ち込む途中に違和感を覚える人も,タイプミスをしていてそれがエラーの原因になって苦労する人もいたのではないかと想像します.なお,論理エラーのデバッグは,そこで間違うと残りの問題が解けなくなるので,課していません*5
全体として何をするプログラムか,そして特定の式はどういう意味なのか,といった「読めるか」を問うのは例年通りですが,今回は等価変換の代わりに,「もとのコードではおかしな出力になるけど,1箇所だけ変えると,きれいな図形になる」のでそれを見つけてもらう問題を入れました.意図だけ言えば「縦(column)と横(row)を間違えないように」なのですが,予想していない別解がありました.すぐに正しいかどうかわからない答案は,こちらでも動かして,正否判定しました.
これまでにない,しかし考えてほしい問題も,作りました.ひとつは,「再帰呼び出しをする関数」と「実行しても一度も呼び出されない関数」を指摘するといもの.指摘するだけですので,「目で見りゃわかる」のですが,コードが大きくなるとそうはいきません.テキストエディタで検索する方法と,関数の呼び出し関係をグラフにしてmainからの到達可能性を判定する方法を,解説に書いておきました.
もう一つは,究極の問題とも言われる,「自分で問題を作って解きなさい」です.実に多彩な“問題”が出ました.これから,整理していかないといけません.
最後に,触れたくはないけど触れないといけないことを.内容の酷似した答案がいくつか見られました.現在,該当学生にメールを送って,期限を決めて返事を待っているところです.もちろん送った当日に反応をくれた学生もいましたが,この件については,期限後に改めてここで報告し,レポートのコピーについて自分の態度を表明したいと思います.

*1:他に,「xの4乗は,x=x*x*x*x; よりも x*=x;x*=x; とする」とか「関数を定義して処理を共通化する」を挙げておきました.とはいえ,2回の2乗を書いた答案も,なかったのですが.

*2:ただし,#includeは,標準ライブラリ関数に絡めて先に説明しています.

*3:配列を写像として用いる例をもう一つ - わさっき

*4:…というほど「ありがたい」コードではありませんが.

*5:ある年度の試験で課して,採点に苦労したものです.