リストの応用である多次元リストについて解説します。複雑なデータの処理ができる多次元リストは実用的なプログラミングには欠かせません。
目次
多次元リストとは
多次元リストはリストの中にリストを内包したデータ構造です。
リストではインデックス番号を使って中の要素を指定することができましたが、多次元リストでは複数のインデックス番号を使って要素を指定できます。
この記事では多次元リストの中で最も基本的な2次元リストを例に学習を進めていきます。
2次元リストは2つのインデックスで要素を指定するリストで、将棋や囲碁などのボードゲームの盤面や写真・イラスト、2DのRPGゲームのマップなどの2次元的なデータを効率よく処理することができます。
Excleなどの表形式をイメージしてもらうと分かりやすいかと思います。
例えば上記のようにshogimapという2次元リストがあったときに、その各要素は「列」と「行」という2つのインデックス番号で指定することができます。
例えばshogimap[0][0]なら銀、shogimap[2][0]なら金といった具合です。
2次元リストの作成
実際に2次元リストを作成してみましょう。
次のようにリストの中にリストを格納したものが2次元リストです。
piece_a = ["歩兵", "香車", "桂馬"] piece_b = ["飛車", "角行"] piece_c = ["銀将", "金将", "王将"] piece = [piece_a, piece_b, piece_c] # 2次元リスト print(piece)
この例だとpiece_a、piece_b、piece_cというリストがそれぞれ存在し、それらを要素とした2次元リストがpieceとなっています。
実際にこの2次元リストを出力してみましょう。実行結果は次のようになります。
[['歩兵', '香車', '桂馬'], ['飛車', '角行'], ['銀将', '金将', '王将']]
リストは全体が角括弧で囲まれ、各要素がシングルクォーテーションで囲まれてカンマで区切られて出力されます。
この実行結果をみると、[‘歩兵’, ‘香車’, ‘桂馬’]のようなリストが、さらに大きなリストの中の要素として格納されているのがわかります。これが2次元リストです。
わかりやすいように各リストを定義してから2次元リストに格納しましたが、次のように直接定義することもできます。
piece = [['歩兵', '香車', '桂馬'], ['飛車', '角行'], ['銀将', '金将', '王将']]
では実際にこのリストの要素を取り出して出力してみましょう。
piece = [['歩兵', '香車', '桂馬'], ['飛車', '角行'], ['銀将', '金将', '王将']] print(piece[0])
実行結果は次のとおりです。
['歩兵', '香車', '桂馬']
2次元リストの要素は各リストなので、インデックス番号0番のリストが表示されました。
次はこのリストの中から桂馬だけを取り出してみましょう。
piece = [['歩兵', '香車', '桂馬'], ['飛車', '角行'], ['銀将', '金将', '王将']] print(piece[0][2])
このようにインデックス番号を2つ指定することで、2次元リストの中の0番目のリスト、そしてその中の2番目の要素を指定することができます。
実行結果は次のようになります。
桂馬
文字にするとわかりにくいですが要するに表のセルを指定することをイメージしてください。
つまり桂馬を取り出すときには0列目・2行目のセルを指定しています。
2次元リストの基本操作
2次元リストについても要素を追加したり、上書き、削除する方法をみていきましょう。
といっても基本的にはリストの操作と同様です。
要素の追加
リストの末尾に要素を追加するにはappendメソッドを使います。
例として先ほどの2次元リストに新しいリストを追加してみましょう。コードは次のように記述します。
piece = [['歩兵', '香車', '桂馬'], ['飛車', '角行'], ['銀将', '金将', '王将']] piece.append(["龍王","龍馬"]) # pieceリストに要素を追加 print(piece) print(len(piece))
実行結果は次のようになります。
[['歩兵', '香車', '桂馬'], ['飛車', '角行'], ['銀将', '金将', '王将'], ['龍王', '龍馬']] 4
末尾に[‘龍王’, ‘龍馬’]という新しいリストが追加され、要素の個数が4個に増えているのがわかります。
次は2次元リストの中のリストに要素を追加してみましょう。コードは次のように記述します。
piece = [['歩兵', '香車', '桂馬'], ['飛車', '角行'], ['銀将', '金将', '王将']] piece[1].append("龍王") # pieceリストの1番目の要素となっているリストに要素を追加 print(piece) print(len(piece[1]))
実行結果は次のようになります。
[['歩兵', '香車', '桂馬'], ['飛車', '角行', '龍王'], ['銀将', '金将', '王将']] 3
インデックス番号1番である[‘飛車’, ‘角行’]のリストに龍王が追加され、要素数が3個に増えているのがわかりますね。
このように2次元リストでは2次元リストそのものに要素を追加する場合と、中のリストに要素を追加する場合があります。
要素の上書き
次は2次元リストの要素を上書きしてみましょう。こちらも要素の指定にインデックス番号をいくつ使うかという違いはありますが、通常のリスト同じように値を代入するよう記述します。
では、中のリストの飛車を龍王に上書きしてみましょう。
piece = [['歩兵', '香車', '桂馬'], ['飛車', '角行'], ['銀将', '金将', '王将']] piece[1][0] = ("龍王") # インデックス番号[1][0]の要素を上書き print(piece)
飛車はインデックス番号[1][0]なのでここに龍王を代入してあげればいいですね。
実行結果は次のようになります。
[['歩兵', '香車', '桂馬'], ['龍王', '角行'], ['銀将', '金将', '王将']]
飛車が龍王に上書きされています。
もちろんインデックス番号を1つだけ指定すれば、2次元リストの中のリストを丸ごと上書きすることもできます。
要素の削除
要素を削除するにはdel文を使います。これもインデックス番号を1つだけ指定すれば中のリストを、2つ指定すれば中のリストの要素を削除できます。
ではさきほどの2次元リストから香車を削除してみましょう。
piece = [['歩兵', '香車', '桂馬'], ['飛車', '角行'], ['銀将', '金将', '王将']] del piece[0][1] # インデックス番号[0][1]の要素を削除 print(piece)
実行結果は次のようになります。
[['歩兵', '桂馬'], ['飛車', '角行'], ['銀将', '金将', '王将']]
ちゃんと香車を削除することができました。
2次元リストをループで処理
2次元リストの基本がわかったところで、ループ処理と2次元リストを組み合わせて平面を表示する方法をみていきましょう。
次の2次元リストは将棋の盤面の初期配置での駒の有無を数字の0と1で表しています。
shogimap = [[1,1,1,1,1,1,1,1,1], [0,1,0,0,0,0,0,1,0], [1,1,1,1,1,1,1,1,1], [0,0,0,0,0,0,0,0,0], [1,1,1,1,1,1,1,1,1], [0,1,0,0,0,0,0,1,0], [1,1,1,1,1,1,1,1,1]]
1が何らかの駒を配置するところ、0が駒を置かないところ、各行のリストが将棋盤の横の並びになっていて、それを内包した2次元リストが将棋盤全体を表しています。
これを文字列で表示させてみましょう。コードは次のように記述します。
for line in shogimap: # 各行を1つずつ取り出してline変数に代入 for area in line: # line変数の各要素を1つずつ取り出してarea変数に代入 print(area, end="") # area変数を出力 print() # 各行の終わりに改行を追加
最初のfor文で2次元リストshogimapの各要素である各行のリストを1つずつ取り出してline変数に代入。
その中でさらにfor文を記述し、各行のリストの要素である各マスを1つずつ取り出してarea変数に代入し、改行コードを消して出力。
内側のループが終わったタイミング(1つの行を表示し終わったタイミング)で中身が空のprint関数を実行して改行しています。
実行結果は次のようになります。
111111111 010000010 111111111 000000000 111111111 010000010 111111111
各要素がちゃんと行ごとに表示されました。
0と1のままではわかりにくいので他の文字に置き換えてみましょう。
for line in shogimap: # 各行を1つずつ取り出してline変数に代入 for area in line: # line変数の各要素を1つずつ取り出してarea変数に代入 if area == 1: # area変数が1だった場合は print("@", end="") else: # area変数がそれ以外だったら print(" ", end="") print() # 各行の終わりに改行を追加
内側のfor文で各マスにあたる0または1を取得したあと、それが1であれば@を、そうでなければ全角スペースを表示します。
実行結果は次のようになります。
@@@@@@@@@ @ @ @@@@@@@@@ @@@@@@@@@ @ @ @@@@@@@@@
駒のあるマスとないマスがひと目でわかります。
あとは@を実際の駒に置き換えれば将棋の初期配置の完成です。
将棋盤に駒を配置してみよう
先ほどの2次元リストに駒と全角スペースを格納して中身をそのまま表示すれば将棋の初期配置はできますが、毎回このようにリストを全て手書きするのは面倒です。
そこでループを使って2次元リストを楽に作成する方法についてみていきましょう。まずは将棋の盤面である9×9の盤面を作ります。
shogimap = [] # shogimapリストを定義 for j in range(9): # 次の処理を9回実行 line = [] # lineリストを定義 for i in range(9): # 次の処理を9回実行 line.append("+") # +という文字列をlineの要素として追加 shogimap.append(line) # lineリストをshogimapのリストとして追加
このように記述してもいいのですが、リスト内包表記を使うと次のように短く簡単に記述することができます。
shogimap = [["+" for i in range(9)]for j in range(9)]
たった1行で済みました。便利ですね。
これは+という文字列を9個リストの要素として格納し、そのリスト9個を要素とする2次元リストを作成しています。
このリストを先ほど学習したループで表示してみましょう。ただし今回はリストの中身の文字列をそのまま出力します。
for line in shogimap: # 各行を1つずつ取り出してline変数に代入 for area in line: # line変数の各要素を1つずつ取り出してarea変数に代入 print(area, end="") # area変数の中身をそのまま出力 print() # 各行の終わりに改行を追加
実行結果は次のようになります。
+++++++++ +++++++++ +++++++++ +++++++++ +++++++++ +++++++++ +++++++++ +++++++++ +++++++++
縦9マス横9マスの盤面ができています。
ここに王将と金将を追加してみましょう。次のように記述します。
shogimap = [["+" for i in range(9)]for j in range(9)] shogimap[8][4] = "王" shogimap[8][3] = "金" shogimap[8][5] = "金" # ここから下は出力用のコード for line in shogimap: for area in line: print(area, end="") print()
まずループで9×9の盤面を作った後、初期配置に各駒を配置していけばいいですね。
実行結果は次のようになります。
+++++++++ +++++++++ +++++++++ +++++++++ +++++++++ +++++++++ +++++++++ +++++++++ +++金王金+++
次は歩兵を配置してみましょう。歩兵はたくさんあるのでいちいち書くのは面倒です。
条件分岐で3列目と7列目の時には歩と表示するよう記述しましょう。リストのインデックス番号と要素の両方を取り出したい時はenumerate関数を使います。
shogimap = [["+" for i in range(9)]for j in range(9)] shogimap[8][4] = "王" shogimap[8][3] = "金" shogimap[8][5] = "金" # ここから下が出力用のコード for i,line in enumerate(shogimap): # インデックス番号をi変数に、要素をline変数に代入 for area in line: if i + 1 == 3 or i + 1 == 7: # 3列目または7列目の場合 print("歩", end="") else: # それ以外の場合 print(area, end="") print()
ループで2次元リストのインデックス番号と要素の両方を取り出し、インデックス番号に1を足した値(列)が3または7の時は「歩」と表示、それ以外の場合はリストの要素となっている文字列をそのまま表示しています。
実行結果は次のようになります。
+++++++++ +++++++++ 歩歩歩歩歩歩歩歩歩 +++++++++ +++++++++ +++++++++ 歩歩歩歩歩歩歩歩歩 +++++++++ +++金王金+++
18個もある歩を1つ1つ書かずに済みました。
たくさんの要素を扱う2次元リストですが、このようにループや条件分岐を上手く使えば短くシンプルなコードで書くことができます。