
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ブログに転送することにしました