天気がいいのに、教室で1時間過ごすのは気が進みません。 いらいらしながら教室に入ると、黒板に妙に心地よい三角形が描かれているのに気づきます。 数学の先生が来るのを待つ間、その三角形のいくつかのパターンに思わず目がとまります。外側の値はすべて1で、次の行は前の行より値が1つ多く、三角形は左右対称です。 不思議です!
席に着いてまもなく、先生が教室に入ってきて、この三角形が有名なパスカルの三角形だと説明します。
その後の1時間で、先生はこの三角形に隠された驚くべき事柄をいくつか明かします。
先生は、ほかの使い道も調べてみるようにとクラスの皆に勧め、まだまだたくさんあると請け合います。 ちょうどそのとき、学校のチャイムが鳴ります。 気づけば、この1時間、パスカルの三角形の学習にすっかり夢中になっていたのです。 急いでかばんからノートパソコンを取り出して外へ出ると、太陽の光_と_パスカルの三角形の不思議の両方を楽しむ準備は万端です。
パスカルの三角形の最初のN行を出力するのが課題です。
パスカルの三角形は、正の整数が三角形状に並んだ配列です。
パスカルの三角形では、各行の値の数はその行番号と同じです(行番号は1から始まります)。 したがって、1行目には1つの値、2行目には2つの値があり、以下同様に続きます。
最初の(一番上の)行には、1という1つの値だけがあります。
それ以降の行の値は、1つ前の行にある現在位置のすぐ右と左の数を足し合わせて計算します。
1つ前の行の現在位置の左または右に値が_ない_場合(これが起こるのは一番左と一番右の位置だけです)、その位置の値は0とみなします(つまり、足し算では実質的に「無視」されます)。
パスカルの三角形の最初の5行を見てみましょう。
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
一番上の行には、1という1つの値があります。
一番左と一番右の値は、考慮すべき前の位置が1つだけです。それは、一番左の値にとっては右隣、一番右の値にとっては左隣の位置です。
一番上の値が1であることから、一番左と一番右の値もすべて1になることがわかります。
それ以外の値はすべて、考慮すべき位置が2つあります。
たとえば、5行目(1 4 6 4 1)の中央の値は6です。1つ前の行にあるその左と右の値が3と3だからです。
この演習は、ループではなくrecursionを使って解くように設計されています。 再帰関数とは、自分自身を呼び出す関数で、自分自身を使って定義される問題を解くときに役立ちます。 無限の再帰(より正確には、スタックのオーバーフロー)を避けるために、「ベースケース」と呼ばれるものを使います。 ベースケースに到達すると、再帰しない値が返されます。すると、1つ前の関数呼び出しが値を確定して返せるようになり、同じことがスタックをさかのぼって順に伝わり、最終的に最初の関数呼び出しが答えを返します。 5!(つまり5 * 4 * 3 * 2 * 1)の答えを求める再帰関数は、次のように書けます。
def factorial(number):
if number <= 1: # base case
return 1
return number * factorial(number - 1) # recursive case
print(factorial(5)) # returns 120
最後に、Pythonでは再帰呼び出しの回数に上限があり(デフォルトでは1000回)、末尾再帰は最適化されないことに注意してください。
ときには、例外を発生させる必要があります。 そのときは、エラーの原因が何であるかを示す意味のあるエラーメッセージを必ず含めてください。 これによりコードが読みやすくなり、デバッグにも大いに役立ちます。 エラーの原因が特定の種類になるとわかっている場合は、組み込みのエラーの種類から選んで発生させることができますが、それでも意味のあるメッセージを含めるようにしてください。
この演習では、rows()関数に負の数が渡された場合に、raise文を使って複数のValueErrorsを「スロー」する必要があります。
テストが通るのは、raiseでexceptionを発生させ、それにメッセージを添えた場合だけです。
メッセージ付きのValueErrorを発生させるには、exception型の引数としてメッセージを書きます。
# if the rows function is passed a negative number.
raise ValueError("number of rows is negative")