プログラミング未経験者がPython覚えて子ども用計算ドリルを作る

##まえがき
35歳までプログラミングを一切やったことはなかったのですが、いろいろ作りたいものが出てきたのでUdemyでプログラミングを覚えることにしました。なんとなく覚えやすそうで最近流行っているらしいPythonを勉強しました。

###Pythonの勉強をするために使ったUdemyのコース
プログラミング言語　Python 3 入門
https://www.udemy.com/intro-to-python3/learn/v4/content

みんなのAI講座 ゼロからPythonで学ぶ人工知能と機械学習
https://www.udemy.com/learning-ai/learn/v4/overview


##今回作りたいもの
四則演算の計算ドリルを作ります。まずは足し算で繰り上がりがあるものと無いものがまじった計算ドリルです。
↓こういうの
<img width="341" alt="スクリーンショット 2019-01-25 18.44.29.png" src="https://qiita-image-store.s3.amazonaws.com/0/119751/e2fa3d5c-35d5-7b6f-53d4-05bba2413b3c.png">
なんでこれを作りたいかというと、うちには小学生の子どもがいて、毎日足し算のドリルをやらせたいのですが、こういう計算ドリルって市販のものだと数枚しかついていないのです。仮に自分で作ってコピーしても、子どもは並びを覚えているので計算せずに、記憶だけで解こうとしてしまうので、並びが変わると応用がききません。
今回は、足し算がA4用紙で印刷できるかたちで出力されて、かつ毎回計算の並びが異なるものを作ろうと思います。
ちなみに分とか秒と書いてあるのは毎回タイムを測っておいて、どれぐらいのスピードでできたかを毎回記録していくために入れています。まだ足し算が完全にできないときには結構時間がかかるのですが、だんだん早くなってくるのが可視化されるので、褒めるポイントが増えるしモチベーションにもつながるのです（タイムが伸びなかったり遅くなるとモチベーションが落ちるのですが）。
ちなみに自分で作った問題でやってみたのですが、30秒でした。あと1、2秒は縮められると思いますが鉛筆のスピード的にそれが限界かなあと。このプログラムで計算ドリルを作って皆さんもやってみてください。

##要件
1. 繰り上がりありと繰り上がりなしの足し算が並んでいる。
2. 6+7と7+6は別の足し算とする。（小さい子はこれを同じものだという認識がまだない）
3. プログラムを実行する毎回並びが変わる。
4. Excelで出力できる、A4で印刷できる。


##開発したプログラム
まず完成品です。多分スーパーな人から見たらきれいじゃないプログラムだってわかるんですけど、今はこれで精一杯です。

```increase.py
import openpyxl
import random

def insert(under, c, x, y, line):
    for r in range(1, line, 2):
        if x < under:
            number = list[x][y]
            sheet.cell(row=r, column=c, value=number)
            x += 1

book = openpyxl.load_workbook('足し算.xlsx')
sheet =book['Sheet1']

list = []
for a in range(1, 10):
    for b in range(1, 10):
        list.append([a, b])
random.shuffle(list)

under = 13
c = 1
x = 0
y = 0
line = 26
while under < len(list)/2  :
    if y == 0:
        insert(under, c, x, y, line)
        c += 2
        y = 1

    else:
        insert(under, c, x, y, line)
        under += 13
        c += 3
        x += 13
        y = 0

under +=1
line +=2
while under <= len(list)  :
    if y == 0:
        insert(under, c, x, y, line)
        c += 2
        y = 1
    else:
        insert(under, c, x, y, line)
        under += 14
        c += 3
        x += 14
        y = 0
        
book.save('足し算.xlsx')
```
あと今回はExcelに数字を埋め込む形でやりました。スペースを作るために、一段とばしで数字を入れていくやり方にしたのですが、別にこんなことしなくても文字と幅をうまく調整すればよかったんじゃないかと作ってから思いました。
A列からO列を1枚目、P列からAD列を2枚目として印刷できるように幅や高さを調整しています。
+とか=とか分とか秒という文字は予め入れておいて、今回のプログラムで挿入することはしません。あくまで数字だけです。
![スクリーンショット 2019-01-25 19.03.46.png](https://qiita-image-store.s3.amazonaws.com/0/119751/93552eb3-c3bd-9e2e-d399-bf43e032b7b8.png)

一個ずつ解説していきます。
##足し算のリストを作る
```python
list = []
for a in range(1, 10):
    for b in range(1, 10):
        list.append([a, b])
random.shuffle(list)
```
aが+の右側、bが左側です。for a in range(1, 10)で変数aに1〜9の数字を入れ、合わせてbにも同様に1〜9を入れます。これによって、listには[[1,1],[1,2],[1,3],〜[9,8],[9,9]]と値が入っていってくれます。
listができたらrandomを使って配列の並びをバラバラにします。
randomは
import random
で使えるようにしています。
randomについての簡単な解説はこちらから。

pythonでrandomを使ってみる。
https://qiita.com/ajitama/items/cacf4d68b26333143fd8

##Excelのファイルを開く
Excelのファイルを開いたり書き換えたりするときにはopenpyxlというモジュールを使います。

pythonのopenpyxlの使い方メモ
https://qiita.com/sky_jokerxx/items/dc9d8827d946b467ba4b

```python
book = openpyxl.load_workbook('足し算.xlsx')
sheet =book['Sheet1']
```
pythonのファイルが置いてあるところに足し算というエクセルファイルをおいておきます。それを`openpyxl.load_workbook('足し算.xlsx')`で指定して開きます。`book`という名前は適当に決めました。`book['Sheet1']`でシートを指定します。
今回作ったExcelファイルのシートは画像のようなフォーマットにしています。+と=は予め入れておいて、AとCのところに配列の値を挿入していきます。
<img width="329" alt="スクリーンショット 2019-01-26 15.17.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/119751/9ea86587-6d81-eb60-a5ba-b24a0f751750.png">


##配列の値をExcelに挿入する
こういう関数を作りました。

```python
def insert(under, c, x, y, line):
    for r in range(1, line, 2):
        if x < under:
            number = list[x][y]
            sheet.cell(row=r, column=c, value=number)
            x += 1
```

やりたいこととしては、まずA列にリストのaの値を挿入していくということです。
<img width="329" alt="スクリーンショット 2019-01-26 15.17.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/119751/399e11eb-dffa-76a4-e721-c07c9c1872b8.png">


まず、
`number = list[x][y]`
について、プログラミングを知っている人なら説明を省略していいですが、そうじゃない人向けに書きます。
はさっき作った足し算のリストのx番目の配列のy番目の値をnumberに入れるという意味です。[[1,1],[1,2],[1,3],〜[9,8],[9,9]]というリストで`number = list[1][0]`なら[1,2]の1をnumberに入れますし、`list[1][1]`は2をnumberに入れいます。さっきのリストでいうaは0で、bは1ですね。

次に
`sheet.cell(row=r, column=c, value=number)`
について説明します。openpyxlのモジュールの機能でcellを使うと指定の場所に値を入れる事ができます。普通はA1とかA3とかで指定しますが、今回は繰り返し処理をしたいので行列表記でいれます。
rowで行（1行目、2行目などですね）、columnは列（A列やB列のことで、A列は1、B列は2です）で、valueで挿入する値をしていします。
たとえば`sheet.cell(row=1, column=1, value=3)`とするとA1に3という数字を入れてくれます。

そして、

```python
for r in range(1, line, 2):
        if x < under:
```

ですが、rは先のsheet.cellでrowに入る値を指します。`for r in range(1, line, 2)`で1、3、5、7という順でrに値を入れるのを繰り返します。値を入れてほしいのは26行目までなので、lineには26をいれておきます。
`if x < under`ではリストの配列の順番を制御しているものがunderという基準より下かどうかを判定します。
後述しますが、1列目ではこういう値が入ります。
`insert(13, 1, 0, 0, 26):`
するとそれ以降の関数はこのようになります。

```python
    for r in range(1, 26, 2):
        if x < 13:
            number = list[x][0]
            sheet.cell(row=r, column=1, value=number)
            x += 1
```

xは最初0からスタートしてリストの0番目のaの値を取ってきます。その後にA1のセルに値を挿入したあとに、xに1がプラスされます。
このあとfor文に戻ってrが3になったあとにリストの1番目をとってきて、A3のセルにaの値を挿入して、xに1がプラスされて2になって、ということをxが13になるまで繰り返されます。こうやってA列の下まで値が入っていきます。

##C列以降に値を挿入する
C列以降に値を入れようと思ったら、先程のfor文はこういう値になります。xは0に戻る必要があります。

```python
    for r in range(1, 26, 2):
        if x < 13:
            number = list[x][1]
            sheet.cell(row=r, column=3, value=number)
            x += 1
```

先程のとの違いは3列目numberのところの数式の[0]が[1]になってリストのbの値をとってこようとしてるところと、4列目のcolumnが3になっています。ここが3になるとC列に値を入れようとしているということです。先程A列に入れていたことと同じことをやっていきます。
この動きを数式にしようとすると、

```python
under = 13
c = 1
x = 0
y = 0
line = 26
while under < len(list)/2  :
    if y == 0:
        insert(under, c, x, y, line)
        c += 2
        y = 1

    else:
        insert(under, c, x, y, line)
        under += 13
        c += 3
        x += 13
        y = 0
```
となります。まず、if文のところから説明するのですが、yが0だった場合は、aの値を挿入する処理を行っていきます。
そしてinsert関数が完了するとcに2を足して、yを1にします。
ここはwhileによってループするのですが、yが1なので下の段の計算に入って、またinsert関数を適用させてbの値をExcelに挿入していきます。
そしてbの値の挿入を完了するとunderとxに13を足します。これは、先のリストで0番目〜12番目まで値の取得が完了したため、次のF列に入れる値は、13番目〜26番目の値であるためです。
cに3を加えているのはD列で=、E列に回答用のスペースを作っているので、3列分ずれる必要があるためです。またyは0に戻して、リストのaの値をとってこれるようにします。
whileでループさせないようにするとこんな頭の悪いプログラムになります。
`insert(13, 1, 0, 0, 26)`←A列
`insert(13, 3, 0, 1, 26)`←C列
`insert(26, 6, 13, 0, 26)`←F列
`insert(26, 8, 13, 1, 26)`←H列
`insert(39, 11, 26, 0, 26)`←K列
`insert(39, 13, 26, 1, 26)`←M列
最初どうしていいかわかんなくてこんなのをまず書いてから、ロジック考えていましたけど。

それで、whileが`while under < len(list)/2`ってなっている理由なのですが、1枚目と2枚目で計算ドリルの問題数が違うためなのです。

##2枚目の計算ドリルに値を挿入する
1+1から9+9までの組み合わせは81個あります。これを2枚にきっちり分けようとすると、39問と42問の分け方になってしまいます。40問と41問という分け方もできるでしょうかど、プログラムのロジックがよくわからなくなるのでそこは妥協しました。2枚目の方が問題数多いのです。

```python
under +=1
line +=2
while under <= len(list)  :
    if y == 0:
        insert(under, c, x, y, line)
        c += 2
        y = 1
    else:
        insert(under, c, x, y, line)
        under += 14
        c += 3
        x += 14
        y = 0
```
1列あたりの問題数が13問から14問に増えるので、underに1を、lineに2を足して、14問分のループができるようにしました。
これでM列に値を挿入するのにどのようなinsert関数にどのような値が入っているかというと、以下のとおりです。xは39からスタートです。

```python
for r in range(1, 28, 2):
        if x < 53:
            number = list[x][0]
            sheet.cell(row=r, column=16, value=number)
            x += 1
```

whileを使わないとこんなinsert関数の処理が行われます。
`insert(53, 16, 39, 0, 28)`←P列
`insert(53, 18, 39, 1, 28)`←R列
`insert(67, 21, 53, 0, 28)`←U列
`insert(67, 23, 53, 1, 28)`←W列
`insert(67, 26, 67, 0, 28)`←Z列
`insert(67, 28, 67, 1, 28)`←AB列

##Excelを保存する
ようやく値を入れ終わりました。openpyxlモジュールのsaveを使うとファイル保存ができます。今回は上書き保存でいいので開いたファイルと同じ名前で保存しました。
`book.save('足し算.xlsx')`

以上です。これでPythonを実行するたびに、問題の並びが変わる計算ドリルを作る事ができました。


##足し算以外の計算の作り方
###引き算
2-1から18-9までのパターンです。引く側が一桁でかつ答えが0を除く1桁になる引き算の混ざったリストは以下のかたちでできます。Excelの+は-に変えておいてください。

```python
list = []
for a in range(2, 19):
    for b in range(1, 10):
        if a - b < 10 and a - b > 0 :
            list.append([a, b])
random.shuffle(list)
```

###掛け算
これは九九の問題ですね。足し算と一緒です。Excel側で+を×に変えておいてください。

```python
list = []
for a in range(1, 10):
    for b in range(1, 10):
        list.append([a, b])
random.shuffle(list)
```

###割り算
これは1÷1とかやっても仕方ないので2スタートです。

```python
list = []
for a in range(2, 10):
    for b in range(2, 10):
        list.append([a * b, b])
random.shuffle(list)
```
ただ、これだと問題数が64問になるので、1ページ32問になってしまいます。ただ、それだと1枚11行、11行、10行の組み合わせで、数式が複雑になってしまいます。まあ、1枚目10行、10行、11行で2枚目は全部11行とかにすればいいと思うのですが、なんかそれは納得いかない。ここは全体的に解決方法見つけたら改めてプログラムを書こうと思います。


#余談
最初は「みんなのAI講座 ゼロからPythonで学ぶ人工知能と機械学習」からやっていたのですが、途中からついていけなくなるところがあって、Pythonに特化した学習コースがあるのがいいなと思って「プログラミング言語　Python 3 入門」をやっていました。
開発環境は「プログラミング言語　Python 3 入門」はターミナルを使って進めているのですが、プログラムの保存の仕方などの説明はなく、書いては消しという使い方しかできなかったので、「みんなのAI講座 ゼロからPythonで学ぶ人工知能と機械学習」で使っていたPyCharmの方がプログラムの保存もできるしインタフェースがとっつきやすいのでこっちを使っています。

なんとなくですが、「知識ゼロから○○を作る」みたいなタイプの学習プログラムって、初学者にはわからない専門用語を多用していたり、なぜそうなるのか、という説明をすっ飛ばしていたりすることが多いので、プログラミングを本当に一から勉強するなら、その説明に特化したもののほうがいいのかなという気がします。

#余談2
続編を書きました。今度は二桁の足し算です。
[プログラミング未経験者がpythonで今度は二桁以上の子ども用計算ドリルを作る](https://qiita.com/sotogawa/items/ad9098bc229af3376dfa)