Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
0
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

JavaおじさんがPythonを使えるようになるまでの全記録(その8)

はじめに

こんにちは、Javaおじさんです。

うん、間が空くのよくない。
ちゃんと毎日(土日除く)更新しないとね。

環境構築自体は一応継続しているけど、段々わかんなくなってきたのでちょっと中断。

オブジェクトとしての関数

引き続きテキストの5章を読み進めている。

やっとこ高階関数とmapが出てきた...

ふむふむ、高階関数=関数を引数に取る関数だ、と。

Pythonの場合、(ここまで見てきた範囲だと)型が曖昧だから、高階関数を考える場合でも「引数が何個あるか」しか縛る要素がない。
なので結構いろんな関数を柔軟に高階関数の引数として含めることができるということね。
このあたり、JavaやらC#やらといった静的型付け言語だとなかなか難しいところかなー。

サンプル一部抜粋.py
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のコマンドをパイプでつないでるようなイメージが自分の中でできあがりつつあるんだけども。

でもこれ柔軟なのはいいけど諸刃の剣のような気もする。
本来意図した以外のデータ型が入ってきたらどうするの、とかさ。

たとえばこんな風に書くと

データ型がおかしい場合.py
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'

こうならないようにするためには、たぶんこうする必要があるのかな。

データ型を事前に合わせる.py
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にList以外を使用.py
#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を使用.py
#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や高階関数で使えますよっていうことよね。
だからこんなコードも書けると。

テキスト抜粋.py
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]

なるほどなー。

とりあえずこのトピックはここまで。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
0
Help us understand the problem. What are the problem?