
Pythonに関する記事で、 Xronosユーザーが関数型プログラミング(FP)について話すように頼みました。 私はかつてLispでかなり忙しかったので、それについて少しお話ししたいと思います。 私たちは純粋なFPについて話していないことをすぐに言いたいです。 例としてPython言語を使用した、より単純で適用可能な手法について説明します。
私は純粋なAFについて話さない理由をすぐに言わなければなりません。 純粋なFPは、計算状態がなく、メモリが変更できないことを意味します(サブルーチン実行の副作用がないこと)。 大まかに言って、純粋なFPのプログラム全体は1つの大きな公式です。 一連の計算、入出力、可変状態などの純粋に必須の概念は、 Haskellの 僧 monなど、さまざまな美しい方法で実装されています。 さらに、さまざまな高次の概念がFIに含まれています。
- パターンマッチング -関数のオーバーロードのように、より思慮深く柔軟性が高い
- 継続(継続) -計算を停止し、別の時点で続行する機能(つまり、計算の状態を「保持」してから再開する)。
yield
操作で見られる継続の種類 - 遅延計算 -このモデルでは、大まかに言って、関数の引数は関数が入力されたときではなく、本当に必要なときにのみ計算されます
- 代数的データ型、再帰的データ型、自動型推論など など
私はこれに集中しません。a)私自身はこれらの概念を実際に適用しなかった(またはそれらを限定した範囲で適用した)、b)「通常の」言語の「通常の」プログラマーへのそれらの適用性はまだ利用できない したがって、より単純な概念から始めます。
機能
FPでは、すべてが関数に結び付けられているため、 関数は第1種のオブジェクト(ファーストクラスオブジェクト)でなければなりません 。 これは、関数(匿名関数)を作成したり、変数に割り当てたり、関数に渡したり、関数から返すことができることを意味します。 新しく作成された関数には、 closureプロパティが必要です-つまり 新しい関数は、周囲のコンテキスト(ローカルスコープとグローバルスコープの両方の宣言された変数)をキャプチャする必要があります。 簡単な例(この投稿の完全なコードは、投稿の下部にあるリンクから見つけることができます):
#エンコード:utf-8 def get_writer(タイプ、パラメーター): #HTML出力 def html_writer(ファイル名): f = open(ファイル名+ '。' +タイプ、 'w'); f.write( "" " <html> <head> <title>%s </ title> </ head> <本体> <h1>こんにちは</ h1> </ body> "" "%params ['title']) f.close() #PLAIN TEXTへのデータ出力 def text_writer(ファイル名): f = open(ファイル名+ '。' +タイプ、 'w'); f.write( "" " %s =================================== こんにちは "" ") f.close() #要求されたデータ型を定義し、対応する関数を返す タイプ== 'html'の場合: html_writerを返す elif type == 'txt': text_writerを返します params = {'title': 'Header'} #HTMLへの出力 f = get_writer( 'html'、params) f( 'file1') #PLAIN TEXTへの出力 f = get_writer( 'txt'、params) f( 'file2')
html_writer
引数(
type
および
params
)は
html_writer
および
text_writer
内で使用されることに注意してください。 これはどのようにできますか? 結局、
get_writer
から戻った後
get_writer
彼女の議論は理論的には存在しなくなるのでしょうか? これがクロージャーの本質です。ある関数が別の関数を返す場合、いわゆるコンテキストが後者に追加されます -呼び出し時のすべての利用可能な変数(ローカル、グローバル、引数)の値。 したがって、関数から関数を返すときは、関数(タウトロジーでは申し訳ありません)だけでなく、 クロージャ (関数+コンテキスト)を返します。
高階関数
次に、そのような例を想像してください。 特定の機能のグラフ作成プログラムを作成しています。 そのような関数をいくつか定義します。
#エンコード:utf-8 インポート数学 #y = k * x + b def get_linear(k、b): ラムダxを返す:k * x + b #y = k * x ^ 2 + b def get_sqr(k、b): return lambda x:k * x ** 2 + b #y = A * sin(k * x + phi) def get_sin(振幅、ファイ、k): return lambda x:振幅* math.sin(math.radians(k * x + phi)) #y = A * e ^(k * x) def get_exp(振幅、k、b): return lambda x:振幅* math.exp(k * x + b)
これらは単純な関数です。 それらをどのように使用できますか:
#y = 5 * sin(0.3 * x + 30) y = get_sin(5、30、0.3) #y(90)= 4.19 yを印刷(90) 印刷する #0から180までの間隔にyを適用した結果 print [y(x)for x in range(0、180)]
しかし、ご覧のとおり、これらの各関数はX軸に沿って関数をシフトする操作をサポートしていますが、これは別の関数であり、選択することができます! 同様に、XとYに沿ったスケーリング関数を区別できます。
defシフター(func、b): return lambda x:func(x + b) def x_scaler(関数、k): return lambda x:func(k * x) def y_scaler(関数、A): return lambda x:A * func(x) def結合(func1、func2): return lambda x:func2(func1(x))
shifter
、
x_scaler
、
y_scaler
、
combine
は高次関数です。 これらは、スカラー引数だけでなく、他の関数も受け入れ、それらの動作を変更します。
Combine
は、ある機能を別の機能に適用できる非常に便利な一般機能です。 これで、以前の関数を次のように書き換えることができます。
def identity(x): xを返す def sqr(x): リターンx ** 2
もうおもしろい。 2つの関数の名前を変更し、2つを完全に削除しました。 名前を変更した理由 実際、スケーリングや転送のような「殻」を取り除いたため、さらに一般的な機能が得られました。 これらの最初は
identity
と呼ばれ
identity
-アイデンティティ関数-FPで非常に重要な概念。 非常に重要ですが、非常にシンプルです-その引数とすべてを返します。 2番目の関数は、引数を単純に2乗します。 さて、最初の例の関数で説明できる構成は、単純な関数と高次の関数を組み合わせることで取得できます-主なことは、正しい順序でそれらを組み合わせることです。 正しい順序でそれが何を意味するかを示すために、この表現を引用します。
y = x_scaler(シフター(x_scaler(sqr、5)、6)、7)
結果として得られる関数は何ですか? まず、引数に適用されます... no、
x_scaler(5)
および
sqr
ではなく、最も外部の
x_scaler
です。 それから
shifter
。 次に内部
x_scaler
。 それから
sqr
。 頭の中で少しひねり、少し慣れる必要があります。 順序は次のとおりです。最も外部の修飾子が最初に適用されます。 結果は、次の関数と完全に類似しています。
def y(x): return sqr(((x * 7)+ 6)* 5)
唯一の違いは、実際にこの関数を分割して手動で作成したことです。 では、y = 5 * sin(0.3 * x + 30)と書いて修正してみましょう。
#最新のものはy_scaler(5)を適用する必要があります y = y_scaler(math.sin、5) #最後から2番目-角度をラジアンに変換 y =結合(math.radians、y) #さらに-シフター(30) y =シフター(y、30) #最後に-x_scaler(0.3) y = x_scaler(y、0.3)
明らかに、結果はコンビネータなしの例に完全に類似しています。
そして最後のフェイント機能
組み合わせのスキルを身に付けたので、たとえば、ある関数の変調器を別の関数を使用して簡単に記述します。
def変調(mod、src): return lambda x:mod(x)* src(x)
これで、減衰調和振動を説明できます。
#y1 = 5 * sin(15 * x + 30)-元の関数 y1 = \ x_scaler( シフター( 組み合わせる( math.radians、 y_scaler( math.sin、 5))、 30) 15) #y2 = exp(-0.01 * x)-変調関数 y2 = x_scaler(math.exp、-0.01) y =変調(y2、y1) print [y(x)for x in range(0、180)]
私の意見では、悪くない:

これで、おそらく、投稿は終了します。 次回は、リスト、map-reduceテクニック、およびこのテクニックの構文糖としてのリスト内包表記があり、それらすべてがコードで考えをより明確に表現するのに役立ちます 。
この投稿のコード: 1 2 3 4
更新:理由 カルマが十分にあるので、このトピックをPythonブログに転送することにしました