地球人のための関数型プログラミング-関数





Pythonに関する記事で、 Xronosユーザーが関数型プログラミング(FP)について話すように頼みました。 私はかつてLispでかなり忙しかったので、それについて少しお話ししたいと思います。 私たちは純粋なFPについて話していないことをすぐに言いたいです。 例としてPython言語を使用した、より単純で適用可能な手法について説明します。







私は純粋なAFについて話さない理由をすぐに言わなければなりません。 純粋なFPは、計算状態がなく、メモリが変更できないことを意味します(サブルーチン実行の副作用がないこと)。 大まかに言って、純粋なFPのプログラム全体は1つの大きな公式です。 一連の計算、入出力、可変状態などの純粋に必須の概念は、 Haskellの monなど、さまざまな美しい方法で実装されています。 さらに、さまざまな高次の概念がFIに含まれています。







私はこれに集中しません。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ブログに転送することにしました



All Articles