初めてのPython ~コーディング編2~

# 対象者
[前回の記事](https://qiita.com/kuroitu/items/8542b88d486d1dbf92a5)の続きです。
本記事では前回の記事の[graph_plot.py](https://qiita.com/kuroitu/items/8542b88d486d1dbf92a5#グラフを描く)を元に話題をPythonのリストや条件分岐、ループ処理に移していきます。
全部すっ飛ばしても大丈夫な人は[不連続な関数を描こう](#不連続な関数を描こう)へどうぞ。


# 目次
 - [Pythonのリスト](#pythonのリスト)
 - [条件分岐とループ処理](#条件分岐とループ処理)
    - [if文](#if文)
    - [for文とwhile文](#for文とwhile文)
      - [for文](#for文)
      - [while文](#while文)
 - [不連続な関数を描こう](#不連続な関数を描こう)
    - [めんどくさい？](#めんどくさい)


# Pythonのリスト
[前回の記事](https://qiita.com/kuroitu/items/8542b88d486d1dbf92a5#numpyの使い方)では、Pythonには配列が存在しないと述べました。
その代わり、Pythonにはリスト(List)と呼ばれるデータ構造が標準搭載されています。
今回はそれについて少し触れます。

## リストとは
まずリストについて簡単な説明から行います。

<details>
<summary>list_sample.py</summary>
<div>

```list_sample.py
x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(x)

x.append(10)
print(x)

#print(x + 3)
# TypeErrorが起こる

print(x + [3])
print(x * 3)

x = np.array(x)
print(x)
x = list(x)
print(x)
```
</div></details>

リストは配列と似た概念で、次のように用いることができます。

```list_sample.py
x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(x)
```

この`x`、[numpy_calc.py](https://qiita.com/kuroitu/items/8542b88d486d1dbf92a5#numpyの使い方)で定義した`x`と出力結果が同じになっていることに気づきましたか？
つまり、リストと配列は似たデータ構造であることがわかります。
その最大の違いは**データサイズが可変**であることです。

```list_sample.py
x.append(10)
print(x)
```

10が追加されましたね。
この`append`メソッドはリスト型のデータ構造が持っている関数[^1]で、要素を追加するためのものです。
もちろんnumpy配列で同じことをしようとしてもエラーが出るだけです。
numpyでは一度定義した配列のサイズを変更するには(内部処理で)どこかに改めてコピーする必要があります。
その点リストは予め大きめにメモリを確保することで、要素を追加する際にかかるメモリ確保の時間を節約することができます。
**要するにそれぞれメリット/デメリットがあるということです。**
他にもリストには要素を削除する関数などもあります。
また、リストにはnumpy配列のブロードキャスト機能はありません。

```list_sample.py
#print(x + 3)
# TypeErrorが起こる
```

ただし次のようなことはできます。

```list_sample.py
print(x + [3])
print(x * 3)
```

リスト同士の足し算であれば要素の追加になります。
リストと自然数の掛け算はリストそのものを数値の数だけ繰り返したリストとなります。[^2]
**その他の演算はTypeErrorが出てできません**
さらに、numpy配列とリストは相互に変換もできます。

```list_sample.py
x = np.array(x)
print(x)
x = list(x)
print(x)
```

以上で本記事で必要なリストの知識の紹介はおしまいです。
要約すると

 - リストはデータサイズが可変の配列のようなもの
 - numpyとリストは相互に変換できる

だけ知っておけば本記事ではOKです。


# 条件分岐とループ処理
条件分岐とは**「ある条件を満たすなら〇〇、そうでなければ××」**という処理のことです。
(この処理は人間の意思決定とも似ているところがありますね)
ループ処理とは**「ある条件を満たすまで同じ処理を繰り返す」**という処理のことです。
(これは機械っぽい処理ですね)

では具体的にどのようにコードを書くのか、実際にやっていきましょう。

## `if`文
Pythonの条件分岐と言えば`if`文だけ[^3]です。
C言語などをやったことのある人は「え、`switch`とかないの?」と思うかもしれませんね。
Pythonでは`switch`文の類はありません。いちいち書かなければいけません。~~面倒ですね。~~
初めての人にとっては覚えることが減ってラッキーかもしれません笑。
とりあえず、この条件分岐を使ってみましょう。

<details>
<summary>if_sample.py</summary>
<div>

```if_sample.py
x = 5
if x > 0:
    # 条件式x > 0が真ならばここの処理
    print("xは0より大きいです。")
else:
    # 条件式x > 0が偽ならばここの処理
    # つまりx <= 0ならばここの処理です
    if x == 0:
        # 条件式x == 0が真ならばここの処理
        # <note>
        # ==は式の両辺が等しいか、という条件式です
        # 逆に、式の両辺が等しくないか、という演算は!=です
        print("xは0です。")
    else:
        # 条件式x == 0が偽ならばここの処理
        # つまり、x != 0ならばここの処理です
        print("xは0より小さいです。")

if x > 0:
    print("xは0より大きいです。")
elif x == 0:
    print("xは0です。")
else:
    print("xは0より小さいです。")
```
</div></details>

この二つの`if`文は全く同じ動作をします。
上の方は`if else`のみ、下の方は`if elif else`という文法を使って書いています。
見ての通りですが、`if`文の中に`if`文を書く(**ネストする**、といいます)ことができます。
もちろんもっとたくさんネストさせることも可能ですが、読みにくくなってしまうので**できるだけネストは少なめにする**よう心がけましょう。
つまり下の方が読みやすいコードということですね。

文法まとめ

```if_grammer_summary.py
if (条件式1):
    # (条件式1)が真(True)ならここに記述された処理を実行
#-----ここから-----
elif (条件式2):
    # (条件式1)が偽(False)で(条件式2)が真(True)ならここに記述された処理を実行
elif (条件式3):
...
else:
    # 全ての条件式が偽(False)ならここに記述された処理を実行
#-----ここまで-----
# 全て省略可能です。つまり最初のifだけでもOK。
```

## `for`文と`while`文
続いてループ処理です。
Pythonのループ処理には`for`と`while`の二つがあります。
二つある理由はたまに使い分けたいことがあるからですが、`for`文を`while`文に、`while`文を`for`文に、等価な処理を行うように変換できますのでお好みで...と言いたいですが、**基本的に`for`文を使うことを個人的にオススメします。**
理由は後ほど...

### `for`文
まずは`for`文から。

<details>
<summary>for_sample.py</summary>
<div>

```for_sample.py
for i in range(10):
    print(i)
print("----------")

for i in range(1, 10):
    print(i, end=" ")
    if i > 5:
        print("\n{} > 5".format(i))
        print("ループを抜けます")
        break
else:
    print("この文は出力されません。")
```
</div></details>

さて、上の方の`for`文は0〜9まで、縦に並べて出力されます。
下の方は6までが横に並べて出力されて、「6 > 5」「ループを抜けます」と出力されて終わるはずです。
(ついでなのでいくつか紹介していない関数などを使っています。気になった方はググってみましょう。)

`for`文は文法を見た方がわかりやすいですね。

```for_grammer_summary.py
for (変数名) in (配列やリストなど):
    (繰り返したい処理をここに書く)
else:
    (ループが正常に終了した場合の処理やループが１度も実行されなかった時に行いたい処理をここに書く)
```

`range`関数は指定された数までのリストのようなもの[^5]を返します。
`break`文はループを強制的に抜けるためのものです。
`for`文では、**(配列やリストなど)**で指定された数値が順番に**(変数名)**に代入されて処理が行われます。

for_sample.pyの一つ目のループでは

 - 変数`i`に`0`が代入される
 - `print`関数によって`0`が出力される
 - 変数`i`に`1`が代入される
 - `print`関数によって`1`が出力される
 - ...

のように処理が進みます。

二つ目のループは`for else`文の紹介のために書きました。
Pythonでは他の言語には滅多にない、ループ処理に`else`を使うことができます。
`else`の中には、ループが正常に終了した時に行って欲しい処理を書きます。
つまり、**`break`文でループを強制的に終了した場合はその処理が行われません。**
あまり積極的に使うことはありませんが、ネストされた`for`文を全て抜けたい時などによりスッキリしたコードを書くことができます。

<details>
<summary>nested_for.py</summary>
<div>

```nested_for.py
# elseあり
for i in range(10):
    for j in range(10):
        print(i, j)
        if i * j > 10:
            # i = 2, j = 6でここに入ります
            break
    else:
        continue
    break

# elseなし
flag = False
for i in range(10):
    for j in range(10):
        print(i, j)
        if i * j > 10:
            # i = 2, j = 6でここに入ります
            flag = True
            break
    if flag:
        break
```
</div></details>

`continue`文は以降の処理をスルーして次のループに移るためのものです。

### `while`文
次は`while`文です。

<details>
<summary>while_sample.py</summary>
<div>

```while_sample.py
i = 0
while i < 10:
    print(i)
    i += 1
print("----------")

i = 0
while i < 10:
    print(i, end=" ")
    if i > 5:
        print("\n{} > 5".format(i))
        break
    i += 1
else:
    print("この文は出力されません")
```
</div></details>

このwhile_sample.pyはそれぞれfor_sample.pyと等価なループ処理となっていることが確認できると思います。
`while`文の文法は次の通りです。

```while_grammer_summary.py
while (条件式):
    (繰り返したい処理をここに書く)
else:
    (ループが正常に終了した場合の処理やループが１度も実行されなかった時に行いたい処理をここに書く)
```

`while`文の特徴は**条件式が満たされる限りループ処理をする**ことですね。
**`for`文では予めループする回数が決まるのに対して、`while`文は不定なことがあるのが大きな違いです。**
これが個人的に`while`文を使わないようにして欲しい理由につながります。

while_sample.pyの一つ目のループを以下のように変更して実行すると大量の`0`が出力され続けます。
**<<注意>>**
**実行する場合は止め方をよく頭に入れておいてください。**

```while_sample.py
i = 0
while i < 10:
    print(i)
    #i += 1
```

**<<止め方>>**
上部の■をクリックすれば止まります。
jupyter notebookでは簡単な操作で実行を中断できるので便利ですね〜
![while_error.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/31654607-f735-aa4f-5f8b-7b7414f52e94.gif)

変数`i`の値が変わらないためにいつまでも条件式`i < 10`が満たされ続けてしまい、処理が無限に行われてしまうためこのようなことが発生します。
このような状況を**無限ループ**と言います。
今回は`print`関数で出力しているため異常が分かり易いですが、例えばこれが1000行のコードの中で、`print`関数などによる出力もない状態で発生したらどうでしょう？

一生懸命書いたコードがなぜかエラーで動かない！
**ではなく**
一生懸命書いたコードがエラーもないのに反応しない！

という状況になります。
初心者の頃は無限ループなんてそう簡単には気づけないためずーっと頭を悩ませることになりかねません。
以上の理由から、`while`文でなければ書けないようなループはまずないでしょうから、**基本的に`for`文を使用する**ようにしましょう。


# 不連続な関数を描こう
長くなりましたが、条件分岐とループ処理を用いて不連続な関数を書いてみましょう！
[graph_plot.py](https://qiita.com/kuroitu/items/8542b88d486d1dbf92a5#グラフを描く)の関数`f`の部分を次のように変更しましょう。

```graph_plot.py
# リストを用いたバージョン
def f(x):
    result = []
    for i in x:
        if i < 0:
            result.append(i + 3)
        else:
            result.append(i ** 2)
    return result
```

```graph_plot.py
# numpy配列を用いたバージョン
def f(x):
    result = np.zeros(len(x))
    for i in range(len(x)):
        if x[i] < 0:
            result[i] = x[i] + 3
        else:
            result[i] = x[i] ** 2
    return result
```

こんな感じのグラフがプロットされればOKです。
![discontinuous.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640911/c3101328-47a2-778e-24af-8e112d6f074a.png)

## めんどくさい？
めんどくさいって？間違いない。
ということでスマートな書き方も紹介しておきます。

```graph_plot.py
def f(x):
    return np.where(x < 0, x + 3, x ** 2)
```

numpyの`where`関数は俗にいう三項演算子というやつです。
詳しくはググりましょう。
ちなみに頑張れば標準の書き方でもそれなりにスマートになります。

```graph_plot.py
def f(x):
    return [i + 3 if i < 0 else i ** 2 for i in x]
```

リスト内包表記と`if`文の三項演算子を組み合わせたものですね。

```list_comprehension_grammer.py
[(処理) for (変数名) in (配列やリストなど)]
```

```ternary_operator_grammer.py
(条件式が真の時の処理) if (条件式) else (条件式が偽の時の処理)
```

とはいえ読みにくいし複雑です。
**パッケージの偉大さがよくわかります。**


# 初めてのPythonシリーズ記事

 - [初めてのPython ~環境構築編~](https://qiita.com/kuroitu/items/f632e5c1f7445280fa9a)
 - [初めてのPython ~コーディング編~](https://qiita.com/kuroitu/items/8542b88d486d1dbf92a5)
 - [初めてのPython ~課題編~](https://qiita.com/kuroitu/items/efc2f025012447555c1a)


[^1]: 正確にはリスト型のクラスが持つ`append`メソッドです。
[^2]: 0以下の整数値は空のリストを返します。試してみてください。
[^3]: `try`文は条件分岐と言えなくもないです。
[^4]: その点での左極限と右極限が一致しており、その点での値と極限値が一致する時、「その関数は連続である」といいます。つまり関数が綺麗につながってれば連続です。
[^5]: 実際はイテレータ(ループ処理可能な)オブジェクトを返します。