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

# 目次と前回の記事

https://qiita.com/ysgeso/items/2381dd4e3283cbed49a0

https://qiita.com/ysgeso/items/9efd60c7a28381e6cbf7

# 前回のおさらい

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

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

# ゲーム盤のデータの表現

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

```python
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 つが文字列を囲う記号のペアとして認識されるので、実行すると以下のようにエラーになります。

```python
print(""abc"")
```
実行結果
```
  Cell In[2], line 1
    print(""abc"")
            ^
SyntaxError: invalid syntax
```

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

* __SyntaxError__
構文（文法のこと）（syntax）に関するエラー
* __invalid syntax__
無効な（invalid）構文（syntax）でプログラムが記述されている。

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

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

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

```python
print(""abc"")
```

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

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

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

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

```python
print('"abc"')
```
実行結果
```
"abc"
```

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

### `"` と `'` の使い分け

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



## ゲーム盤のデータ構造

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

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

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

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

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

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

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

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

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

|  | 0 | 1 | 2 |
|:-:|:-:|:-:|:-:|
| __0__  | 〇  | ×  |   |
| __1__  | 〇  | ×  |   |
| __2__  |   |   | 〇  |

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


```python
1  board = [
2      ["〇", "〇", " "], 
3      ["×", "×", " "], 
4      [" ", " ", "×"]
5  ]
6  print(board[1][0]) # (1, 0) のマスの内容を表示する
7  print(board)       # ゲーム盤全体の内容を表示する
```

<details><summary><strong>行番号のないプログラム</strong></summary>

```diff_python
board = [
    ["〇", "〇", " "], 
    ["×", "×", " "], 
    [" ", " ", "×"]
]
print(board[1][0]) # (1, 0) のマスの内容を表示する
print(board)       # ゲーム盤全体の内容を表示する
```
</details>

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

:::note
上記のプログラムを __VSCode__ で入力すると、下図のように、`〇` と `×` の部分が __オレンジ色の枠__ で囲まれて表示されます。
　
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/5777865b-81e4-2a53-d6af-3deb7c358dd1.png" width="200">

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

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

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

## ゲームの初期化処理

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

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

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

```python
board = [
    [" ", " ", " "], 
    [" ", " ", " "], 
    [" ", " ", " "]
]
print(board)
```
実行結果
```
[[' ', ' ', ' '], [' ', ' ', ' '], [' ', ' ', ' ']]
```


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

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

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

# Python の繰り返し処理

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

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

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

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

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

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

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

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

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

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

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

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

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


### 反復可能オブジェクト

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

https://docs.python.org/ja/3/glossary.html#term-iterable

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

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

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

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

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

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

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

```python
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]` の要素に代入されたデータを __先頭から順に取り出して__ 画面に表示するという処理が行われます。

:::note
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 文字__ をインデントとする場合が多いようです。

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

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

```python
for i in [0, 1, 2]:
    square = i * i
    print(i, square)
```
実行結果
```
0 0
1 1
2 4
```
同じブロック内では、インデントに使う半角の空白文字の __文字数を統一__ する必要があります。下記のプログラムは、同じブロック内のインデントが異なっているので実行するとエラーが発生します。

```python
for i in [0, 1, 2]:
    square = i * i
        print(i, square)
```
実行結果
```
  Cell In[8], line 3
    print(i, square)
    ^
IndentationError: unexpected indent
```
:::note
上記のエラーメッセージは以下のような意味を表します。

* __IndentationError__
インデントに関する（indentation）エラー
* __unexpected indent__
予期しない（unexpected）インデント（indent）が記述されている

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

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

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

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

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

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

```python
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
```

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

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

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

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

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/78fef95a-a34e-0c0e-2b11-3665e83f3af5.png" width="200">

:::note
プログラムで変数や関数などの名前に全角文字である日本語を使うのが __好まれない__ 原因の 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` を使うことで下記のように記述することができます。

```python
for i in range(3):
    print(i)
```
実行結果
```
0
1
2
```

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

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

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

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

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

:::note
__VSCode__ では、上記のプログラムのように __大量の表示を行う__ プログラムを実行した場合、表示内容が __一部省略__ される場合があります。下図は上記のプログラムを実行した際に表示される内容のうちの __最後の一部分__ を表しています。 
　
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/aa77e9cc-c348-091c-b6f3-8f981a8e58f2.png" width="500">

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

* __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 未満の偶数を表示するプログラムは以下のように記述できます。

```python
for i in range(2, 8, 2):
    print(i)
```
実行結果
```
2
4
6
```

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

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

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

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

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

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

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

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

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

```python
for char in "abc":
    print(char)
```
実行結果
```
a
b
c
```
:::note
上記のプログラムでは、for 文で反復可能オブジェクトである `"abc"` から取り出すデータは __1 文字分のデータ__ です。そこで、そのデータを代入する変数には、文字を表す英単語である `character` の略から __`char`__ という名前をつけました。
:::

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

```python
text = "abc"
print(text[1])
```
実行結果
```
b
```

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

```python
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 を使った場合はメモリを __ほとんど必要とせず__ に繰り返し処理を行うことができます。

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

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

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

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

https://github.com/ysgeso/marubatsu/blob/master/005/marubatsu.ipynb

# 次回の記事

https://qiita.com/ysgeso/items/2a436b3819bef9a6a2b4

[^1]:コンピュータは、空白も文字の一種として扱います
[^2]:プログラム言語では、__文字の長さに関わらず__、文字を表すデータを __文字列__ 型のデータと呼びます。例えば a のように 1 文字しかないデータも、文字が存在しない 0 文字のデータも文字列型のデータです。なお、0 文字の文字列型のデータのことを __空文字__ と呼び、一般的なプログラム言語では `""` のように記述します
[^3]:C 言語や JavaScript など、多くのプログラム言語ではブロックの範囲を `{` と `}` で囲って記述します。他の方法でブロックを記述するプログラム言語もあります
[^4]:Python ではインデントに Tab キーで入力するタブ文字を使うこともできますが、あまり __好まれてい__ ないようです。そのせいか、VSCode では Tab キーを押してインデントを入力した場合、タブ文字ではなく、半角の空白文字が 4 文字分入力されるようになっています
[^5]:VSCode で自動的に入力されるインデントの文字数は、VSCode の設定で変更することができます
[^6]:JavaScript など、一部のプログラム言語では半角の空白文字を、全角の空白文字の代わりに使ってもエラーにならないものもありますが、一般的にはエラーになる場合のほうが多いと思います
[^7]:イテレータの英語は「反復子」を表す iterator です。「子」は何かを行うために利用できるもののことを表すので、イテレータは __反復を行うために利用できるもの__ という意味を表します。「子」の他の例としては識別するために利用できるものという意味を表す「識別子」があります。