#はじめに
こんにちは、Javaおじさんです。
うん、間が空くのよくない。
ちゃんと毎日(土日除く)更新しないとね。
環境構築自体は一応継続しているけど、段々わかんなくなってきたのでちょっと中断。
#オブジェクトとしての関数
引き続きテキストの5章を読み進めている。
やっとこ高階関数とmapが出てきた...
ふむふむ、高階関数=関数を引数に取る関数だ、と。
Pythonの場合、(ここまで見てきた範囲だと)型が曖昧だから、高階関数を考える場合でも「引数が何個あるか」しか縛る要素がない。
なので結構いろんな関数を柔軟に高階関数の引数として含めることができるということね。
このあたり、JavaやらC#やらといった静的型付け言語だとなかなか難しいところかなー。
def applyToEach(L, f):
"""Lをリスト、fを1つの引数を持つ関数であるものとする。
Lのそれぞれの要素eをf(e)に置き換えてLを更新する"""
for i in range(len(L)):
L[i] = f(L[i])
L = [1, -2, 3.14]
print('L =', L)
applyToEach(L, abs)
print('L =', L)
applyToEach(L, int)
print('L =', L)
実行するとこうなる。
L = [1, -2, 3.14]
L = [1, 2, 3.14]
L = [1, 2, 3]
なんとなくUNIXのコマンドをパイプでつないでるようなイメージが自分の中でできあがりつつあるんだけども。
でもこれ柔軟なのはいいけど諸刃の剣のような気もする。
本来意図した以外のデータ型が入ってきたらどうするの、とかさ。
たとえばこんな風に書くと
def applyToEach(L, f):
"""Lをリスト、fを1つの引数を持つ関数であるものとする。
Lのそれぞれの要素eをf(e)に置き換えてLを更新する"""
for i in range(len(L)):
L[i] = f(L[i])
L = [1, '-2', 3.14]
print('L =', L)
applyToEach(L, abs)
print('L =', L)
実行時に以下のエラーが出る。
Traceback (most recent call last):
File "d:\PythonExercise\TextExercise\5-3\5-3-1.py", line 10, in <module>
applyToEach(L, abs)
File "d:\PythonExercise\TextExercise\5-3\5-3-1.py", line 5, in applyToEach
L[i] = f(L[i])
TypeError: bad operand type for abs(): 'str'
こうならないようにするためには、たぶんこうする必要があるのかな。
def applyToEach(L, f):
"""Lをリスト、fを1つの引数を持つ関数であるものとする。
Lのそれぞれの要素eをf(e)に置き換えてLを更新する"""
for i in range(len(L)):
L[i] = f(L[i])
L = [1, '-2', 3.14]
print('L =', L)
#absの事前条件を満たすためにいったんすべてfloatに変換
applyToEach(L, float)
print('L =', L)
applyToEach(L, abs)
print('L =', L)
実行結果はこうなる。
L = [1, '-2', 3.14]
L = [1.0, -2.0, 3.14]
L = [1.0, 2.0, 3.14]
柔軟だからといってあまり雑なことをすると使えないやつ呼ばわりされるよ、ということよね。
ところでこのapplyToEach関数、Lの内部状態を変えているんだけどこれは副作用があるという判定でいいんだっけ?
#map
さて、mapというのをJavaの感覚で捉えると間違う。
JavaでいうところのMapインターフェースは後で出てくる辞書型というやつなのかな。
mapはあくまでもPythonにビルトインされた高階関数の名称であると。
forループとセットで使うように設計されている。
ほうほう。
前にコメントで教えてもらった例だと必ずしもセットにしなくてもよさそうに見えちゃうけど、実はあれもsumの中でforループ使っているから、ということで納得できるね。
map関数の文法はこんな感じなのか。
map(関数, 関数に与える実引数1(, 関数に与える実引数2, ...))
要するに関数と関数の必要とする個数の実引数のセットを順序型で列挙してやればいいってことだよね。
一応テキストのサンプルでは実引数セットにリストしか使ってないけどタプルやrangeでもよかったりするのかしら。
どれどれ...
#mapにタプルを使用
for num in map(abs, (-1, 2.2, 3.14)):
print(num)
print()
#mapにrangeを使用
for num in map(abs, range(-5, 0)):
print(num)
実行すると...
1
2.2
3.14
5
4
3
2
1
うん、使えるね。
2個以上の実引数リストのサイズが異なる場合はエラーになるのかな...
#mapに不揃いなlistを使用
L1 = [1, 2, 3, 4]
L2 = [100, 99, 98]
for num in map(min, L1, L2):
print(num)
print()
for num in map(max, L1, L2):
print(num)
実行すると...あれ?動いた?
1
2
3
100
99
98
これはつまりあれかな、要素が多い分は捨てられるって解釈でいいのかな。
呼び出しの条件を満たしてないから。...本当?
#ラムダ式
ここでいうラムダ式ってのは文法としてはこうか。
lambda <変数の名前のシークエンス>: <式>
要するにビルトインの関数以外でも自分で自由に関数定義してmapや高階関数で使えますよっていうことよね。
だからこんなコードも書けると。
L = []
for i in map(lambda x, y: x**y, [1, 2, 3, 4], [3, 2, 1, 0]):
L.append(i)
print(L)
で、実行するとこうなる。
[1, 4, 3, 1]
なるほどなー。
とりあえずこのトピックはここまで。