LoginSignup
1
0

Pythonで〇×ゲームのAIを一から作成する その5 ゲーム盤のデータ構造と繰り返し処理

Last updated at Posted at 2023-09-19

目次と前回の記事

前回のおさらい

前回の記事では、〇×ゲームの下記の 仕様 1 を実装するために必要な知識として、配列と list について説明しました。

  1. 正方形で区切られた 3 x 3 の 2 次元のゲーム盤上でゲームを行う
  2. ゲーム開始時には、ゲーム盤の全てのマスは空になっている
  3. 2 人のプレイヤーが遊ぶゲームであり、一人は 〇 を、もう一人は × のマークを受け持つ
  4. 2 人のプレイヤーは、交互に空いている好きなマスに自分のマークを 1 つ置く
  5. 先手は 〇 のプレイヤーである
  6. プレイヤーがマークを置いた結果、縦、横、斜めのいずれかの一直線の 3 マスに同じマークが並んだ場合、そのマークのプレイヤーの勝利とし、ゲームが終了する
  7. すべてのマスが埋まった時にゲームの決着がついていない場合は引き分けとする

ゲーム盤のデータの表現

前回の記事では、ゲーム盤と同じ大きさの 3 x 3 の表を表す 2 次元配列のデータを下記のようなプログラムで list を用いて記述しましたが、このデータは〇×ゲームのゲーム盤とは異なるデータです。

board = [
    [1, 4, 7],
    [2, 5, 8],
    [3, 6, 9]
]

マスを表すデータの表現

このデータを、〇×ゲームのゲーム盤のデータを表すように修正するためには、マスのデータをどのように表現するか を決める必要があります。仕様の中でゲーム盤のマスの内容に関する仕様を探してみると、下記の 仕様 3、4 が見つかります。

3. 2人のプレイヤーが遊ぶゲームであり、一人は 〇 を、もう一人は × のマークを受け持つ
4. 2 人のプレイヤーは、交互に空いている好きなマスに自分のマークを 1 つ置く

上記の仕様 3、4 から、マスを表す list の要素には「空白」、「〇」、「×」の 3種類 を表すデータを格納する必要があることがわかります。3 種類のデータを 区別して表現 するには様々な方法がありますが、今回の記事では初心者でも比較的思いつきやすそうな方法を採用することにします。

それは、ゲーム盤のマスには「空白」、「〇」、「×」という文字1が入るので、それらの文字をそのまま使ってマスを表すデータを表現するという方法です。Python に限らず一般的なプログラム言語は、文字列2型のデータを半角の " または ' で囲んで記述します。そこで、空白を 半角の空白文字" "、〇を "〇"、×を "×" で表現することにします。

文字列型のデータを囲う記号

文字列を囲う記号に "' の 2 種類があるのは、文字列の中に 文字列を囲う "'入れる ことができるようにするためです。例えば、先頭と末尾" という文字を含んだ "abc" という文字列を、プログラムで ""abc"" のように記述すると、最初の 2 つと最後の 2 つが文字列を囲う記号のペアとして認識されるので、実行すると以下のようにエラーになります。

print(""abc"")

実行結果

  Cell In[2], line 1
    print(""abc"")
            ^
SyntaxError: invalid syntax

上記のエラーメッセージは、以下のような意味を持ちます。

  • SyntaxError
    構文(文法のこと)(syntax)に関するエラー
  • invalid syntax
    無効な(invalid)構文(syntax)でプログラムが記述されている。

エラーメッセージの上に記述されている ^ は、エラーの原因の可能性が高い場所を表しており、このプログラムは ^ の上の abc の部分が文法的に間違っています。

文字列型のデータのシンタックスハイライト

下記のプログラムのエラーは、プログラム内に記述した 文字の色 から判別することができます。

print(""abc"")

以前の記事でも説明したように、この色分けの機能のこと シンタックスハイライト と呼びます。Qiita のシンタックスハイライトでは、文字列型のデータを表す文字の色は、文字列を囲う記号を含めて 水色 で表示されます。上記では、abc の部分が水色で表示されていないことから、その部分が文字列型のデータとして 認識されていない ことがわかります。

なお、シンタックスハイライトの色は ソフトごとに異なり ます。VSCode では文字列型のデータを 茶色 で表示する点に注意して下さい。

中に " が入った文字列の記述方法

以下のプログラムのように '"abc"' と記述すれば、外側の ' が文字列を囲う記号として認識され、その 内部"文字列の一部 として認識されます。また、'"abc"' の部分が 全て水色 で表示されていることから、文字列型のデータとして認識されていることがわかります。

print('"abc"')

実行結果

"abc"

上記以外にも、エスケープ文字を利用することで '" などの 特殊な意味を持つ記号 を含んだ文字列をプログラムで記述することができます。エスケープ文字については必要になった時点で説明します。

"' の使い分け

文字列の中に "' が入らない場合は、文字列を囲う記号に どちらの記号を使っても構いません。自分の好きな方の記号を使うと良いでしょう。ただし、同じプログラムの中では使う記号を 統一したほうが良い でしょう。本記事では 文字列を囲う記号に " を使用 します。

ゲーム盤のデータ構造

ここまでで、〇×ゲーム盤を表すデータを どのような方法で表現する かが決まりました。その方法を 厳密に記述 すると以下のようになります。

  • 〇×ゲームのゲーム盤のマスの座標を、左右方向を x 座標、上下方向を y 座標とする 2 次元の座標 で表現する
  • x 座標はゲーム盤の左端の列を 0 とし、右に 1 つ列がずれるたびに x 座標が 1 増える
  • y 座標はゲーム盤の上端の行を 0 とし、下に 1 つ行がずれるたびに y 座標が 1 増える
  • ゲーム盤の各マスを表すデータを 2 次元配列 を表す list で表現する
  • 2 次元配列の list の 1 つ目のインデックスを x 座標、2 つ目のインデックスを y 座標に対応させる
  • 空白、〇、× が配置されたのセルを表すデータを、それぞれ " ""〇""×" という 文字列型のデータ で表現する
  • ゲーム盤の各セルを表す list の要素に、そのセルに配置されたマークに対応する文字列を代入する

上記のように、プログラムで扱うデータ を、どのようにプログラムで表現するかを 定めた形式 のことを データ構造 と呼びます。プログラムでゲーム盤のような、何らかのデータを扱う際には、そのデータを表す データ構造を決める 必要があります。

プログラムでは、同じデータを 様々なデータ構造 で表現することができます。Python に慣れ親しんでいる方や、ある程度プログラミングに習熟している方は、「マスを表すデータを文字ではなく数字で表現するべきだ」、「numpy を使ったほうが簡単に表現できる」、「ビットでゲーム盤を表現したほうが処理が高速になる」など、上記で紹介した以外のゲーム盤を表すデータ構造を思いつく人がいるかもしれません。

本記事ではそういった知識の無い 初心者 がゲーム盤を表すデータを記述する場合を想定してプログラムを実装しているので、最初はそのような ある程度の知識を必要とする ようなデータ構造をあえて採用しませんでした。

一方で、データ構造の 良し悪し は、プログラムの 記述のしやすさ や、プログラムの 実行速度 など、様々な面に 影響を及ぼす ので、扱うデータの種類に応じた 適切なデータ構造 を考えることは重要です。今回の記事で紹介する方法は、今後の〇×ゲームのプログラムを記述する際の効率の面でも、プログラムの処理速度の面でもあまり良いデータ構造であるとはいえません。

適切なデータ構造を自分で考えて選択できるようになるためには、様々なデータ構造を用いたプログラムを実際に数多く記述して 経験を積む ことが重要になります。そこで、〇×ゲームの 実装が一通り終わってから、他のデータ構造を使って〇×ゲームの改良を行うことにします。

ゲーム盤を表すデータの具体例

ゲーム盤を表すデータ構造が決まったので、そのデータ構造に従って、実際にゲーム盤のデータをプログラムで記述することにします。具体例として、下記の表のような〇×ゲームのゲーム盤を記述することにします。

0 1 2
0 ×
1 ×
2

ここまで学んだ知識を理解していれば、そのようなプログラムを記述することは難しくはないでしょう。具体的には上記の表のようなゲーム盤を表すデータは、下記のようなプログラムで記述することができます。プログラムの 6 行目と 7 行目では、確認のために "×" が代入された (1, 0) のマスを表す要素と、ゲーム盤全体のデータを表すデータを表示しています。

1  board = [
2      ["", "", " "], 
3      ["×", "×", " "], 
4      [" ", " ", "×"]
5  ]
6  print(board[1][0]) # (1, 0) のマスの内容を表示する
7  print(board)       # ゲーム盤全体の内容を表示する
行番号のないプログラム
board = [
    ["", "", " "], 
    ["×", "×", " "], 
    [" ", " ", "×"]
]
print(board[1][0]) # (1, 0) のマスの内容を表示する
print(board)       # ゲーム盤全体の内容を表示する

実行結果

×
[['〇', '×', ' '], ['×', '×', ' '], [' ', ' ', '×']]

Python の print は、上記の実行結果の 1 行目のように、文字列のデータだけ を表示する場合は 文字列を囲う記号を表示しません が、実行結果の 2 行目のように list の要素に文字列が代入されていた場合などでは、文字列を囲う記号に ' を使って文字列を表示します。

上記のプログラムを VSCode で入力すると、下図のように、× の部分が オレンジ色の枠 で囲まれて表示されます。
 

これは、全角の (丸) と 半角の小文字と大文字の oO(オー)、全角の ×(バツ) と 半角の小文字と大文字の xX(エックス)などのように、全角と半角で見た目が似ていて 区別が付けづらい ような 全角の文字 を入力した場合に表示される枠です。

他にもアルファベット、アラビア数字、空白文字、一部の記号など、全角と半角で 同じ意味を表す文字がある 場合でも、全角の文字 を入力した場合は同様にオレンジ色の枠が表示されます。全角の空白文字に関しては、後で具体例を紹介します。

なお、ひらがなや漢字のように、全角文字しか存在しない文字を入力した場合はオレンジ色の枠は表示されません。

ゲームの初期化処理

上記のプログラムでは、いくつかのマスに 〇 と × を配置した状態のゲーム盤を作成しましたが、実際には下記の仕様 2 から、ゲーム開始時 には すべてのマスを空の状態 にしておく必要があります。

2. ゲーム開始時には、ゲーム盤の全てのマスは空になっている

全てのマスが空のゲーム盤のデータは、以下のプログラムのように記述できます。

board = [
    [" ", " ", " "], 
    [" ", " ", " "], 
    [" ", " ", " "]
]
print(board)

実行結果

[[' ', ' ', ' '], [' ', ' ', ' '], [' ', ' ', ' ']]

上記のようなゲームなどの 開始時に行う処理 の事を 初期化処理 と呼びます。

初期化処理で行う処理は プログラムによって異なります。例えばオセロのプログラムの初期化処理は「ゲーム盤のマスを空にしてから白と黒の駒を所定の位置に配置する」、「黒の手番にする」の 2 つです。点数があるゲームの場合は、「点数を 0 点にする」という初期化処理があるでしょう。

ゲームのプログラムの初期化処理は、最初にゲームを始める時だけでなく、ゲームが終わったり、ゲームの途中であきらめたりして 新しいゲームを始める 際にも 必ず行う 必要があります。

Python の繰り返し処理

実は、上記のプログラムで、〇×ゲームのゲーム盤のマスを空にする処理は 完成しています。このプログラムを使って〇×ゲームの実装を進めていくこともできますが、上記のプログラムは空のゲーム盤を記述するプログラムとしては 効率的ではありません

〇×ゲームの場合はゲーム盤のマスが 9 つしかないので、上記のように空のマスを表すデータである " " を 9 回記述するようなプログラムを、多少面倒ではありますが手で記述することは充分可能です。しかし、例えば将棋のようにゲーム盤のマスが 9 × 9 = 81 マスだったり、囲碁のようにゲーム盤のマスが 19 × 19 = 381 マスだったりする場合に、上記のようなプログラムを手で全て記述するのはさすがに大変すぎるでしょう。

空のゲーム盤のデータを記述するという処理は、ゲーム盤の 全てのマスに同じデータ を代入する処理です。プログラム言語ではこのような、同じような処理繰り返して行う 処理を簡潔に記述する方法が用意されており、その方法を利用することでプログラムを 簡潔に 記述できます。

繰り返し処理はプログラムの中で頻繁に記述する 非常に重要な処理 です。ただし、Python の繰り返し処理は、他のプログラム言語しか学んだことがない人にとっては 見慣れない方法 でプログラムを記述するので最初は戸惑う人が多いかもしれません。

例えば、C 言語や JavaScript などの他のプログラム言語の繰り返し処理である for 文を学んだ方は、0 から 10 未満の整数を使って繰り返し処理を行うプログラムを以下のように記述することを想像するかもしれませんが、Python の for 文による繰り返し処理は 下記のようには記述できません

for (let a = 0 ; a < 10; a++) {
   繰り返し処理のブロックのプログラム
}

上記は、JavaScript のプログラム です。Python のプログラムではない、VSCode の JupyterLab で記述しても 実行できません

そこで、次は Python の繰り返し処理について説明することにします。

Python の for 文による繰り返し処理

Python では繰り返し処理を行うための記述方法がいくつか用意されています。今回の記事ではその中で最も良く使われる for 文 について説明します。他の Python の繰り返し処理については必要になった時点で説明します。

Python では for 文による繰り返し処理を以下のように記述します。

for 変数 in 反復可能オブジェクト:
    繰り返し処理のブロック

上記は Python の文法を説明したものなので、記述しても 実行することはできません

反復可能オブジェクト

Python の 反復可能オブジェクト (iterable)とは、for 文による繰り返し処理などで オブジェクトの中のデータを 1 つずつ順番に取り出すことができる オブジェクトの事を表します。前回の記事で紹介した list は反復可能オブジェクトの一種です。なお、オブジェクトとは何かという説明は次回以降の記事で詳しく説明する予定です。

反復可能オブジェクトは英語で iterable と表記し、日本語のカタカナで「イテラブルオブジェクト」または、単に「イテラブル」と表記されることがあります。iterable は「繰り返し」、「反復」を表す英単語で、それに「可能」という意味を表す able を語尾につけた iterable は「繰り返し(反復)可能」という意味を持ちます。

for 文で行われる処理の手順

Python では for 文を実行すると以下のような繰り返し処理が行われます。

for 変数 in 反復可能オブジェクト:
    繰り返し処理のブロック
  1. 反復可能オブジェクトの中のデータを 先頭から順番に 1 つ取り出す
    その際に、反復可能オブジェクトの中に取り出せる要素が 1 つも残っていない 場合は、繰り返し処理を終了 する
  2. 取り出したデータを for の直後に記述した 変数に代入 する
  3. 繰り返し処理のブロックに記述されているプログラムを実行し、手順 1. に戻る

具体例を挙げます。下記のプログラムは for 文の反復可能オブジェクトに [0, 1, 2] という list を記述しています。上記の手順に従ってこのプログラムで行われる処理を順番に列挙すると以下のようになります。

  1. 手順 1 で [0, 1, 2] の先頭の要素の 0 が取り出される
  2. 手順 2 で a0 が代入される
  3. 手順 3 で 繰り返し処理のブロックに記述された print(a) が実行された結果、a の値の 0 が表示され、手順 1 に戻る。
  4. 手順 1 で [0, 1, 2] の次の要素である 1 が取り出される
  5. 手順 2 で a1 が代入される
  6. 手順 3 で print(a) が実行され、a の値の 1 が表示され、手順 1 に戻る
  7. 手順 1 で [0, 1, 2] の次の要素である 2 が取り出される
  8. 手順 2 で a2 が代入される
  9. 手順 3 で print(a) が実行され、a の値の 2 が表示され、手順 1 に戻る
  10. 手順 1 で [0, 1, 2] の次の要素がもう存在しないので繰り返し処理を終了する
for i in [0, 1, 2]:
    print(i)

実行結果

0
1
2

上記のプログラムでは、反復可能オブジェクトとして記述された [0, 1, 2]3 つの要素 に対して、繰り返しのブロックの処理 が上記の 1 ~ 3、4 ~ 6、7 ~ 9 の 3 回 に渡って 繰り返して 行われます。その結果、実行結果からわかるように、上記のプログラムでは、[0, 1, 2] の要素に代入されたデータを 先頭から順に取り出して 画面に表示するという処理が行われます。

for 文で反復可能オブジェクトから取り出したデータを代入する 変数の名前 は以下のように付けるのが一般的です。

  1. 代入する データの内容 を表す 簡潔な名前 を使う
  2. ふさわしい名前が思いつかない場合は、i を使う
  3. for 文が入れ子で記述されている場合など、i が既に使われている場合は j を使う。j も使われていた場合は k のように次のアルファベットを使う
  4. 繰り返し処理の中ブロックの中でその変数を使わない場合は _ を使う

上記のプログラムでは、変数に代入する値を表すふさわしい名前が 思いつかなかった ので i にしました。

何故そのような場合に i が使われるかについては諸説ありますが、この変数を 配列のインデックス(index)を表すデータを代入する目的で使用することが多かったので、その 頭文字 が変数名に使われるようになったという説が有力なようです。他にもこの後で説明するイテレータ(iterater)や整数を表す integer の頭文字から取られたという説もあるようです。

上記の 1、3、4 に関してはそのような事例が出てきた時に説明します。

Python のブロックの記述方法

繰り返し処理などで行う処理を表す ひとまとまりのプログラム のことを ブロック と呼びます。

多くのプログラム言語では、ブロックの範囲を何らかの記号を使って記述3しますが、Python では、半角の :インデント を使ってブロックの範囲を記述するので、初めての方は戸惑うかもしれません。

Python のブロックは以下のように記述します。

  • ブロックが開始される 直前の行の最後半角の : (コロン)を記述する
  • ブロック内のプログラムは、ブロックの前後のプログラムに対して、数文字分 右にインデントをずらして 記述する。インデントとは、行内でプログラムを記述する 先頭の位置 のことを表す
  • ブロックの終了後のプログラムのインデントは、ブロックの開始前のプログラムの インデントの位置に戻す

インデントは 半角の空白文字を数文字分 を使って入力します4。ブロックを表すインデントに何文字分の空白文字を記述するかは決まっていませんが、一般的には 半角の空白 4 文字 をインデントとする場合が多いようです。

VSCode など、Python に対応した開発環境でプログラムを入力する場合は、行の最後に : を記述した後でエンターキーを押して 改行 してブロックを開始すると、自動的に半角の空白 4 文字分のインデントが行われる5ため、ブロックを表すインデントを自分で入力しなければならない場面はそれほど多くはないでしょう。

下記のプログラムは、先ほどのプログラムの繰り返し処理のブロックを 2 行分のプログラム に修正したものです。繰り返し処理のブロックの 1 行目で i * i を計算して square(2乗を表す英単語) に代入し、2 行目で isquare の値を表示しています。

for i in [0, 1, 2]:
    square = i * i
    print(i, square)

実行結果

0 0
1 1
2 4

同じブロック内では、インデントに使う半角の空白文字の 文字数を統一 する必要があります。下記のプログラムは、同じブロック内のインデントが異なっているので実行するとエラーが発生します。

for i in [0, 1, 2]:
    square = i * i
        print(i, square)

実行結果

  Cell In[8], line 3
    print(i, square)
    ^
IndentationError: unexpected indent

上記のエラーメッセージは以下のような意味を表します。

  • IndentationError
    インデントに関する(indentation)エラー
  • unexpected indent
    予期しない(unexpected)インデント(indent)が記述されている

このプログラムのエラーは、エラーメッセージの上の ^ の上の print の部分に余分なインデントが記述されていることが原因です。

ただし、同じブロック内でも以下のような場合はインデントが 揃っていなくても エラーになりません。

  • ブロックの中に別のブロックが 入れ子で 記述されている場合
  • 1 つの文を途中で改行 して複数行にまたがって記述する場合

後者の例としては、変数に list を代入する 代入文 を記述する際に、list の要素ごとにデータを改行して入力するというものがあります。前回の記事でも紹介した下記のプログラムがその実例です。なお、下記のプログラムを入力する際には、VSCode が改行時に自動的に適切なインデントをつけてくれるので、自分でインデントを調整する必要はありません

# 下記の 5 行分のプログラムは 1 つの代入文を表す
table = [
    [1, 4, 7], # ここから 3 行分のデータのインデントが
    [2, 5, 8], # 揃っていないがエラーにはならない
    [3, 6, 9]
]

Python では、全角の空白文字 をインデントなどで 使うことができない 点に注意して下さい6。下記のプログラムはインデントに全角の空白文字を使っているため実行すると エラーが発生 します。なお、Qiita では、全角の空白文字は下記のように 赤い点線 が表示されるので、半角の空白文字と見た目で区別することができるようになっています。

for i in [0, 1, 2]:
    square = i * i
  print(i, square) # この行のインデントに全角の空白文字を記述している

実行結果

  Cell In[10], line 3
    print(i, square) # この行のインデントに全角の空白文字を記述している
    ^
SyntaxError: invalid non-printable character U+3000

上記のエラーメッセージは以下のような意味を表します。

  • SyntaxError
    構文(syntax)(文法のこと)に関するエラー
  • invalid non-printable character U+3000
    プログラムで表記できない(non-printable)無効(invalid)な全角の空白文字(U+3000)が記述されている

なお、U+3000 は数字で Unicode の文字を表す際の記法で、3000 は全角の空白文字を表す Unicode の文字コード(文字を表す番号のこと)を意味します。

VSCode では、先ほど説明したように、全角の空白文字には 下図のように オレンジ色の長方形 で囲まれて表示されるため、一目で半角と全角の空白文字の 区別がつく ようになっています。なお、下図の赤い波線は、その部分に文法エラーがあることを表す VSCode の機能です。

プログラムで変数や関数などの名前に全角文字である日本語を使うのが 好まれない 原因の 1 つは、半角の空白文字を入力したつもりで、うっかり 全角の空白文字を入力 してしまうことが 原因 による エラーが発生しやすくなるから ではないかと思います。

VSCode で Python のプログラムを記述する場合は、基本的に改行を行うと VSCode が 自動的に適切なインデントをつけてくれる ようになっています。そのため、自分でインデントを調整しなければならない場面は それほど多くはありません。具体的には、主に以下のような場合に 自分でインデントを調整 する必要があります。

  • 自分で 間違って 空白文字を入力してインデントを入れたり、BackSpace キーなどを押してインデントを削除した場合
  • ブロックが 終了 した場合。自分で BackSpace キーを押してインデントを戻す必要がある
  • プログラムを記述した後で、プログラムの途中 に for 文などの ブロックを追加 した場合
  • プログラムを記述した後で、入れ子 になっているブロックのうちの 外側のブロックだけを削除 した場合
  • 他のプログラムの一部を コピー して自分のプログラムに 張り付けた場合

VSCode には、複数行のインデントをまとめて簡単に変更 する機能があります。具体的には、インデントを行いたい行を まとめて選択した後で、以下の ショートカットキー操作 を行います。似たような操作なので、コメントに関するショートカットキー操作もついでに紹介しています。

  • Ctrl + ]: 選択した行のインデントをまとめて右にずらす
  • Ctrl + [: 選択した行のインデントをまとめて左にずらす
  • Ctrl + /: 選択した行をまとめてコメントにする。既にコメントだった場合はコメントを外す

range を使った繰り返し

先程のプログラムでは、0 から 2 までの数字を for 文による繰り返しを使って記述するプログラムを紹介しました。ただし、Python では 0 から 特定の数まで 繰り返し処理を行うようなプログラムで、反復可能オブジェクトに list を記述することは 避けたほうが良いでしょう。その理由の 1 つは、例えば 0 から 99 まで 100 回繰り返しを行うためには、要素が 100 もある list のデータを記述する必要があり大変すぎるからです。他の理由については後述の「range と list の違い」で説明します。

0 から 特定の数までの繰り返し処理を記述する場合は、Python では range という関数を利用します。range はイテレータ(後述します)という 反復可能オブジェクト を返す関数で、for 文の中で記述すると繰り返しのたびに、0 から 引数に指定した 数字未満整数 を順番に取り出します。先ほどのプログラムは、range を使うことで下記のように記述することができます。

for i in range(3):
    print(i)

実行結果

0
1
2

range を利用する際は、以下の点に注意して下さい。

  • for 文で記述した際に取り出す最初の整数は、1 からではなく、0 から始まる
  • for 文で記述した際に取り出す最後の整数は、() の中に記述した数字ではなく、数字 - 1 である

なお、取り出す整数の数は、() の中に記述した 数字と同じ です。例えば、for 文に range(10) を記述すると、0 ~ 9 までの 10 個 の整数が順番に取り出されます。

range を使えば 下記のプログラムのように、0 から 99 までの数字を表示するような繰り返し処理を、range(100) を使って下記のプログラムのように 簡潔に記述 することができます。なお、実行結果は長くなるので省略します。

for i in range(100):
    print(i)

VSCode では、上記のプログラムのように 大量の表示を行う プログラムを実行した場合、表示内容が 一部省略 される場合があります。下図は上記のプログラムを実行した際に表示される内容のうちの 最後の一部分 を表しています。
 

図の上部に表示される ... は、表示が一部省略されていることを表します。また、図の下部に表示されているうちの、リンクになっている部分をクリックすることで以下のような操作を行うことができます。

  • scrollable element:クリックすると、表示結果が VSCode の内部にウィンドウのような形で表示され、スクロールして全ての表示結果を見ることができるようになる
  • text editor:表示結果を VSCode の別のタブで開いて全ての表示結果を見ることができるようにする。別のタブは JupyterLab ではなく、テキストエディタ(文字だけを編集するソフトのこと)で開かれる
  • settings:VSCode の設定画面が開かれる。Notebook › Output: Text Line Limit の設定項目を設定することで、表示結果を省略せずに表示する行数を設定できる

なお、git にアップロードした marubatsu.ipynb では、上記のプログラムの実行結果として 0 から 99 までの全ての数字が表示されています。上記のような一部が省略される実行結果を確認したい人は、VSCode でこのプログラムを 実行 して下さい。

range は、以下のように引数を 2 つまたは、3 つ記述することで、より柔軟な繰り返し処理を記述することができます。

  • range(start, stop):start から stop 未満 までの整数を順番に取り出す
  • range(start, stop, step):start から stop 未満 までの整数を step 刻みで順番に取り出す

例えば、2 から 8 未満の偶数を表示するプログラムは以下のように記述できます。

for i in range(2, 8, 2):
    print(i)

実行結果

2
4
6

range整数しか扱えない 点に注意が必要です。整数以外の range のようなイテレータは、numpy というモジュールの numpy.arange という関数を使うことで利用することができます。numpy.arange の具体例については必要になった時に紹介します。

反復可能オブジェクトとイテレータ

先程、rangeイテレータ7 という反復可能オブジェクトを返す関数だと説明しました。この説明からわかるように、イテレータは 反復可能オブジェクトの一種 です。

イテレータと反復可能オブジェクトは、用語が異なることからわかるように、異なる意味を持つ 用語です。ただし、この違いを詳しく説明すると非常に長くなってしまいますし、これらを for 文でしか使わない のであれば、この 2 つの違いを意識する必要はあまりありません。

そこで、イテレータと反復可能オブジェクトの違いについては、その違いを意識してプログラムを記述する必要がでてきた時点で詳しく説明することにします。

現時点では、イテレータを、for 文の反復可能オブジェクトとして 利用できる ということを理解しておけばよいでしょう。

他の反復可能オブジェクト

Python には list 以外にも反復可能オブジェクトに 分類 されるデータ型があります。そのうちの 1 つが 文字列型 のデータで、下記のプログラムのように記述することで、for 文を使って文字列の中の文字を先頭から順に取り出して表示することができます。

他の Python で良く使われる反復可能オブジェクトには dict があります。dict については必要になった時に紹介します。

for char in "abc":
    print(char)

実行結果

a
b
c

上記のプログラムでは、for 文で反復可能オブジェクトである "abc" から取り出すデータは 1 文字分のデータ です。そこで、そのデータを代入する変数には、文字を表す英単語である character の略から char という名前をつけました。

文字列型のデータは、先頭の文字から順番に 0 から始まるインデックスが付けられた list のような 使い方をすることができます。例えば、下記のプログラムは、"abc" の 1 番(先頭から 2 番目)の文字を画面に表示します。

text = "abc"
print(text[1])

実行結果

b

なお、上記のように、文字列型のデータと list は 類似する性質 を持ちますが、異なるデータ型 なので上記の例を見て 同じものだと混同しない ようにして下さい。文字列型のデータは文字以外のものを扱うことはできませんし、list のように 要素に文字を代入することはできません。そのため、下記のようなプログラムを記述して実行すると、2 行目でエラーが発生します。

char = "abc"
char[1] = "e"

実行結果

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[16], line 2
      1 char = "abc"
----> 2 char[1] = "e"

TypeError: 'str' object does not support item assignment

上記のエラーメッセージは以下のような意味を表します。

  • TypeError
    データ型(type)に関するエラー
  • 'str' object does not support item assignment
    文字列型(str)のオブジェクト(データのこと)(object)は、要素(item)に割り当て(代入のこと)(assignment)を行う(support)ことはできない(does not)

list と range の違い

for a in [0, 1, 2]:for a in range(3): によって 同じ繰り返し処理 が行われるので、list と range は同一のデータを表すように 見えるかもしれません が、この 2 つは 全く異なるデータ です。

例えば、0 から 99 までの 100 個の要素を持つ list には、実際に整数が代入された 100 個の要素 が格納されています。一方、range(10000) のように記述しても 要素が 10000 個ある list のようなデータが作られる わけではありません

現実世界で 0 から 9999 までの数字を紙に書いて数える際に、10000 個 の数字を 並べて書いて 数えるという方法があります。反復可能オブクトに list を記述した for 文は、そのような方法で数を数えながら繰り返し処理を行っています。

一方、数字を数える際に、数字を増やすたびに紙に書かれた数字を 書き替える ことで、紙に数字を 1 つだけ 書いて 0 から 9999 まで数えることができます。反復可能オブクトに range を記述した for 文はそのような方法で数を数えながら繰り返し処理を行っています。

このことから、list を使って 数を規則正しく数えるような繰り返し処理 を行うと、range を使った場合と比較してかなり効率の悪い処理が行われることになります。具体的には list を使ってそのような繰り返し処理を行うと、繰り返しの回数に比例 したコンピュータの メモリ が必要になりますが、range を使った場合はメモリを ほとんど必要とせず に繰り返し処理を行うことができます。

上記のような性質から、0 から 99 までのような、数を規則正しく数えるような繰り返し処理 を行う際は、反復可能オブジェクトには list ではなく、range を使うべき だと覚えておいてください。

for 文に list を 常に使うべきではないと誤解する 人がいるかもしれないので補足します。例えば、list の中にクラスの生徒の点数を格納し、その中のデータを 順番に取り出して 計算を行うような場合は、for 文で list を使うことに 問題は全くありません

本記事で入力したプログラム

以下のリンクから、本記事で入力して実行した JupyterLab のファイルを見ることができます。

次回の記事

  1. コンピュータは、空白も文字の一種として扱います

  2. プログラム言語では、文字の長さに関わらず、文字を表すデータを 文字列 型のデータと呼びます。例えば a のように 1 文字しかないデータも、文字が存在しない 0 文字のデータも文字列型のデータです。なお、0 文字の文字列型のデータのことを 空文字 と呼び、一般的なプログラム言語では "" のように記述します

  3. C 言語や JavaScript など、多くのプログラム言語ではブロックの範囲を {} で囲って記述します。他の方法でブロックを記述するプログラム言語もあります

  4. Python ではインデントに Tab キーで入力するタブ文字を使うこともできますが、あまり 好まれてい ないようです。そのせいか、VSCode では Tab キーを押してインデントを入力した場合、タブ文字ではなく、半角の空白文字が 4 文字分入力されるようになっています

  5. VSCode で自動的に入力されるインデントの文字数は、VSCode の設定で変更することができます

  6. JavaScript など、一部のプログラム言語では半角の空白文字を、全角の空白文字の代わりに使ってもエラーにならないものもありますが、一般的にはエラーになる場合のほうが多いと思います

  7. イテレータの英語は「反復子」を表す iterator です。「子」は何かを行うために利用できるもののことを表すので、イテレータは 反復を行うために利用できるもの という意味を表します。「子」の他の例としては識別するために利用できるものという意味を表す「識別子」があります。

1
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0