ChatGPTは本当にプログラミングができるのか　自分の本の練習問題で実験した

# はじめに
「最短コースでわかる」シリーズ、「Pythonで儲かるAIを作る」著者の赤石です。
今、世界中に衝撃を与えている**ChatGPT**。
その機能はあらゆる領域に及んでいますが、その中の一つにプログラミング機能があります。
マンデルブロウの図形を描画させるプログラムを実装したりして、十分びっくりしてはいるのですが、本当に自分で考えてコーディングしているのか。実は、有名なプログラムはテンプレートで覚えているだけなのではないか。
それを試す格好の題材を見つけました。
手前味噌ですが、私の最新刊である「[最短コースでわかる Pythonプログラミングとデータ分析](https://www.amazon.co.jp/dp/4296201123
)」では、各節ごとに、理解度を確認するための練習問題を付けています。そうです、この問題をChatGPTにやらせれば、本当に問題の意味を自然言語として理解し、それに対応したコーディングができているのか、わかるはずです。
ということで、早速試してみました。結論からいうと、1章のPython入門の問題はほとんど難なくこなした、というより簡単過ぎてChatGPTには物足りなかった模様です。　
ChatGPT恐るべしの思いを新たにした次第です。ここからは、個別の問題と、それに対するChatGPTの解答をみていきたいと思います。
皆さんも、ChatGPTに対抗して自分で解いてみてください。

ちなみに、「最短コースでわかる Pythonプログラミングとデータ分析」のAmazonリンクは下記になります。

https://www.amazon.co.jp/dp/4296201123

また、他の章の分を含め、問題と解答はすべて次の、書籍用サポートサイト(Github)からアクセス可能です。

https://github.com/makaishi2/data_analysis_book_info/blob/main/README.md


#  第一部　問題編
第一部は「問題編」ということで、「原則」書籍の問題文をそのまま抜き出しました。
「原則」と注釈を付けたのは、一つだけ例外があったからです。どういう例外かは1.8節の問題のところで説明します。

## 1.3節 変数

```
次のプログラミングの問題をPythonで実装してください。

3つの変数a、b、cを使います。
変数cは、変数aと変数bを使って、「c = b - a」の計算により求められるものとします。
今、変数aの値が3、変数bの値が10であるとします。この時、変数cの値を変数a、bを経由して求め、結果をprint関数で表示してください。
```

## 1.4節 データ型と算術演算

```
次のプログラミングの問題をPythonで実装してください。
　
変数a、b、cにそれぞれ値1000、2000、3000を代入します。
次に、3つの変数それぞれを整数の範囲で7で割った余りを計算し、3つの余りをすべて足した結果をdに代入してください。
dの計算方法はいろいろありますが、累算代入演算子をうまく使って計算してください。
```

## 1.5節 条件分岐

```
次のプログラミングの問題をPythonで実装してください。

1. 変数xに12を代入します。
　次にコード1-5-3を参考に、「xが10以上でかつxは3で割り切れる」を判断する論理演算の計算をして、その結果を条件分岐の条件とするようなif文を作ります。
　そして、上の条件を満たしている場合は、'Success!'を、満たしていない場合は'Fail!'をprint関数で出力するようにしてください。
　正しくコーディングできている場合、結果は'Success!' になるはずなので、それを確認します。

2. 変数 xの値を9、13、15に変更した場合、結果がそれぞれどうなるか確認します。
```

## 1.6節 関数呼び出しとメソッド呼び出し

```
次のプログラミングの問題をPythonで実装してください。

変数s1に文字列 'I like an apple.'を、変数s2に文字列 'apple' を代入します。
次にfindメソッド、比較演算子、条件分岐をうまく使って、変数s1の中に変数s2が含まれているかどうかを判断し、結果をprint関数で出力するプログラムを作ってください。
結果のメッセージは、次のようなものにします。

s2が 'apple' の場合：「I like an apple.はappleを含んでいます。」
s2が 'orange' の場合：「I like an apple.はorangeを含んでいません。」
```

## 1.7節 リストとループ処理

```
次のプログラミングの問題をPythonで実装してください。

変数s1に文字列 'サンプル文字列' を代入します。
このとき、range関数やループ処理をうまく使って、次のような結果を出力するプログラムを作ってください。

1 番目の文字は　 サ
2 番目の文字は　 ン
3 番目の文字は　 プ
4 番目の文字は　 ル
5 番目の文字は　 文
6 番目の文字は　 字
7 番目の文字は　 列
```

## 1.8節 タプル・集合・辞書

この問題は、問題文の中に表を含んでいたので、最初、そもそもChatGPTに入力するのは不可能と考え、スキップしていました。
しかし、「マークダウンにしてあげれば表も理解できるかも」と思い、次のように、マークダウン化した表データに翻訳して、問題として与えてみました。

```
次のプログラミングの問題をPythonで実装してください。

あるECサイトでは、ある期間内の商品の売り上げが次のMarkDownで表現される表のようでした。

| 商品コード | 販売数 |
| ---------- | ------ |
| T254       | 12     |
| A727       | 6      |
| T256       | 4      |
| T254       | 10     |
| A726       | 7      |
| A727       | 4      |

この表が、次の変数dataで表現されていることを前提として、商品ごとの販売数合計結果を出して下さい。

data = [
    ('T254', 12),
    ('A727', 6),
    ('T256', 4),
    ('T254', 10),
    ('A726', 7),
    ('A727', 4)
]

結果は、次のような辞書形式で欲しいです。

{'T254': 22, 'A727': 10, 'T256': 4, 'A726': 7}
```

## 1.9節 関数定義

```
次のプログラミングの問題をPythonで実装してください。

（1） 「章番号:章タイトル」の書式の文字列を引数とし「:」によって文字列を章番号、章タイトルに分ける関数div_chapterを定義してください。

（2） 上で定義した関数を用いて、次の文字列をそれぞれ章番号、章タイトルに分割してください。章番号を「キー」に、章タイトルを「値」にする辞書title_dictに登録してください。

'1章:業務と機械学習プロジェクト'
'2章:機械学習モデルの処理パターン'
'3章:機械学習モデルの開発手順'
```

## 1.10節 やや複雑なループ処理

```
次のプログラミングの問題をPythonで実装してください。

cabins = ['B5', 'C22', 'G6']を処理対象として、内包表記を用いて各要素の文字列の長さを要素にした新しいリストlengthsを作ってください。
```



# 第二部　解答編
第二部は「解答編」です。ここでは、それぞれの出題に対してChatGPTが返した内容をそのままコピペして貼り付けた後、それぞれに対して「解説」（というか感想）を簡単に加えました。

## 1.3節 変数

```python
a = 3
b = 10
c = b - a
print(c)
```

### 解説
一番最初の問題で簡単ということはありますが、まずは完璧な解答でした。

## 1.4節 データ型と算術演算

```python
a = 1000
b = 2000
c = 3000
d = (a % 7) + (b % 7) + (c % 7)
print(d)
```

### 解説
プログラムとしては完全に動作するのですが、問題の中で言及している「累算代入演算子をうまく使って」には対応できていません。
この点は減点材料かと思いますが、１行でできるプログラムをわざわざ複数行に分割することになります。
その点が、ChatGPTのようなイケてるプログラマーには耐えられなかったのかもしれません。
一応、書籍の解答例も示しておきます。

```python
# 変数a, b, cの値をそれぞれ設定します
a = 1000
b = 2000
c = 3000

# aを7で割った余りを変数dに代入」します
# 1.4.2項　コード1-4-11参照
d = a % 7

# 上の結果に、累算代入演算子を利用してbを7で割ったあまりを加えて下さい
# 1.4.3項　コード1-4-13参照
d += b % 7

# 更にその結果に、ｃを7で割った余りを加えます
d += c % 7

# print関数で結果を表示します。
print(d)
```

## 1.5節 条件分岐

```python
x = 12
if x >= 10 and x % 3 == 0:
    print('Success!')
else:
    print('Fail!')
```

```python
x = 9
if x >= 10 and x % 3 == 0:
    print('Success!')
else:
    print('Fail!')

x = 13
if x >= 10 and x % 3 == 0:
    print('Success!')
else:
    print('Fail!')

x = 15
if x >= 10 and x % 3 == 0:
    print('Success!')
else:
    print('Fail!')
```

```
Fail!
Fail!
Success!
```

### 解説
完璧な解答で、文句なしです。

## 1.6節 関数呼び出しとメソッド呼び出し

```python
s1 = 'I like an apple.'
s2 = 'apple'

if s1.find(s2) != -1:
    print(s1 + 'は' + s2 + 'を含んでいます。')
else:
    print(s1 + 'は' + s2 + 'を含んでいません。')
```

### 解説
こちらも完璧です。恐れ入りました。。。

## 1.7節 リストとループ処理

```python
s1 = 'サンプル文字列'
for i, c in enumerate(s1):
    print(f"{i + 1} 番目の文字は　 {c}")
```

### 解説
書籍では、いったん下記の解答を示したあとで、もっと後ろの節でenumerate関数の解説のあとで、ChatGPTが示したような短い実装例を示しています。先回りしてよりシンプルなコードを示したChatGPTは一言でいうと完璧です。

```python
s1 = 'サンプル文字列'
N = len(s1)
for i in range(N):
    print(i+1, '番目の文字は　', s1[i])
```

## 1.8節 タプル・集合と辞書

```python
from collections import defaultdict

data = [
    ('T254', 12),
    ('A727', 6),
    ('T256', 4),
    ('T254', 10),
    ('A726', 7),
    ('A727', 4)
]

result = defaultdict(int)
for item in data:
    result[item[0]] += item[1]

print(dict(result))
```

```
{'T254': 22, 'A727': 10, 'T256': 4, 'A726': 7}
```

### 解説
驚きました。表を含んでいたりとか、結構、この問題は日本語自体が難しいと思います。
それを完璧に理解して、正しい実装を示しています。
しかも、ChatGPTが使っているdefaultdictというライブラリを私は知らなかったです。
恥を忍んで、私のイケていない実装を以下にさらします。
なるほど、このライブラリを使うと、条件分岐がいらないのですね。勉強になりました。
こいつ、私よりPythonは上手であることがわかりました。。。

```python
# 読み込み対象データの初期設定
data = [
    ('T254', 12),
    ('A727', 6),
    ('T256', 4),
    ('T254', 10),
    ('A726', 7),
    ('A727', 4)
]

# 辞書　初期状態は空
d2 = {}

# この部分を実装します
# 外側はループ処理
# 内側はif文で２階層の組み合わせになります。

# 1.8.1項　コード1-8-7参照
for key, value in data:
    # キーが辞書に存在するかどうかをチェック
    # 1.8.3項　コード1-8-13参照
    if key in d2.keys():
        # 該当キー項目が存在する場合
        # 1.8.3項　コード1-8-12参照
        d2[key] += value
    else:
        # 該当キー項目が存在しない場合
        # 1.8.3項　コード1-8-12参照
        d2[key] = value

# 結果の確認
print(d2)
```


## 1.9節 関数定義

```python
def div_chapter(chapter):
  key, value = chapter.split(':')
  return key, value

title_dict = {}
chapters = ['1章:業務と機械学習プロジェクト', '2章:機械学習モデルの処理パターン', '3章:機械学習モデルの開発手順']
for chapter in chapters:
  key, value = div_chapter(chapter)
  title_dict[key] = value

print(title_dict)
```

### 解説
またまた完璧な解答で恐れ入りました。

## 1.10節 やや複雑なループ処理

```python
cabins = ['B5', 'C22', 'G6']
lengths = [len(cabin) for cabin in cabins]
print(lengths)
```

### 解説
これまた完璧な実装でした。

(ここからは、1/31に追加しました)
# 第三部　教師編

これだけでも十分すごいのですが、実はChatGPTの真骨頂は今まで試したような一問一答型式のやりとりでなく、コンテキストに依存した何往復ものやりとりにあります。
今までの題材を使って、この実験をしてみましょう。具体的には「1.4節 データ型と算術演算」と「1.8節 タプル・集合と辞書」の問題を利用します。

## 「1.4節 データ型と算術演算」

まずは、「1.4節 データ型と算術演算」に関して、どうしてこちらの指示を無視して、累算代入演算子を使わなかったのか、聞いてみようと思います。

同じ問題を再び解いてもらいました。

---
次のプログラミングの問題をPythonで実装してください。
　
変数a、b、cにそれぞれ値1000、2000、3000を代入します。
次に、3つの変数それぞれを整数の範囲で7で割った余りを計算し、3つの余りをすべて足した結果をdに代入してください。
dの計算方法はいろいろありますが、累算代入演算子をうまく使って計算してください。

---


```python
a, b, c = 1000, 2000, 3000
d = (a % 7) + (b % 7) + (c % 7)
print(d)
```

今度は、「アンパック代入」と呼ばれる３つの変数を同時に代入する技を使ってきました。
(ちなみに、まったく同じ指示を何度も出すと、一回一回微妙に実装を変えてくることが多いです。全部ちゃんと動きます。)

では、ここで次の質問をしてみます。

『「累算代入演算子をうまく使って」とお願いしたのに、この演算子を使わなかったのはなぜですか。』

返事は以下でした。なんとも謙虚なChatGPTです。「指示が適切でないから、もっとシンプルな実装をした」みたいな生意気な答えを期待していたのですが。

『ご指摘ありがとうございます。下記のように修正いたします。』

```python
a, b, c = 1000, 2000, 3000
d = 0
d += a % 7
d += b % 7
d += c % 7
print(d)
```
受け答えを含めて完璧でした。

## 「1.8節 タプル・集合と辞書」
次に試すのは、私の知らないクラスを使って、エレガントな実装を見せつけられた問題です。もう一度同じ問題を出しました。

---
次のプログラミングの問題をPythonで実装してください。

あるECサイトでは、ある期間内の商品の売り上げが次のMarkDownで表現される表のようでした。

| 商品コード | 販売数 |
| ---------- | ------ |
| T254       | 12     |
| A727       | 6      |
| T256       | 4      |
| T254       | 10     |
| A726       | 7      |
| A727       | 4      |

この表が、次の変数dataで表現されていることを前提として、商品ごとの販売数合計結果を出して下さい。

data = [
    ('T254', 12),
    ('A727', 6),
    ('T256', 4),
    ('T254', 10),
    ('A726', 7),
    ('A727', 4)
]

結果は、次のような辞書形式で欲しいです。

{'T254': 22, 'A727': 10, 'T256': 4, 'A726': 7}

---

```python
data = [    ('T254', 12),    ('A727', 6),    ('T256', 4),    ('T254', 10),    ('A726', 7),    ('A727', 4)]

result = {}
for item in data:
    if item[0] in result:
        result[item[0]] += item[1]
    else:
        result[item[0]] = item[1]

print(result)
```

前回、こちらが結果に動揺したのに忖度したのか、今回は書籍の解答例と同じ平凡な実装でした。
いや違う、私の見たいのはこのコードではないのだ。
「もっとやれ」と追加の指示を出します。

私『もっと簡単な実装はできませんか。』

ChatGPT『はい、次のようなコードを使うともっと簡単に実装できます。』

```python
from collections import defaultdict

data = [
    ('T254', 12),
    ('A727', 6),
    ('T256', 4),
    ('T254', 10),
    ('A726', 7),
    ('A727', 4)
]

result = defaultdict(int)
for item in data:
    result[item[0]] += item[1]

print(dict(result))
```

なんだ、やっぱり知っているのに隠していたのか。水くさいやつだ。
でも、これでようやく本当にやりたかった質問ができるようになりました。

私『すごいです。defaultdictを使ったのはなぜですか。』

ChatGPT『defaultdictは通常の辞書と同様に振る舞いますが、存在しないキーを参照したときにデフォルト値を返すという点が異なります。ここでは、販売数を保存するために使っています。もし、通常の辞書を使った場合、存在しないキーを参照したときに KeyError エラーが発生しますが、defaultdictを使えばデフォルト値 (ここでは0) を返すため、エラーが発生しません。』

私『ありがとうございます。大変勉強になりました。』

ChatGPT『どういたしまして！お力になれてうれしいです。何か分からないことがありましたら、気軽に質問してください。』

素晴らしい！受け答えの礼儀正しさを含めて完璧です。

第三部ではちょっとロールプレイっぽくChatGPTと会話してみたわけですが、このやりとりを通じて何をいいたかったかというと、「**今やプログラミングを勉強したい人はスクールなどに通わず、ChatGPTさんを先生にすればいいのでかないのか**」ということなのです。

相手が人間だと、同じことを何度も聞くのは恥ずかしくて、わかっていなくてもわかったふりしたりすると思います。
ChatGPTなら、そんなことを気にせず、自分のペースで納得がいくまで何回でも質問できます。壁打ち相手役として、ChatGPTは完璧なわけです。
一方で、自学で勉強するにはテキストが必要です。その時何を使えばいいのか。第三部を長々と引っ張った最終的な目的がここにあります。
私の最新著書「[最短コースでわかる Pythonプログラミングとデータ分析](https://www.amazon.co.jp/dp/4296201123)」がその答えです。
これが、最強の自習教材と自信を持ってお勧めします。本当かどうか怪しいと思われる方は、まず　[書籍サポートページ](https://github.com/makaishi2/data_analysis_book_info/blob/main/README.md
)を見てみて下さい。この本の特徴も説明していますし、当記事で引用した例題とその解答を含めて、サンプルコードはすべて公開しています。このページでご確認の上、お買い求めいただければ幸いです。我田引水なオチで失礼しました。

# 番外編　ChatGPT利用に関して雑感
今回の一連のテストでのハイライトは、1.8節の問題でのやりとりであり、この結果を得るためには、質問する側にも工夫が必要でした。
私が、「この人、表もちゃんと理解している」と確信するに至った理由は、「マークダウンで記述すれば表も理解できるのでは」という仮説を立てた後、仮説に基づき、次の実験をして想定通りの答えを得ていたからです。

私の質問

```
あるECサイトでは、ある期間内の商品の売り上げが次のMarkDownで表現される表のようでした。

| 商品コード | 販売数 |
| ---------- | ------ |
| T254       | 12     |
| A727       | 6      |
| T256       | 4      |
| T254       | 10     |
| A726       | 7      |
| A727       | 4      |

この前提で、次の質問に答えて下さい。

(Q1) 「男の子用食器セット」の商品コードがA726である場合、この期間内の「男の子用食器セット」の販売数はいくつですか？
```

ChatGPTの答え
```
「男の子用食器セット」の商品コードがA726である場合、この期間内の「男の子用食器セット」の販売数は7です。
```

私の質問
```
(Q2) 「目覚まし時計(青)」の商品コードはT254です。この期間内の「目覚まし時計(青)」の販売数はいくつですか？　
```

ChatGPTの答え
```
「目覚まし時計(青)」の商品コードがT254である場合、この期間内の「目覚まし時計(青)」の販売数は12 + 10 = 22です。
```

このやりとりからChatGPTが、完全に「表」と「商品コード」の概念を理解していることがわかります。
細かくこの結論に至ったプロセスを自己分析していくと、まず、私が「マークダウンにしてあげれば表も理解できるのではないか」という仮説を持ち、更にその仮説に基づいて今、示したようなテストケースを考えつき、テストの結果仮説の正しさが検証され、ようやくこの利用方法にたどり着けたわけです。
いってみれば、コンサルタントとして必須の能力である「仮説立案・仮説検証」がちゃんとできるかどうかという話に帰着されます。

ChatGPTは「秘書」「アシスタント」「先生」呼び方はわかりませんが、十分過ぎるほどの潜在能力を持った、対等のパートナーとしての力を持っています。
その潜在能力を引き出せるかどうか、それは、ひとえに使う側の人間の能力にかかっています。
これからの社会は、こういう人間として一番根本的な能力が、今まで以上にシビアに問われる時代になるのかと、感じました。
その世界は、人によってはユートピアですし、人によってはディストピアでしょう。