対象者
前回の記事の続きです。
本記事では前回の記事のgraph_plot.pyを元に話題をPythonのリストや条件分岐、ループ処理に移していきます。
全部すっ飛ばしても大丈夫な人は不連続な関数を描こうへどうぞ。
目次
Pythonのリスト
前回の記事では、Pythonには配列が存在しないと述べました。
その代わり、Pythonにはリスト(List)と呼ばれるデータ構造が標準搭載されています。
今回はそれについて少し触れます。
リストとは
まずリストについて簡単な説明から行います。
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)
リストは配列と似た概念で、次のように用いることができます。
x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(x)
このx
、numpy_calc.pyで定義したx
と出力結果が同じになっていることに気づきましたか?
つまり、リストと配列は似たデータ構造であることがわかります。
その最大の違いはデータサイズが可変であることです。
x.append(10)
print(x)
10が追加されましたね。
このappend
メソッドはリスト型のデータ構造が持っている関数1で、要素を追加するためのものです。
もちろんnumpy配列で同じことをしようとしてもエラーが出るだけです。
numpyでは一度定義した配列のサイズを変更するには(内部処理で)どこかに改めてコピーする必要があります。
その点リストは予め大きめにメモリを確保することで、要素を追加する際にかかるメモリ確保の時間を節約することができます。
要するにそれぞれメリット/デメリットがあるということです。
他にもリストには要素を削除する関数などもあります。
また、リストにはnumpy配列のブロードキャスト機能はありません。
#print(x + 3)
# TypeErrorが起こる
ただし次のようなことはできます。
print(x + [3])
print(x * 3)
リスト同士の足し算であれば要素の追加になります。
リストと自然数の掛け算はリストそのものを数値の数だけ繰り返したリストとなります。2
その他の演算はTypeErrorが出てできません
さらに、numpy配列とリストは相互に変換もできます。
x = np.array(x)
print(x)
x = list(x)
print(x)
以上で本記事で必要なリストの知識の紹介はおしまいです。
要約すると
- リストはデータサイズが可変の配列のようなもの
- numpyとリストは相互に変換できる
だけ知っておけば本記事ではOKです。
条件分岐とループ処理
条件分岐とは**「ある条件を満たすなら〇〇、そうでなければ××」という処理のことです。
(この処理は人間の意思決定とも似ているところがありますね)
ループ処理とは「ある条件を満たすまで同じ処理を繰り返す」**という処理のことです。
(これは機械っぽい処理ですね)
では具体的にどのようにコードを書くのか、実際にやっていきましょう。
if
文
Pythonの条件分岐と言えばif
文だけ3です。
C言語などをやったことのある人は「え、switch
とかないの?」と思うかもしれませんね。
Pythonではswitch
文の類はありません。いちいち書かなければいけません。面倒ですね。
初めての人にとっては覚えることが減ってラッキーかもしれません笑。
とりあえず、この条件分岐を使ってみましょう。
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より小さいです。")
この二つのif
文は全く同じ動作をします。
上の方はif else
のみ、下の方はif elif else
という文法を使って書いています。
見ての通りですが、if
文の中にif
文を書く(ネストする、といいます)ことができます。
もちろんもっとたくさんネストさせることも可能ですが、読みにくくなってしまうのでできるだけネストは少なめにするよう心がけましょう。
つまり下の方が読みやすいコードということですね。
文法まとめ
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
文から。
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("この文は出力されません。")
さて、上の方のfor
文は0〜9まで、縦に並べて出力されます。
下の方は6までが横に並べて出力されて、「6 > 5」「ループを抜けます」と出力されて終わるはずです。
(ついでなのでいくつか紹介していない関数などを使っています。気になった方はググってみましょう。)
for
文は文法を見た方がわかりやすいですね。
for (変数名) in (配列やリストなど):
(繰り返したい処理をここに書く)
else:
(ループが正常に終了した場合の処理やループが1度も実行されなかった時に行いたい処理をここに書く)
range
関数は指定された数までのリストのようなもの4を返します。
break
文はループを強制的に抜けるためのものです。
for
文では、**(配列やリストなど)で指定された数値が順番に(変数名)**に代入されて処理が行われます。
for_sample.pyの一つ目のループでは
- 変数
i
に0
が代入される -
print
関数によって0
が出力される - 変数
i
に1
が代入される -
print
関数によって1
が出力される - ...
のように処理が進みます。
二つ目のループはfor else
文の紹介のために書きました。
Pythonでは他の言語には滅多にない、ループ処理にelse
を使うことができます。
else
の中には、ループが正常に終了した時に行って欲しい処理を書きます。
つまり、break
文でループを強制的に終了した場合はその処理が行われません。
あまり積極的に使うことはありませんが、ネストされたfor
文を全て抜けたい時などによりスッキリしたコードを書くことができます。
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
continue
文は以降の処理をスルーして次のループに移るためのものです。
while
文
次はwhile
文です。
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("この文は出力されません")
このwhile_sample.pyはそれぞれfor_sample.pyと等価なループ処理となっていることが確認できると思います。
while
文の文法は次の通りです。
while (条件式):
(繰り返したい処理をここに書く)
else:
(ループが正常に終了した場合の処理やループが1度も実行されなかった時に行いたい処理をここに書く)
while
文の特徴は条件式が満たされる限りループ処理をすることですね。
for
文では予めループする回数が決まるのに対して、while
文は不定なことがあるのが大きな違いです。
これが個人的にwhile
文を使わないようにして欲しい理由につながります。
while_sample.pyの一つ目のループを以下のように変更して実行すると大量の0
が出力され続けます。
<<注意>>
実行する場合は止め方をよく頭に入れておいてください。
i = 0
while i < 10:
print(i)
#i += 1
<<止め方>>
上部の■をクリックすれば止まります。
jupyter notebookでは簡単な操作で実行を中断できるので便利ですね〜
変数i
の値が変わらないためにいつまでも条件式i < 10
が満たされ続けてしまい、処理が無限に行われてしまうためこのようなことが発生します。
このような状況を無限ループと言います。
今回はprint
関数で出力しているため異常が分かり易いですが、例えばこれが1000行のコードの中で、print
関数などによる出力もない状態で発生したらどうでしょう?
一生懸命書いたコードがなぜかエラーで動かない!
ではなく
一生懸命書いたコードがエラーもないのに反応しない!
という状況になります。
初心者の頃は無限ループなんてそう簡単には気づけないためずーっと頭を悩ませることになりかねません。
以上の理由から、while
文でなければ書けないようなループはまずないでしょうから、基本的にfor
文を使用するようにしましょう。
不連続な関数を描こう
長くなりましたが、条件分岐とループ処理を用いて不連続な関数を書いてみましょう!
graph_plot.pyの関数f
の部分を次のように変更しましょう。
# リストを用いたバージョン
def f(x):
result = []
for i in x:
if i < 0:
result.append(i + 3)
else:
result.append(i ** 2)
return result
# 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
めんどくさい?
めんどくさいって?間違いない。
ということでスマートな書き方も紹介しておきます。
def f(x):
return np.where(x < 0, x + 3, x ** 2)
numpyのwhere
関数は俗にいう三項演算子というやつです。
詳しくはググりましょう。
ちなみに頑張れば標準の書き方でもそれなりにスマートになります。
def f(x):
return [i + 3 if i < 0 else i ** 2 for i in x]
リスト内包表記とif
文の三項演算子を組み合わせたものですね。
[(処理) for (変数名) in (配列やリストなど)]
(条件式が真の時の処理) if (条件式) else (条件式が偽の時の処理)
とはいえ読みにくいし複雑です。
パッケージの偉大さがよくわかります。