配列 4. 順番に並べる

配列に後から要素を追加したり削除したりする練習です。
7行目までの部分でcharactersという配列が用意され、Blu型キャラクター、ワープポータル、Hopper型キャラクター、宝石が入れられます。キャラクター以外のものが混じってたりするので修正したいのですが、直接4行目や6行目を削除することはできません。ここでは一旦作って中身を入れてしまった後の配列に対し、.removeメソッドで要素を消したり.insertメソッドで追加したりする練習をするのが目的だからです。

まず正解例はこんな感じになります。

■配列から要素を削除したり追加するには

配列名に対してドット記法で.removeや.insertメソッドを指定します。この際、.removeなら何番目の要素を削除するかをパラメーターとして指定してやります。

ここで非常に重要な注意点があります。3〜6行目をみると、余分な要素であるポータルと宝石は2番目と4番目にあります。配列の中身は0から数えるルールなので、

としてみたのではないでしょうか?そうすると宝石を削除する方が配列の範囲外エラーになります。どうしてでしょう?この理由をイメージできる感覚はプログラミングにとても重要な気がしますので、じっくり考えてみてください。正解は最後に。

でとりあえず次は挿入。.insertの場合は、なにを足すかと、どこに割り込ませるかを指定します。

■16行目以降について

今回はループ毎に配列の中身を順に読み出しても変化するのはキャラクターの種類だけです(Blu->Expert->Hopper)。それとは別に配置する位置は自分でズラしていかなければなりません。そこで、rowの値が繰り返し毎に1ずつズレていくよう別の変数rowPlacementを用意して工夫します。最初に16行目で0をセットしておくことで、初回のループではrow:パラメーターが0になり、(1,0)の位置にBluが出現します。そして繰り返しで戻る前の20行目で1を足しておくことで、2回目は(1,1)にExpertが、3回目は(1,2)にHopperが出てくるわけです。

■なぜ12行目は範囲外エラーになるのか?

さて、あえて正解例コードにバグを入れておいた「範囲外エラー」の答え合わせについてです。範囲外エラーなので試しに数字を小さくしてみれば上手くいっちゃったりするかも知れません。そう、正解は、3ではなく2ですね。大事なのは何故かです。これは直前の行でワープポータルを削除したことに関係します。
最初はこうでした。

その後で、

したらどうなるでしょう?

こうですね。つまり、charactersの中のオブジェクト数は4から3に減りました。そして、Gem()の順番も4番目でなく3番目に繰り上げになっています。配列は0番スタートなので、3番目ということは0,1,2で2を指定する必要があるわけです。3を指定するということは存在しない4番目を削除しようとすることになるので、範囲外エラーというわけですね。
もちろん、.insertする場合も影響しますので、常にその時点で配列の中がどうなっているかを意識しましょう。

配列 3. ブロックを積む

配列の各部屋には

のように整数を入れるだけでなく、様々な型のオブジェクトを入れることができます。

このステージでは列番号と行番号をひとまとまりにしたCoordinate(座標)型というオブジェクトを配列に格納します。ちょっと入れ子のようでややこしいですが、「カンマで区切られた数だけ、同じ型のものが順番に入れられる」という意味では整数の時と全く同じなので気を楽にしてください。

まずは正解例はこんな感じになります。

3行目から8行目は長いので改行していますが、[]でひとまとまりで、4つのCoodinate型オブジェクトをblockLocationsという名前の配列に入れていることになります。

もしピンとこなければこういうコードで考えてみてください。

正解例コードの14行目でもやっていますが、「後で名前で参照できなくて良いオブジェクトはいきなり型名を指定してもOK」でしたよね。「ステージを作る」章の「階段を置く」で出てきた省略記法です。これと同じことをすると、上のコードは、正解例コードの3〜8行目のように書けるわけです。

ということで、blockLocationには4箇所の座標(ステージの4隅)を示す4つのCoordinate型オブジェクトが入りました。これをfor〜in文を使って繰り返します。blockLocation配列から1つずつ取り出されてcoodinateという名前でループ内で利用可能になるので、13行目のworld.place文でat:パラメーターの中身が置き換えられます。最初のループでは、

としたのと同じことになります。2ループ目は

ですね。

配列 2. 反復処理を試す

for文によるループと配列の組み合わせを学ぶステージです。配列に入っているひとつひとつの要素(オブジェクト)に対して同じ処理をさせることができます。全く同じ処理というだけでなく、要素毎の違い活用した処理ができるのが強みです。
例えばこのステージでは、0〜5番目の各列に宝石とスイッチを配置します。今までのやり方だと、こんな風になります。

ほぼ同じパターンが5回繰り返されますが、残念ながらatColumn:パラメーターが違うので単純なfor文ループにはできません。
まさにこんな時に、配列を使った繰り返しが役に立ちます。
同じforで始まりますが、

のように書きます。これで「(配列名)の中にある要素ひとつひとつを(ループ内での変数名)に入れつつ、(ループさせたい処理)を実行」という意味になります。
正解例のコードを見てみましょう。

columns配列の中身が前から1つずつ取り出され、columnという変数に入れられてループ内の処理が行われます。1ループ目はcolumnの中身は0です。なので、world.placeメソッドの中のatColumn:パラメーターには0と書いたのと同じことになります。
そして最初のループが終わって繰り返される時、columnには2番目の値の1がセットされます。つまり2回目のループではatColumn:は1になります。以下、atColumn:が2,3,4,5と変化しながら繰り返されていきます。最終的に全ての陸地にスイッチと宝石が並んでいることでしょう。
このように、ちょっとずつ違う処理を繰り返す時に、その違う部分を変数に置き換えておき、このfor〜in文で毎回変数の中身を変えてやる方法が利用できます。

配列 1. 情報をまとめて保存

配列とは変数の一種ですが、中に仕切りがあってそれぞれの部屋に通し番号がついているものを想像してください。仕切りがあるので中に複数の値やオブジェクトを格納しつつ、全体をひとまとまりとして扱うことができます。また中身を通し番号で指定できるのでループと非常に相性が良いのが特徴です。
出題文通り、まずはそのまま実行してみましょう。4体のキャラクターが出現します。これは、最後の行にある

がrowsという配列変数の中のひとつひとつの数字を座標にしてキャラクターを並べてくれます。placeCharactersコマンドについてはあまり深く考える必要はありません。ともかくrowsの中に0が入っていたら(0,0)に、1があれば(0,1)にキャラクターが出ると思っておきましょう。
さて、最初の状態では、4体しか出てきません。空いたコマが2つあります。(0,2)と(0,6)ですね。逆にいうと、(0,0)、(0,1)、(0,3)、(0,4)に出現しています。これがそのまま先頭行の

から来ているわけです。足りない数字を追加してやりましょう。数字と数字は半角カンマで区切ります。

(標準の各英文コメントの後ろに訳を入れてあります)

ステージを作る 7. 自分だけのステージ

ここは特にクリア条件もなく、world.place()メソッドで好きなオブジェクトを置いてみましょう、というステージなので、それこそ正解はないので今まで憶えたパターンをおさらいしてみましょう。
座標は列番号columnと行番号rowの組み合わせで指定します。マップ上の特定のコマをタップすると(0,0)のような形で表示されますので、自分で数えるよりも早いでしょう。