Functional Programming

4932 ワード

このノートは廖雪峰から来ました.
関数式プログラミングは抽象度の高いプログラミングモデルであり、純粋な関数式プログラミング言語で書かれた関数には変数がないため、任意の関数は、入力が確定すれば、出力が確定し、このような純粋な関数は副作用がないと呼ばれています.変数を使用できるプログラム設計言語は,関数内部の変数状態が不確定であるため,同じ入力で異なる出力が得られる可能性があるため,この関数には副作用がある.
関数式プログラミングの特徴の一つは、関数自体をパラメータとして別の関数に転送することを許可し、また1つの関数を返すことを許可することです.
Pythonは関数式プログラミングを一部サポートしています.Pythonは変数の使用を許可するため、Pythonは純粋な関数式プログラミング言語ではありません.
高次関数-higher-order function
関数をパラメータとして伝達する関数を高次関数と呼び,関数式プログラミングとはこのような高度に抽象的なプログラミングパターンを指す.
def add(x, y, f):
  return f(x) + f(y)

add(-5, 6, abs)

map
map()関数は2つのパラメータを受信し、1つは関数であり、1つはIterableであり、mapは入力した関数をシーケンスの各要素に順次作用させ、結果を新しいIteratorとして返す.
myList = [1,2,3]
def f(x):
  return x * x

r = map(f, myList)
print list(r)
---
[1,2,3]

map()が入力する最初のパラメータはf、すなわち関数オブジェクト自体である.結果rはIteratorであり,Iteratorは不活性シーケンスであるためlist()関数によりシーケンス全体を計算してlistを返す
reduce
reduceは1つの関数を1つのシーケンスに作用させる[x 1,x 2,x 3,...]上で、この関数は2つのパラメータを受信しなければなりません.reduceは結果をシーケンスの次の要素と累積計算し続け、その効果はreduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)です.
from functools import reduce
  def fn(x, y):
    return x * 10 + y

print reduce(fn, [1, 3, 5, 7, 9])
---
13579

filter
filter()も関数とシーケンスを受信します.map()とは異なり、filter()は入力された関数を各要素に順次作用させ、戻り値がTrueかFalseかに基づいて要素を保持または破棄するかを決定します.
def is_odd(n): 
  return n % 2 == 1

list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
#   : [1, 5, 9, 15]

フィルタ()という高次関数を使用することがわかります.重要なのは、フィルタ関数を正しく実装することです.filter()関数はIterator、すなわち不活性シーケンスを返すので、filter()に計算結果の完了を強制するにはlist()関数ですべての結果を取得しlist()関数を返す必要があります.
sorted
sorted()関数も高次関数であり、key関数を受信してカスタムソートを実現することもできます.たとえば、絶対値サイズでソートします.
>>> sorted([36, 5, -12, 9, -21], key=abs)
[5, 9, -12, -21, 36]

戻り関数
高次関数はパラメータとして関数を受け入れるほか,結果値として関数を返すこともできる.
def lazy_sum(*args): 
  def sum(): 
    ax = 0 
    for n in args: 
      ax = ax + n 
    return ax 
  return sum

この例では関数lazy_sumには関数sumも定義されており、内部関数sumは外部関数lazy_を参照することができる.sumのパラメータと局所変数、lazy_sumが関数sumを返すと,関連パラメータと変数が返される関数に保存され,この「クローズドパッケージ」と呼ばれるプログラム構造は大きな威力を持つ.
lambda
>>> list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
[1, 4, 9, 16, 25, 36, 49, 64, 81]


キーワードlambdaは匿名関数を表し、コロンの前のxは関数パラメータを表す.
匿名関数には1つの式しかないという制限があります.returnを書く必要はありません.戻り値はその式の結果です.
デコレーション
def now():
  print '1'

ここで、now()関数の機能を強化すると仮定します.たとえば、関数呼び出しの前後にログを自動的に印刷しますが、now()関数の定義を変更することは望ましくありません.コードの実行中に機能を動的に追加する方法を「デコレータ」(Decorator)と呼びます.
本質的にdecoratorは関数を返す高次関数である.したがって、ログを印刷できるdecoratorを定義します.次のように定義できます.
def log(func): 
  def wrapper(*args, **kw): 
    print('call %s():' % func.__name__) 
    return func(*args, **kw) 
  return wrapper

上のlogを見て、decoratorなので、パラメータとして関数を受け入れ、関数を返します.Pythonの@構文を使用して、decoratorを関数の定義に配置します.
@log
def now(): 
  print('1')

now()関数を呼び出すと、now()関数自体だけでなく、now()関数を実行する前にログが印刷されます.
>>> now()
call now():
1

@logをnow()関数の定義に置くと、文:now = log(now)を実行したことに相当する.log()はdecoratorであり、関数を返すため、元のnow()関数は依然として存在するが、現在同名のnow変数が新しい関数を指しているだけで、now()を呼び出して新しい関数、すなわちlog()関数で返されるwrapper()関数を実行する.
decorator自体がパラメータを入力する必要がある場合は、decoratorを返す高次関数を記述する必要があります.書くともっと複雑になります.たとえば、logのテキストをカスタマイズするには、次のようにします.
def log(text): 
  def decorator(func): 
    def wrapper(*args, **kw): 
      print('%s %s():' % (text, func.__name__)) 
      return func(*args, **kw) 
  return wrapper 
return decorator

この3層ネストのdecoratorの使い方は以下の通りです.
@log('execute')
def now(): 
  print('2015-03-25')

実行結果は次のとおりです.
>>> now()
execute now():
2015-3-25

      decorator  ,3          :
>>> now = log('execute')(now)

上記の文を剖析して、まずlog('execute')を実行して、decorator関数を返して、更に返した関数を呼び出して、パラメータはnow関数で、返した値は最終的にwrapper関数です.
以上の2つのdecoratorの定義は問題ありませんが、まだ最後のステップがありません.関数もオブジェクトで、nameなどの属性がありますが、decoratorの装飾を経た関数を見てみると、元の「now」から「wrapper」に変わりました.
>>> now.__name__
'wrapper'

返されるwrapper()関数の名前が「wrapper」なので、元の関数のnameなどの属性をwrapper()関数にコピーする必要があります.そうしないと、関数署名に依存するコードの実行がエラーになります.
wrapperを書く必要はありません.name = func.名前のようなコード、Python内蔵functools.wrapsはこのことをしているので、完全なdecoratorの書き方は以下の通りです.
import functoolsdef 
log(func): 
  @functools.wraps(func) 
  def wrapper(*args, **kw): 
    print('call %s():' % func.__name__) 
    return func(*args, **kw) 
  return wrapper

        decorator:
import functools
def log(text): 
  def decorator(func): 
    @functools.wraps(func)
    def wrapper(*args, **kw): 
      print('%s %s():' % (text, func.__name__)) 
      return func(*args, **kw) 
    return wrapper 
  return decorator