Pythonで〇×ゲームのAIを一から作成する　その13　関数の定義と関数呼び出し

# 目次と前回の記事

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

https://qiita.com/ysgeso/items/d691eb18d4258953a82a

# 実装の進捗状況と前回までのおさらい

## 〇×ゲームの仕様と進捗状況

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

仕様の進捗状況は、以下のように表記します。

* 実装が完了した部分を `背景が灰色の長方形` で記述する
* 実装の一部が完了した部分を、__太字__ で記述する


## 前回までのおさらい

前回の記事では、仕様の 3、4 を実装するために、「ゲーム盤の (0, 1) のマスが空いている場合に 〇 のマークを配置する」という処理を行う、下記のプログラムを記述しました。

```python
board = [[" "] * 3 for x in range(3)]
if board[0][1] == " ":
    board[0][1] = "〇"
else:
    print("(0, 1) のマスにはマークが配置済です")
print(board)
if board[0][1] == " ":
    board[0][1] = "×"
else:
    print("(0, 1) のマスにはマークが配置済です")
print(board)
```
実行結果
```
[[' ', '〇', ' '], [' ', ' ', ' '], [' ', ' ', ' ']]
(0, 1) のマスにはマークが配置済です
[[' ', '〇', ' '], [' ', ' ', ' '], [' ', ' ', ' ']]
```

他のマスや、× のマークに対しても __同様の処理__ を記述すれば良いのですが、その度に上記のプログラムの 2 ~ 5 行目のような 4 行分のプログラムを記述するのは __大変__ です。このような場合は、__関数__ を使って、指定したマスにマークを配置する処理を __まとめる__ ことで、プログラムを簡潔に記述することができます。

# Python の関数

## 関数とは何か

プログラム言語における関数は、__入力__ されたデータに対して、何らかの __処理__ を行い、処理の結果を __出力__ するような機能を持ちます。このように、関数は、__入力__、__処理__、__出力__ の __3 つ__ で構成されます。

例えば、過去の記事で Python のオブジェクトを説明する際に使用した Python の `id` という関数は、以下の表のような、入力、処理、出力を持ちます。

<table>
  <tr>
    <td><strong>入力</strong></td>
    <td>データ</td>
  </tr>
  <tr>
    <td><strong>処理</strong></td>
    <td>入力されたデータを管理するオブジェクトを探す</td>
  </tr>
  <tr>
    <td><strong>出力</strong></td>
    <td>入力されたデータを管理するオブジェクトの id</td>
  </tr>
</table>

__他人が作成した関数を利用__ する際には、その関数の「入力」、「処理」、「出力」のそれぞれについて学ぶ必要がありますが、関数の処理が __具体的にどのような手順で行われるか__ について __知る必要はありません__ 。関数のように、中で行われている処理が外から見えないようなもののことを、黒い箱に例えて __ブラックボックス__ と呼びます。

また、関数には、関数が __定義された後__ は、プログラムの __好きな場所__ から __何度でも利用__ することができるという性質があります。

:::note
現実の世界でも、関数のような __性質を持つ__ ものは __身の回りにあふれています__。例えば、自動販売機は、「お金」と「商品を選ぶ」という __入力__ を行うと、自動販売機の中で何らかの __処理__ が行われ、「商品」と「おつり」という __出力__ が行われます。その際に、自動販売機の中で行われる処理が具体的にどのような手順で行われるかについて、__知る必要はありません__ 。

他の例としては、車は、「ハンドル」、「アクセル」、「ブレーキ」という __入力__ を行うと、車の中で何らかの __処理__ が行われ、車を運転するという __出力__ が得られます。その際に、車の運転の仕方さえ知っていれば、車の中でどのような手順で処理が行われているかについて知る必要はありません。

そう考えると、身の回りに関数のようなものがあふれていることが実感できるのではないでしょうか？
:::

## 関数の定義

Python では関数を以下のように記述して __定義__[^1]します。

```Python
def 関数名(仮引数):
    関数の処理を記述するブロック
```

__関数名__ には、変数名と同様のルール[^2]で自由に名前を付けることができます。

__仮引数__ は、関数に __入力されたデータ__ を __代入する変数__ のことで、`()` の中に、必要な数だけ __半角の `,` で区切って__ 記述します。なお、入力が __一つもない__ 関数の定義の場合でも、関数名の後に __必ず `()` を記述__ する __必要__ がある点に注意して下さい。

関数の __ブロックの記述方法__ は、「その 5」の記事で説明した for 文のブロックと同様です。

__関数の定義__ は __文の一種__ なので、代入文や、for 文などと同様に、__実行されます__。ただし、__関数の定義が実行__ された際に、関数の __ブロック__ に記述されたプログラムは __実行されない__ 点に注意して下さい。関数の __ブロック__ に記述されたプログラムは、後述の __関数呼び出し__ という処理を行うことで、 __実行されます__。

上記のことから、__関数の定義が実行__ された際に __何の処理も行われない__ ように __見えるかもしれません__ が、実際には、後述の __関数呼び出し__ を行うことが __出来るようにする__ という処理が行われます。

__関数の定義の実行__ は、処理の手順を書いたマニュアルを作成することに似ています。マニュアルを __作成しただけ__ では __何の作業も行われません__。作成したマニュアルを誰かに渡して、「マニュアルの通りに作業を行ってくれ」という __指示を行う__ ことで、マニュアルの中に記述された __作業が行われます__。このような指示を行うことが、後述の __関数呼び出し__ に相当します。

なお、Python では関数の定義時の __仮引数の記述方法__ にはいくつかの __バリエーション__ があります。今回の記事ではその中の __基本的な記述方法__ について説明します。他の仮引数の記述方法については、必要になった時点で紹介します。

:::note
既に __値が代入された変数名__ や、既に __定義された関数名__ と __同じ名前__ の関数を定義すると、その名前は新しく定義された関数で __上書きされる__ ことになります。

下記のプログラムでは 1 行目で、`a` に `1` を代入しているので、2 行目を実行すると `1` が表示されます。

その直後の 4、5 行目で、__同じ `a` という名前__ を使って __関数を定義__ しているので、`a` は `1` ではなく、__新しく定義した関数を表す__ ようになります。そのため、7 行目を実行すると、`print` で関数を表示した時に表示される、 `<function 変数名 at ・・・>` のような表示[^3]が行われます。

```python
1  a = 1
2  print(a)
3
4  def a():
5     print(1)  # この処理に特に意味はありません
6
7  print(a)
```

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

```diff_python
a = 1
print(a)

def a():
   print(1)  # この処理に特に意味はありません

print(a)
```
</details>

実行結果
```
1
<function a at 0x00000269D7701DA0>
```

このように、関数を定義する際には、他の変数名や、関数名と __名前がかぶらない__ ように __気を付ける必要__ があります。なお、このようなことが起きる理由については、今後の記事で説明します。
:::

## 関数呼び出し

__関数の定義を実行した後__ のプログラムでは、__関数呼び出し__ を記述して、その __関数の定義のブロック__ に記述されたプログラムを __実行する__ ことができます。Python では、関数呼び出しは、下記のように記述します。

```python
関数名(実引数)
```

__実引数__ とは、関数に __入力するデータ__ のことで、関数名の直後の `()` の中に、実引数を表す   __式__ を必要な数だけ __半角の `,` で区切って__ 記述します。関数に入力するデータが __存在しない__ 場合でも、関数名の後に __必ず `()` を記述__ する __必要__ がある[^4]点に注意して下さい。

関数呼び出しが記述された文が実行されると、以下のような手順で処理が行われます。

1. __実引数__ に記述された __式の計算結果__ が、__記述された順番通り__ に、呼び出した関数の定義で記述した __仮引数に代入__ する
2. 呼び出した関数の定義の __ブロック__ に記述されたプログラムを __実行する__
3. 関数の定義のブロックに記述された __すべてのプログラムを実行する__ か、後述の __return 文 が記述された文__ が実行された時点で、関数呼び出しの処理が __終了__ する
4. 関数呼び出しが __return 文で終了__ した場合は、return 文に記述した __式の計算結果__ が、関数が __出力するデータ__ となる。__return 文で終了しなかった場合__ は、__データが存在しない__ という意味を表す __None__ が関数が __出力するデータ__ となる。この関数が __出力するデータ__ のことを関数の __返り値__[^5] と呼ぶ
5. 文の中に記述した __関数呼び出しの部分__ が、関数の __返り値に置き換わって__ 処理が続けられる

:::note warn
__None__ は __データが存在しない__ ことを表す、__None 型__ のデータです。None 型のデータは、__None だけ__ です。None は __偽__ を表す論理型の False とは __異なる用途__ で使われます。
:::

return 文は、__関数呼び出しの処理を終了__ し、__式の計算結果__ を __関数の返り値__ として返す処理を行う文で、下記のように記述します。関数の __ブロックの途中__ に記述された return 文が実行された場合は、__残りのブロック__ の処理は __実行されない__ 点に注意が必要です。

```python
return 式
```

なお、Python では関数呼び出し時の __実引数の記述方法__ には、仮引数の記述方法と同様にいくつかの __バリエーション__ があります。今回の記事ではその中の __基本的な記述方法__ について説明します。他の実引数の記述方法については、必要になった時点で紹介します。

## 関数の定義と関数呼び出しの具体例

関数の定義と関数呼び出しについて、具体例を挙げて説明します。

下記のプログラムの 1、2 行目では、__入力__ として 2 つのデータを __仮引数 `a` と `b`__ で受け取り、その __合計を計算する__ という __処理__ を行い、__合計__ を返り値として __出力__ するという機能を持つ __`add`__ という名前の関数を定義しています。

4 行目では、この関数の実引数に `1` と `2` という __式__ [^6]を記述して関数呼び出しを行っています。

```python
def add(a, b):
    return a + b

print(add(1, 2))
```
実行結果
```
3
```

上記のプログラムを実行した際に行われる処理を以下の表に示します。

| 行数 | 行われる処理 |
|:--|:--|
| 1、2  | __関数の定義が実行__ され、以後の処理でこの __関数を呼び出すことが出来る__ ようになります  |
| 4 | `print` の `()` の中に記述された、`add(1, 2)` という関数呼び出しの処理が開始されます|
| 1  | `add(1, 2)` に記述された 2 つの __実引数__ である `1` と `2` が、`add` の関数の定義の __仮引数__ に記述された `a` と `b` に、その __順番通りに代入__ されます。具体的には、__`a = 1`__、__`b = 2`__ という __代入処理__ が行われます|
| 2  | return 文が記述されているので、`return` の後に記述されている `a + b` が計算され、その __計算結果__ の `3` が関数の __返り値__ となり、関数呼び出しの __処理が終了__ します  |
| 4  | `add(1, 2)` が、関数の __返り値__ の `3` に __置き換わり__ ます。結果として、`print(3)` が実行され、`3` が表示されます |

:::note
上記の「関数の定義が実行され、以後の処理でこの関数を呼び出すことが出来るようになります」という説明は、__どの関数の定義でも同じ__ なので、以後はこの説明は __省略__ します。

また、上記の __4 行目__ と __その次の 1 行目__ の、__関数呼び出し__ が行われた __直後__ の説明はかなり冗長なので、以後は「`add` が呼び出され、仮引数に対して `a = 1` と `b = 2` という代入処理が行われます」のように表記します。
:::

## 実引数と仮引数

### 実引数と仮引数の意味の違い

実引数と仮引数を区別せずに、単に「引数」と記述することがありますが、この 2 つの用語はそれぞれ __異なる意味__ を持つので、本記事では __区別して表記__ します。実際に、英語では __実引数__ のことを __argument__、__仮引数__ の事を __parameter__ のように __異なる用語__ で表記します。

先ほど説明したように、__実引数__ は、関数に __入力するデータ__ のことで、__式__ の形で記述します。一方、__仮引数__ は関数呼び出しで関数に渡された __入力データ__ を __代入する変数__ のことです。どちらも用語に「引数」が入っているので同じようなものを表す用語だと __勘違いされがち__ ですが、__実引数__ が __データ__ を表すのに対し、__仮引数__ はデータを代入する __入れ物（変数）__ を表します[^7]。

このように、実引数と仮引数は、__性質が全く異なる__ ものを表す点に注意して下さい。

:::note warn
実引数は関数に __入力するデータ__ を表す __式__、仮引数はそのデータを代入する __変数__ のことである。
:::



### 実引数と仮引数の間で行われる処理

関数呼び出しが行われた際には、一つ一つの __実引数__ と __仮引数__ の間で、__先頭から記述された順__ に、下記のような __代入処理__ が行われます。

```python
仮引数 = 実引数
```

このことと、「その 8」の記事以降で説明した __Python で行われる代入処理__ について __正しく理解__ できていれば、関数呼び出しを説明する際に、__誤解されがちな例__ としてよく取り上げられる下記の 2 つの例で行われる処理を __正しく理解__ できるはずです。

### 勘違いされがちな関数呼び出しの処理 その 1 

下記のプログラムを実行すると、以下のような処理が行われると __勘違い__ して、`1` が表示されると __勘違いする__ 人がいるかもしれませんが、__実際には `0`__ が表示されます。

:::note alert
下記は、__間違った__ 処理の手順です（間違っているので背景が赤いノートにしています）。

1. 5 行目で `assign_one` の実引数に `a` を記述して呼び出しているので、`assign_one` の仮引数の `x` は `a` と同じデータを共有するようになる
2. 従って、`x = 1` を実行すると、`a` の値も `1` になる （これが __間違い__ で、__共有が解除される__ ので `a` の値は __変化しません__）
:::

```python
1  def assign_one(x):
2      x = 1
3
4  a = 0
5  assign_one(a)
6  print(a)
```

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

```diff_python
def assign_one(x):
    x = 1

a = 0
assign_one(a)
print(a)
```
</details>

実行結果
```
0
```

:::note
1、2 行目で定義されている関数は、仮引数に `1` (__one__) を代入 (__assign__) するという処理を行うので、`assign_one` という名前を付けました。このように、変数や関数に __複数の英単語を組み合わせた名前__ を付ける際に、単語と単語の間を __半角の `_`__ （アンダーバー）で __区切って__ 名前を付けるという __記法__ があります。

他にも、`assignOne` のように、__先頭以外__ の単語の __頭文字を大文字__ で表記するという記法があります。この方法は名前の大文字の部分がラクダのこぶのようにみえることから、__キャメルケース__（camel case）と呼ばれます。なお、先頭を大文字にしないのは、先頭を大文字にした名前が __別の用途で使われる__ からです。別の用途については必要になった時点で説明します。

どちらの記法を使っても構いませんが、__混乱を避ける__ ため、一つのプログラム内では __共通の記法__ を使うべきです。本記事では前者の __`_` を使う記法__ を使うことにします。
:::

実際には、以下の表のような処理が行われるので、上記のプログラムで `assign_one(a)` を呼び出しても、__`a` の値は変化しません__。

| 行数 | 行われる処理 |
|:--|:--|
| 4| `a` に `0` が代入されます  |
| 5、1 | `assign_one` が呼び出され、仮引数に対して __`x = a` という代入処理__ が行われます。この処理によって、`x` と `a` が __同じデータを共有__ するようになります |
| 2  | `x = 1` という代入処理が行われます。`x` に値が代入されたことによって、`x` と `a` のデータの __共有が解除__ されます。その際に __`a` の値は変化しません__。return 文を実行せずに、関数のブロックの処理が終了したので、関数の返り値は None になります  |
| 5  | `assign_one(a)` が、関数の返り値の `None` に置き換わりますが、この行では __代入処理は記述されていない__ ので、置き換わった後で __何の処理も行われません__
| 6 | `a` の `0` のまま値は変化していないので、`0` が表示されます

まだピンとこない人がいるかもしれないので、上記のプログラムを、関数を使わない __同様の処理__ を行うプログラムに修正した下記のプログラムを見て下さい。`x = a` で `x` に `a` を代入した後で、`x` に `1` を代入しても、`a` の値は変更されません。

下記のプログラムは「その 7」の記事で紹介した __プログラム A__ と同様の処理を行っています。下記のプログラムを見てもピンとこない人は、「その 7」から復習して下さい。

```python
a = 0
x = a
x = 1
print(a)
```
実行結果
```
0
```

:::note
上記の修正前と修正後のプログラムは、__厳密には__ 同じ処理を行っている __わけではありません__ が、修正前のプログラムで行われている処理を __理解する__ という意味では __同じ処理__ を行っていると __考えても構いません__。

なお、修正前と修正後のプログラムの違いは、それぞれのプログラムの後で、__`x` に値が代入されているかどうか__ という点にあります。例えば、それぞれのプログラムの直後に `print(x)` を記述して実行すると、__修正前のプログラムはエラー__ が発生しますが、__修正後のプログラムは `1` が表示__ されます。この違いを理解するためには、__名前空間__ という概念を理解する必要がありますが、今回の記事の内容を理解するだけであれば、名前空間を理解する必要はありません。

名前空間は重要な概念なので、次回の記事で詳しく説明します。
:::

### 勘違いされがちな関数呼び出しの処理 その 2

下記のプログラムを実行すると、以下のような処理が行われると __勘違い__ して、`[0, 1, 2]` が表示されると考える人がいるかもしれませんが、__実際には `[1, 1, 2]`__ が表示されます。

:::note alert
下記は、__間違った__ 処理の手順です。

1. 5 行目で `assign_one` の実引数に `a` を記述して呼び出しているので、`assign_one` の仮引数の `x` には `a` の値が複製[^8]される（これが __間違い__ で、複製ではなく __共有__ されます）
2. 従って、`x` と `a` の値は同じ内容を持つ異なる list なので、`x[0] = 1` を実行しても、`a` の値は変化しない
:::

```python
1  def assign_one(x):
2      x[0] = 1
3
4  a = [0, 1, 2]
5  assign_one(a)
6  print(a)
```

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

```diff_python
def assign_one(x):
    x[0] = 1

a = [0, 1, 2]
assign_one(a)
print(a)
```
</details>

実行結果
```
[1, 1, 2]
```

実際には、以下の表のような処理が行われるので、上記のプログラムで `assign_one(a)` を呼び出すと、`a` の値は変化ます。

| 行数 | 行われる処理 |
|:--|:--|
| 4   | `a` に `[0, 1, 2]` が代入されます  |
| 5、1 | `assign_one` が呼び出され、仮引数に対して `x = a` という代入処理が行われます。その結果、`x` と `a` は __同じデータを共有__ することになります |
| 2  | `x[0] = 1` という代入処理が行われます。この処理では __`x` に値を代入していない__ ので、`x` と `a` のデータの共有は __解除されません__。return 文を実行せずに、関数のブロックの処理が終了したので、関数の返り値は None になります  |
| 5  | `assign_one(a)` が、関数の返り値の `None` に置き換わりますが、この行では代入処理は記述されていないので、置き換わった後で何の処理も行われません
| 6 | `a` と `x` は __同じデータを共有__ しているので、2 行目の `x[0] = 1` の処理によって `a` の値も変化します |

まだピンとこない人がいるかもしれないので、上記のプログラムを、関数を使わない __同様の処理__[^9] を行うプログラムに修正した下記のプログラムを見て下さい。下記のプログラムは「その 7」の記事で紹介した __プログラム B__ と同様の処理を行っていますので、下記のプログラムを見てもピンとこない人は、「その 7」から復習して下さい。

```python
a = [0, 1, 2]
x = a
x[0] = 1
print(a)
```
実行結果
```
[1, 1, 2]
```

## 関数呼び出しの代入処理と返り値

関数呼び出しを行うと、その部分が __関数の返り値に置き換わって__ 処理が行われるという説明をしました。その際に、返り値が return 文の 式の計算結果を複製したものであると __勘違い__ しないように __注意して下さい__。返り値と return 文の式の計算結果は、__同じデータを共有__ しています。より具体的には、返り値を表すデータを管理するオブジェクトは、return 文の式の計算結果を管理するオブジェクトと __同じ__ です。

```python
def 関数名(仮引数):
   関数のブロックのプログラム
   return 式

a = 関数名(実引数)
```

上記のような、変数に __関数呼び出しを直接代入__ するプログラムは、下記のプログラムと __同様の処理__[^9] が行われます。

```python
仮引数 = 実引数
関数のブロックのプログラム
a = 式
```

このことと、「その 8」の記事以降で説明した __Python で行われる代入処理__ について __正しく理解__ できていれば、「変数に関数呼び出しを直接代入する」処理を説明する際に、__誤解されがちな例__ としてよく取り上げられる下記の例で行われる処理を __正しく理解__ できるはずです。

### 勘違いされがちな関数呼び出しの代入処理

下記のプログラムの 1、2 行目で定義した `psuedo_copy` という名前の関数は、__仮引数__ に渡されたデータを __複製したデータ__ を返り値として返すという __意図__ で作られた関数です。ただし、実際にはこの関数で __データの複製は行われません__ ので、この関数の名前は「__偽の__」という意味の英単語である __psuedo__ と複製を表す copy を __組み合わせて__ `psuedo_copy` という名前にしました。

下記のプログラムを実行すると、以下のような処理が行われると __勘違い__ して、`[1, 2, 3]` が表示されると考える人がいるかもしれませんが、__実際には `[5, 2, 3]`__ が表示されます。

:::note alert
下記は、__間違った__ 処理の手順です

1. `psuedo_copy` の返り値は、仮引数に代入された `[1, 2, 3]` を複製したものである（これが __間違い__ で、複製ではなく __共有__ されます）
2. 従って、`b = psuedo_copy(a)` によって `b` には `a` を複製したデータが代入される
:::

```python
1  def psuedo_copy(x):
2      return x
3
4  a = [1, 2, 3]
5  b = psuedo_copy(a)
6  a[0] = 5
7  print(b)
```

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

```diff_python
def psuedo_copy(x):
    return x

a = [1, 2, 3]
b = psuedo_copy(a)
a[0] = 5
print(b)
```
</details>

実行結果
```
[5, 2, 3]
```

実際には、以下の表のような処理が行われるので、上記のプログラムで `assign_one(a)` を呼び出した後で、`a[0] = 5` を実行すると __`b` の値も変化__ します。

| 行数 | 行われる処理 |
|:--|:--|
| 4   | `a` に `[1, 2, 3]` が代入されます  |
| 5、1 | `psuedo_copy` が呼び出され、仮引数に対して `x = a` という代入処理が行われます。その結果、`x` と `a` は __同じデータを共有__ することになります |
| 2  | return 文が記述されているので、`return` の後に記述されている `x` が関数の返り値（出力）となり、関数呼び出しの処理が終了します  |
| 5  | `psuedo_copy(a)` が、関数の返り値の `x` に置き換わり、`b = x` が実行されます。__`x` と `a`__ は __同じデータを共有したまま__ なので、`a` と `b` と `x` は __同じデータを共有__ することになります |
| 6 | `a[0] = 5` が実行されます |
| 7 | `a` と `b` は __同じデータを共有__ するので、`b` を表示すると `a` の値である `[5, 2, 3]` が表示されます |

まだピンとこない人がいるかもしれないので、上記のプログラムを、関数を使わない __同様の処理__[^9] を行うプログラムに修正した下記のプログラムを見て下さい。下記のプログラムは「その 7」の記事で紹介した __プログラム B__ と同様の処理を行っていますので、下記のプログラムを見てもピンとこない人は、「その 7」から復習して下さい。

```python
a = [1, 2, 3]
x = a
b = x
a[0] = 5
print(b)
```
実行結果
```
[5, 2, 3]
```

:::note
「その 7」の記事で、「変数の代入に関する処理を正しく理解することは、Python のプログラミングを行っていく上で非常に重要で、避けて通ることはできません」と説明したのは、このように __関数の処理__ を __正しく理解__ する際にも __大きく関係__ するからです。
:::

## 関数呼び出しに関する注意点

### 仮引数と実引数の対応

今回の記事で紹介した関数の定義と関数呼び出しを記述する方法では、関数呼び出しの __実引数の数__ と、関数の定義の __仮引数の数__ が __必ず一致する__ 必要があります。下記のプログラムのようにその数が一致しない場合は、関数呼び出しを実行すると __エラーが発生__ します。

下記のプログラムは、実引数の数が仮引数の数より __少ない__ 場合です。

```python
def sum(x, y):
    return x + y

print(sum(1))
```
実行結果
```
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[10], line 4
      1 def sum(x, y):
      2     return x + y
----> 4 print(sum(1))

TypeError: sum() missing 1 required positional argument: 'y'
```

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

* __TypeError__
データ型（Type）に関するエラー
* __sum() missing 1 required positional argument: 'y'__
`sum` を呼び出す際に必要とされる（required）、場所が決まっている[^10] （positional）`y` の実引数（argument）が 1 つ存在しない（missing）
:::

下記のプログラムは、実引数の数が仮引数の数より __多い__ 場合です。

```python
def sum(x, y):
    return x + y

print(sum(1, 2, 3))
```
実行結果
```
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[11], line 4
      1 def sum(x, y):
      2     return x + y
----> 4 print(sum(1, 2, 3))

TypeError: sum() takes 2 positional arguments but 3 were given
```

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

* __TypeError__
データ型（Type）に関するエラー
* __sum() takes 2 positional arguments but 3 were given__
`sum` は 2 つの場所が決まっている（positional）実引数（argument）を受け取る（takes）が、3 つの実引数が与えられた（were given）
:::


### 関数呼び出しを記述できる場所

Python では __関数の定義を実行する前__ に、関数呼び出しを実行すると __エラーが発生__ します[^11]。

```python
mul(1, 2)

def mul(x, y):
    return x * y
```
実行結果
```
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[12], line 1
----> 1 mul(1, 2)
      3 def mul(x, y):
      4     return x * y

NameError: name 'mul' is not defined
```

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

* __NameError__
名前（Name）に関するエラー
* __name 'mul' is not defined__
`mul` という名前（name）は定義されていない（is not defined）
:::

## 入力、処理、出力が存在しない関数

最初に関数は __入力__、__処理__、__出力__ の 3 つで構成されると説明しましたが、実際には入力、処理、出力が __存在しない関数を定義__ する事が __できます__。それぞれについて説明します。

### 入力が存在しない関数

関数の入力は、入力したデータを使って __関数の処理を行う__ ために __利用される__ ものです。従って、__入力データを必要としない処理__ を行う関数を定義する際には、__仮引数を記述しません__。また、そのような関数を呼び出す場合に __実引数を記述しません__。ただし、その場合でも関数の定義時と、関数呼び出しの際に、関数名の後に __`()` を記述する必要がある__ 点に注意して下さい。

入力を必要としない関数の具体例としては、__常に同じ処理を行う関数__ が挙げられます。具体例としては、〇×ゲームのゲーム盤のデータを初期化する関数をこの後で紹介します。

また、現在時刻を表示する関数のように、常に同じ処理を行わない関数であっても、入力を必要としない場合があります。

:::note warn
入力がない場合でも、関数の定義と関数呼び出しの際に、__必ず `()` を記述する必要がある__。
:::

### 出力が存在しない関数

関数の出力を表す __返り値__ は、関数の中で行われた処理の __結果を知らせる__ ためのデータですが、関数で行われた処理の結果を知らせる __必要がない__ 場合は、関数の定義のブロック内に __return 文を記述しません__。return 文が記述されていない関数を実行すると、__データが存在しない__ ことを表す __None が返り値__ になります。

また、一般的にそのような関数の __返り値を利用__ することは __ない__ ので、そのような関数の __関数呼び出し__ を __式の中に記述する__ ことは通常は __ありません__。

`return 文` を記述しない関数のことを、__返り値を返さない関数__ と呼びます。以後は、__返り値を返さない関数__ の処理を説明する際に、None が返り値として返されるという __処理の説明を省略__ します。

return 文が記述されていない関数の具体例としては、__`print`__ が挙げられます。`print` は文字を表示するという処理を行いますが、関数の返り値という形でデータを表示しているわけでは __ありません__。

下記のプログラムは、`print` の返り値を代入した `a` の値を画面に表示ていますが、2 行目で `a` を表示すると、データが存在しないということを表す `None` が画面に表示されます。

```python
a = print(1)
print(a)
```
実行結果
```
1
None
```

:::note warn
return 文が記述されない関数のことを、__返り値を返さない関数__ と呼ぶ。
:::

:::note
関数呼び出しを「誰かに何かを頼む」ことに例えると、関数の返り値は「頼んだ人からの __応答__」に相当します。例えば、咳をしている子供に「体温計で熱を測って」という指示を行った場合は、「測った体温」を応答として受け取る必要があります。一方、「顔を洗ってきて」のように、__自分が結果を知る必要がない__ ような指示に対しては、応答は必要がありません。
:::

### 処理が存在しない関数

一般的には処理を必要としない関数を定義する事に意味はありませんが、処理を全く行わない関数を定義する事ができます。処理を全く行わない関数は、下記のプログラムのように、関数のブロックに __pass のみ__ を記述します[^12]。

```python
def a():
    pass
```

上記の関数は、入力、処理、出力が __いずれも存在しない__ 関数です。このような関数を定義する意味はないと思うかもしれませんが、これから __実装する予定__ の関数を __仮に定義だけ__ をしておき、__後から__ 関数の処理を記述して __実装する__ などの目的で、__実際に__ 上記のような関数を __定義する__ ことがあります。具体例については、必要になった時点で紹介します。

## 入力、出力という用語に関する補足

プログラムでは、__入力__、__出力__ という __用語__ は下記のように、__様々な意味で使われる__ 点に __注意が必要__ です。なお、下記は一例で、他にも入力、出力という用語が使われる場面は数多くあります。

* 入力
    * 関数の実引数
    * キーボードから入力した文字
    * ファイルから読み込まれたデータ
    * カメラなどから得られた画像データ
    * マイクから得られた音
* 出力
    * 関数の返り値
    * `print` によって表示される文字
    * ファイルに書き込まれるデータ
    * 画面に表示される画像
    * スピーカーに出力される音

例えば、これまで `print` は文字列を表示する処理を行う関数であると説明してきましたが、厳密には __標準出力__ に文字列を __出力__ するという処理を行います。標準出力への出力は、__同じ出力という用語__ ですが、関数の返り値による出力とは __全く異なる意味__ を持ちます。

:::note
標準出力とは何かという説明は長くなるので省略しますが、JupyterLab の場合は、標準出力に出力された文字列が、実行したプログラムの __すぐ下のセルに表示__ されるようになっています。
:::

実際に、`print` は __文字列を出力する関数__ であるという説明を見かけることがあります。この説明は、一見すると関数の __返り値として文字列を出力__ するという意味に __思えるかもしれません__ が、実際には `print` は __返り値を返さない関数__ です。

このように、入力、出力という用語は、様々な意味で使われるため __紛らわしい場合がある__ 点に注意が必要です。本記事では、__単に関数の入力__、__出力__ という用語を記述した場合は、__実引数__ と __返り値__ のことを表すことにします。また、それ以外の意味で入力、出力という用語を使う場合は、何に対するどのような入出力であるかがわかるように記述することにします。



## 名前空間

関数呼び出しで行われる処理を正しく理解するためには、__名前空間__ について理解する必要がありますが、今回の記事はかなり長くなりましたので、次回の記事で説明します。

## 関数のまとめ

:::note warn
今回の記事で行った関数に関する説明をまとめると以下のようになります。

* 関数は、__入力__ されたデータに対して、何らかの __処理__ を行い、処理の結果を __出力__ するような機能を持つもののことである
* __関数の定義を実行__ する事で、プログラムでその関数を __利用することが出来るようになる__
* 定義した関数は、__関数呼び出し__ を記述することで利用することができる
* 関数に入力するデータの事を、__実引数__ と呼び、関数呼び出しの `()` の中に半角の `,` で区切って __式__ の形で記述する
* __実引数を代入__ して受け取る __変数__ の事を、__仮引数__ と呼び、関数の定義の関数名の後の `()` の中に半角の `,` で区切って仮引数の __名前__ を記述する
* 関数が行う __処理__ は、関数の定義の __ブロックの中__ に記述する
* 関数呼び出しが実行されると、関数のブロックの処理を __行う前__ に、__仮引数に実引数を代入__ するという処理が行われる
* 関数の処理の結果として __出力__ されるデータの事を、__返り値__ と呼ぶ。返り値は、関数のブロック内で __return 文__ を記述することで指定する
* 関数呼び出しの処理が完了すると、関数呼び出しの部分が __返り値に置き換わって__ 処理が続行される
:::

# マスを配置する関数

関数の基本的な使い方が分かったので、__(x, y) のマスにマークを配置する__ という処理を行う関数を定義して利用することにします。

関数を定義する際には __関数の名前__、__入力__、__処理__、__出力__ を __決める__ 必要があります。この関数は、__マスにマークを配置する処理__ を行うので、配置 (place) とマーク (mark) を表す英単語を組み合わせて __`place_mark`__ という名前の関数にすることにします。

この関数には、(x, y) の __2 つの座標__ と、〇 か × を表す __マーク__ を入力する必要があるので、仮引数は 3 つ必要になります。そこで、__仮引数の名前__ を `x`、`y`、`mark` にすることにします。

この関数は、関数の中で行った処理の結果を返す必要がないので、__返り値を必要としません__。そのため、この関数は、ブロックの中に return 文を記述しない、__返り値を返さない関数__ として定義します。

下記は、前回の記事で作成した `(0, 1)` のマスに 〇 を配置するプログラムです。

```python
if board[0][1] == " ":
    board[0][1] = "〇"
else:
    print("(0, 1) のマスにはマークが配置済です")
```

この関数を元に、__(x, y)__ のマスに __mark__ で指定したマークを配置する関数を記述すると、以下のようなプログラムになります。下記の修正箇所をクリックするか、上下のプログラムを直接見比べて、どことどこが対応しているかを確認して下さい。

```python
def place_mark(x, y, mark):
    if board[x][y] == " ":
        board[x][y] = mark
    else:
        print("(", x, ",", y, ") のマスにはマークが配置済です")
```

<details><summary><strong>修正箇所</strong></summary>

```diff_python
+ def place_mark(x, y, mark):
- if board[0][1] == " ":
+    if board[x][y] == " ":
-    board[0][1] = "〇"
+        board[x][y] = mark
- else:
+    else:
-    print("(0, 1) のマスにはマークが配置済です")
+        print("(", x, ",", y, ") のマスにはマークが配置済です")
```
</details>

具体的な対応箇所は以下の表のようになります。

| 修正前 | 修正後 |
|:-:|:-:|
| `0`  | `x`  |
| `1`  | `y`  |
| `"〇"`  | `mark`  |

# ゲーム盤を初期化する関数

今後の実装で、__ゲーム盤を初期化する処理__ を __頻繁に利用する__ ことが想定されるので、`place_mark` の動作確認を行う前に、ゲーム盤を初期化する処理を行う関数を定義することにします。

関数名は、初期化を表す initialize と盤を表す board を組み合わせて __`initialize_board`__ にすることにします。ゲーム盤の初期化処理は、__常に同じ処理__ を行うので __仮引数は必要ありません__。また、初期化の結果を返す必要がないので、__返り値を必要としません__。

ゲーム盤の初期化を行う関数は下記のプログラムのように記述することができます。

```python
def initialize_board():
    board = [[" "] * 3 for x in range(3)]
```

# 動作確認

必要な関数が定義できたので動作確認を行います。下記は前回のプログラムです。

```python
board = [[" "] * 3 for x in range(3)]
if board[0][1] == " ":
    board[0][1] = "〇"
else:
    print("(0, 1) のマスにはマークが配置済です")
print(board)
if board[0][1] == " ":
    board[0][1] = "×"
else:
    print("(0, 1) のマスにはマークが配置済です")
print(board)
```
実行結果
```
[[' ', '〇', ' '], [' ', ' ', ' '], [' ', ' ', ' ']]
(0, 1) のマスにはマークが配置済です
[[' ', '〇', ' '], [' ', ' ', ' '], [' ', ' ', ' ']]
```

上記のプログラムを、先ほど定義した関数を使って、下記のように記述して実行してみることにします。

```python
def place_mark(x, y, mark):
    if board[x][y] == " ":
        board[x][y] = mark
    else:
        print("(", x, ",", y, ") のマスにはマークが配置済です")

def initialize_board():
    board = [[" "] * 3 for x in range(3)]

initialize_board()      # ゲーム盤の初期化処理を行う関数を呼び出す
place_mark(0, 1, "〇")  # (0, 1) のマスに 〇 を配置する
print(board)
place_mark(0, 1, "×")   # (0, 1) のマスに × を配置する
print(board)
```
実行結果
```
( 0 , 1 ) のマスにはマークが配置済です
[[' ', '〇', ' '], [' ', ' ', ' '], [' ', ' ', ' ']]
( 0 , 1 ) のマスにはマークが配置済です
[[' ', '〇', ' '], [' ', ' ', ' '], [' ', ' ', ' ']]
```

実行結果を見ると、修正前と異なり、「( 0 , 1 ) のマスにはマークが配置済です」 が __2 回表示__ されてしまいます。これは、修正後のプログラムのどこかに __バグがある__ からです。

次回の記事では、修正後のプログラムのバグの原因を理解するために必要な知識として、名前空間とスコープについて説明します。

:::note
実行結果の `(0, 1)` と `( 0 , 1 )` が微妙に違うと思った人がいるかもしれません。その理由は、`place_mark` の `print` で、__`,` を使って__ 表示するデータを __並べて記述__ しているからです。`print` は `,` で区切ったデータを表示する際に、__間に半角の空白を表示__ する仕様になっています。

`print` を使って、空白を開けずに複数のデータを並べて表示する方法については、今後の記事で紹介します。
:::

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

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

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

# 次回の記事

https://qiita.com/ysgeso/items/cd3e1c0731b859bf5e2c

# 更新履歴

| 更新日時 | 更新内容 |
|:--|:--|
| 2023/09/29 | `for y in range(3)` → `for x in range(3)` |
| 2023/09/26 | 「set_one」を「assign_one」に修正しました<br>「set_mark」を「place_mark」に修正しました |

[^1]:関数の定義の際に記述する def は、定義を表す define の略です
[^2]:Python では、変数名や関数名には、アルファベット、アラビア数字、`_` という記号を使うことができます。ただし、アラビア数字を名前の先頭の文字として使用することはできません
[^3]:`print` で関数名を表示した際に表示される function は、関数を表す英単語です
[^4]:関数名の後に `()` を記述しなかった場合は、関数呼び出しとは __異なる処理__ が行われます。具体的にどのような処理が行われるかについては、そのような記述が必要になった時点で説明します
[^5]:戻り値と呼ぶ場合もあります
[^6]:過去の記事でも説明しましたが、`1` のような単なる数値も __式の一種__ です
[^7]:用語が異なるのではじめはそのように思えないかもしれませんが、__仮引数は__、実引数の値で初期化されるという点を除けば、__変数と全く同じ性質__ を持ちます
[^8]:「その 11」の記事では「疑似的な複製」と区別できるように「完全な複製」と表記していましたが、冗長なので今回の記事からは単に「複製」と記述します
[^9]:先ほどのノートで説明したように、厳密には同じ処理ではありません
[^10]:今回の記事では紹介していませんが、Python には記述する場所が決まっていない引数があります
[^11]:JavaScript など、関数を定義する前に関数呼び出しを行うことができるプログラム言語がありますが、Python ではそのようなことはできません
[^12]:関数の定義でブロックに何も記述しないとエラーが発生します