コンテンツへスキップ
Topos Uranos

要約

このレッスンは、コースの第 5 単元、関数を扱う単元の始まりである。単元が何のためにあるかを説明する。問題を名前のついた部分に分解し、それぞれにインタフェースと契約を与えること、そしてある種の問題を、それ自身のより小さな事例に帰着させて解くこと、すなわち再帰を教えることである。単元の四つのレッスンと、それらを結ぶ糸を紹介する。単元が前提とすること、とりわけ第 4 単元のループとその不変条件を振り返る。単元が解決する問題を述べる。同じ計算をコピーして繰り返し、コピーの際の不注意のせいで、コンパイラが何も言わないまま誤った答えを出すプログラムの問題である。単元が各部分の仕様を定める形式として、契約(事前条件と事後条件)を前もって紹介する。そして、この先の単元と「C++ プログラミング II」のコースで、単元がどこへつながるかを示す。

学習目標

  1. 単元の内容の順序を説明し、各レッスンが次のレッスンに何をもたらすかを説明する。
  2. 第 4 単元のアルゴリズム(ループのトレース、その不変条件と変量)を思い出し、必要なら復習する。
  3. 例を通して、契約の考え方を前もってつかむ。プログラムの一部分がそれを使う側に何を求め、その代わりに何を保証するかである。
  4. 単元を終えたときに何ができるようになっているべきかを知り、それをこの先の単元で学ぶことと区別する。

導入テスト

単元に取りかかる前に、出発点を測っておくとよい。このテストは自己チェックのためのもので、採点はしない。目的はただ学習の方向づけにある。最初の五問は、単元が前提とすることを確かめる。ループのトレース、その不変条件、仕様の事前条件、初期化での値のコピー、そして第 4 単元のプログラムの中から、それ自身の入力と出力をもつ計算を見つけることである。続く五問は、レッスンの順に、単元の中心となる考え方を扱う。コールスタック、実引数の渡し方、計算と表示の分離、階乗の再帰的定義、そして自分自身を呼び出す関数の実行である。まだ正解できなくてもかまわないし、そのうちのいくつかでは「まだわからない」が誠実な答えである。正解であれ不正解であれ、どの答えもその要点を説明し、それを扱うレッスンを示すからである。同じ技能は、単元の最後のまとめのレッスンで再び測る。

導入テスト

単元を始める前に、自分の出発点を測りましょう。短い自己評価です。採点はされず、結果はあなたのブラウザーにだけ保存されます。

  1. 問 1前の単元からint n{305}; int c{1}; while (n >= 10) { n = n / 10; ++c; } std::println("{}", c); は何を出力するか。

  2. 問 2前の単元からループ int total{0}; int k{1}; while (k <= n) { total += k; ++k; }(n≥0n \geq 0)で、条件を調べるたびに成り立つ関係はどれか。

  3. 問 3前の単元から除算の仕様は「整数 a≥0a \geq 0 と b>0b > 0 が与えられたとき、qq と rr を求めよ。ただし a=b⋅q+ra = b \cdot q + r かつ 0≤r<b0 \leq r < b」と述べる。その事前条件は何か。

  4. 問 4前の単元からint a{3}; int b{a}; b = 7; の後、a の値はいくつか。

  5. 問 5前の単元からレッスン 4.4 の、100 より小さい素数を出力するプログラムには、候補を順にたどるループがあり、その中に候補の約数を試す別のループがある。それ自身の入力と出力をもち、名前をつける価値のある計算はどの部分か。

  6. 問 6プログラムは main から始まる。main は関数 g を呼び出し、g は別の関数 h を呼び出す。h の実行中、進行中の呼び出しはどれか。

  7. 問 7関数 int f(int x) { x = x + 1; return x; } があるとき、int a{3}; int b{f(a)}; std::println("{} {}", a, b); は何を出力するか。

  8. 問 8あるプログラムは日付を読み込み、その曜日を出力しなければならない。計算と表示を切り離す分解はどれか。

  9. 問 9次の定義のうち、n!n! を定義するものはどれか(すべての自然数 nn について)。

  10. 問 10int f(int n) { if (n == 0) { return 0; } return n + f(n - 1); } があるとき、std::println("{}", f(3)); は何を出力するか。