はじめに
連載2回目は、プログラミング歴2年余の伊藤が担当します。今回のパズルゲームは、プログラミングを始めたばかりの半年に満たない頃に作成したものです。当時を振り返りながら、初心に還ったつもりで解説しますので、よろしくお願いします。
伝統的なスタイルを卒業して、OOPを習得するときに、越えなければならない壁の一つが配列です。2次元配列を扱った事例は多数ありますが、ともすると配列が主役になり、その中身が脇役になる場面も少なくありません。そこで、発想を転換して主客逆転させると、新しい世界が見えてくるかもしれません。
対象読者
こんな症状を抱えているなら……。
- 配列を扱うたび、例外
ArrayIndexOutOfBoundsException
に悩まされる。
解けないパズルに懸賞金
Fifteen Puzzleは、米国のパズル作家Samuel Loyd(1841-1911)が、1878年に創案したとされています。最初に整然と並べられた状態から「14と15だけを入れ換えた後で元に戻せるか」という問題に懸賞金が賭けられたほどですが、後に、不可能であることが証明されます。
このパズルは「スライディングブロックパズル」に分類され、4×4に区切られたボードの上に番号(1~15)を振ったコマが並べてあります。一つだけ空いたマス目を利用して、これらのコマを移動させながら、目的の形を作ります。
ここで、懸賞問題が解けないことを簡単に(実際の証明はもっと複雑)説明しておきます。コマの移動は、空いたマス目と交換する操作と見なせます。そのため、他のコマに手を触れず、任意の2つのコマだけを交換できません。
今年も世界大会が開かれたルービックキューブは、これを3次元化したものと見なせます。分解して組み直す(途中で諦めて)ときに、キューブの方向を間違えると、元に戻せなくなります。これを逆手に取って、わざとそのキューブを仕込んでおくと、近所の腕自慢を当惑させられるかもしれません。
配列の隘路
現実の世界では、ゲーム盤の外にコマが飛び出すことがありますが、プログラミングの世界でも、同様のバグに悩まされます。配列の添字が範囲を超えると、C言語では実行時に暴走しかねません。Javaでは、運が良いとコンパイル時に警告されますが、運が悪いと実行時に例外ArrayIndexOutOfBoundsException
を生成します。
配列の添字は「0から始まるべきか1からか」という論争は、プログラミング言語の違いを越えて、今も続いています。例えば、Javaで条件式を記述するときに、
for (int i = 0; i <= array.Length; i++)
等号「=
」が必要かどうかで、迷ったりしませんか。この添字問題とよく似た状況が、部分文字列にも見られます。
ある事情:Javaの立場
Javaでは、部分文字列を生成したいとき、次のように記述します。
substring(begin, end);
ここで、先頭の位置が0になるのは、後に示すC#と同じです。先頭はbegin
ですが、違いはその末尾です。例えば、次のようにすると、
"string".substring(3, 5); // Java
得られる部分文字列は、"in"でしょうか。"ing"でしょうか。山手線の先頭と末尾も気掛りですが、ここは先を急ぎましょう。
ある事情:C#の立場
C#では、部分文字列を生成したいとき、次のように記述します。
Substring(startIndex, length);
先頭の位置startIndex
とそこからの長さlength
を指定して、部分文字列を生成します。例えば、次のようにすると、
"string".Substring(3, 2); // C#
部分文字列"in"が得られます。先頭の位置が0になるのは、Javaと同じです。3番目の位置から長さ2の部分文字列を生成するときに、選択の余地はありません。
ある事情:Pythonの立場
Pythonでは、部分文字列を生成するときに、スライスという概念を適用します。スライスとは、どの位置で「切り分けるslice」かということです。
指定した位置で文字列を切り分けたと考えるなら、添字end
の位置は、生成される部分文字列には含まれません。山手線の起点/終点を知らなくても問題ありませんが、部分文字列の先頭/末尾は押さえておきたいものです。