web-dev-qa-db-ja.com

なぜPythonは関数型プログラミングにはあまり適していませんか?

関数型プログラミングはPythonでできるといつも思っていました。したがって、Pythonが this の質問であまり言及されていなかったことに驚きました。そして、言及されたとき、それは通常あまり肯定的ではありませんでした。ただし、これには多くの理由が示されていません(パターンマッチングの欠如と代数的データ型が言及されました)。だから私の質問は次のとおりです:なぜPythonは関数型プログラミングにあまり適していませんか?パターンマッチングと代数データ型の欠如よりも多くの理由がありますか?または、これらの概念は関数型プログラミングにとって非常に重要であるため、それらをサポートしていない言語は二次関数型プログラミング言語としてのみ分類できますか? (関数型プログラミングの私の経験はかなり限られていることに注意してください。)

315
David Johnstone

あなたが参照する質問は、どの言語がOOと関数型プログラミングの両方を促進するかを尋ねます。 Pythonは機能しませんpromote機能的プログラミングworksかなり良好です。

最適な引数against Pythonの関数型プログラミングでは、命令型/ OOのユースケースはGuidoによって慎重に検討されますが、関数型プログラミングのユースケースはそうではありません。命令型Pythonを書くとき、私が知っている最も美しい言語の1つです。機能的なPythonを書くと、 BDFL を持たない平均的な言語と同じくらいasくて不快になります。

機能のプログラミングを促進する言語に変更したり、OO Pythonに変更した場合に比べて、一生懸命働かなければならないというだけで、それは悪いことではありません。

Pythonで見逃した機能的なものは次のとおりです。


  • パターンマッチングや末尾再帰がないため、基本的なアルゴリズムを命令的に記述する必要があります。 Pythonでは再帰はくて遅いです。
  • リストライブラリが小さく、機能的な辞書がないため、多くのものを自分で作成する必要があります。
  • カリー化や合成の構文がないということは、ポイントフリースタイルが明示的に引数を渡すのと同じくらい句読点でいっぱいであることを意味します。
  • 遅延リストの代わりにイテレータを使用すると、効率が必要か永続性が必要かを知り、永続性が必要な場合はlistへの呼び出しを分散させる必要があります。 (イテレータは1回限り)
  • Pythonの単純な命令型構文とその単純なLL1パーサーは、if-expressionsとlambda-expressionsのより良い構文が基本的に不可能であることを意味します。グイドはこの方法が好きで、私は彼が正しいと思います。
379

Guidoにはこれについての良い説明があります here 。最も重要な部分は次のとおりです。

私はPythonが関数型言語の影響を強く受けていると考えたことはありません。私はCやALGOL 68などの命令型言語にはるかに精通しており、関数をファーストクラスのオブジェクトとして作成しましたが、Pythonを関数型プログラミング言語とは見なしませんでした。ただし、以前は、ユーザーがリストと関数を使用してさらに多くのことをしたいことは明らかでした。

...

また、Pythonを関数型言語として想定していなかったとしても、クロージャーの導入は他の多くの高度なプログラミング機能の開発に役立ちました。たとえば、新しいスタイルのクラス、デコレータ、およびその他の最新機能の特定の側面は、この機能に依存しています。

最後に、長年にわたって多くの関数型プログラミング機能が導入されてきましたが、Pythonには「実際の」関数型プログラミング言語に見られる特定の機能がまだありません。たとえば、Pythonは、特定の種類の最適化(テール再帰など)を実行しません。一般に、Pythonは非常に動的な性質を持っているため、HaskellやMLなどの関数型言語で知られているようなコンパイル時の最適化を行うことは不可能です。そしてそれは大丈夫です。

これから2つのことを引き出します。

  1. この言語の作成者は、Pythonを関数型言語とは実際には考えていません。そのため、「機能的に」機能を見ることができますが、機能的なものはほとんど見られません。
  2. Pythonの動的な性質により、他の関数型言語で見られる最適化の一部が抑制されます。確かに、LISPはPythonと同じように(より動的でないとしても)動的なので、これは部分的な説明にすぎません。
98
Jason Baker

Schemeには代数的なデータ型やパターンマッチングはありませんが、確かに関数型言語です。関数型プログラミングの観点からPythonについての迷惑なこと:

  1. 障害のあるラムダ。 Lambdasには式のみを含めることができ、式のコンテキストですべてを簡単に行うことはできないため、「オンザフライ」で定義できる関数が制限されます。

  2. Ifは式ではなくステートメントです。これは、とりわけ、その内部にIfを持つラムダを持つことができないことを意味します。 (これはPython 2.5の3項で修正されますが、見苦しくなります。)

  3. Guidoは map、filter、reduce を時々脅かす

一方、pythonには字句のクロージャー、ラムダ、およびリストの内包表記があります(Guidoが認めているかどうかにかかわらず、実際には「機能的な」概念です)。 Pythonで多くの「関数型」プログラミングを行っていますが、理想的とは言い難いです。

50
Jacob B

私はPythonを「機能的」と呼ぶことは決してありませんが、Pythonでプログラムを作成するたびに、コードは常にほぼ完全に機能するようになります。

確かに、それは主に非常に素晴らしいリストの理解によるものです。したがって、関数プログラミング言語としてPythonを必ずしも提案するわけではありませんが、Pythonを使用する人には関数プログラミングを提案します。

21
Konrad Rudolph

「関数型」への答えから取ったコードの一部でデモンストレーションさせてください Python question SO

Python:

def grandKids(generation, kidsFunc, val):
  layer = [val]
  for i in xrange(generation):
    layer = itertools.chain.from_iterable(itertools.imap(kidsFunc, layer))
  return layer

ハスケル:

grandKids generation kidsFunc val =
  iterate (concatMap kidsFunc) [val] !! generation

ここでの主な違いは、Haskellの標準ライブラリには関数型プログラミングに役立つ関数があることです。この場合、iterateconcat、および(!!)

16
yairchu

この質問(および答え)で本当に重要なことの1つは、次のとおりです。地獄は関数型プログラミングであり、その最も重要なプロパティは何ですか。私の意見を述べようと思います:

関数型プログラミングは、ホワイトボードに数学を書くことによく似ています。ホワイトボードに方程式を書くとき、実行順序については考えません。 (通常)突然変異はありません。あなたは翌日に戻ってそれを見ていない、そしてあなたが再び計算をするとき、あなたは異なる結果を得る(あるいは、あなたが新鮮なコーヒーを飲んだなら:))。基本的に、ボード上のものはそこにあり、答えは物事を書き始めたときにすでにそこにありました、あなたはそれがまだ何であるかをまだ理解していません。

関数型プログラミングは非常に似ています。物事を変えずに、方程式(またはこの場合は「プログラム」)を評価して、答えを見つけます。プログラムは未変更のままです。データについても同じです。

以下を関数型プログラミングの最も重要な機能としてランク付けします。a)参照の透過性-同じステートメントを別の時間と場所で評価し、同じ変数値で評価する場合、それは同じことを意味します。 b)副作用なし-ホワイトボードをどれだけ長く見つめても、他の人が他のホワイトボードを見ている方程式が誤って変わることはありません。 c)関数も値です。これは、他の変数とともに、または他の変数に適用できます。 d)関数の構成、h = g・fを実行して、g(f(..))の呼び出しと同等の新しい関数h(..)を定義できます。

このリストは私の優先順位であるため、参照の透明性が最も重要であり、副作用はありません。

さて、pythonを調べて、言語とライブラリがこれらの側面をどの程度サポートし、保証しているのかを確認したら、あなたは自分の質問に答えることができます。

14
xeno

Pythonはほとんど機能的な言語です。それは「機能的なライト」です。

追加の機能があるため、一部のユーザーには十分ではありません。

また、いくつかの機能が不足しているため、一部の機能には十分ではありません。

欠落している機能は比較的簡単に記述できます。 PythonのFPで this のような投稿をチェックしてください。

11
S.Lott

上記以外の別の理由は、組み込み型の多くの組み込み関数とメソッドがオブジェクトを変更しますが、変更されたオブジェクトを返さないことです。機能的なコードはより簡潔で簡潔になります。たとえば、some_list.append(some_object)がsome_objectを追加したsome_listを返した場合。

8
Dvd Avins

他の回答に加えて、Pythonおよび他のほとんどのマルチパラダイム言語が真の関数型プログラミングにあまり適していない理由の1つは、コンパイラ/仮想マシン/ランタイムが関数最適化をサポートしていないためです。この種の最適化は、コンパイラが数学的規則を理解することにより達成されます。たとえば、多くのプログラミング言語はmap関数またはメソッドをサポートしています。これはかなり標準的な関数で、関数を1つの引数として受け取り、2番目の引数としてiterableを受け取り、その関数をiterableの各要素に適用します。

とにかく、map( foo() , x ) * map( foo(), y )map( foo(), x * y )と同じであることがわかります。前者は2つのコピーを実行し、後者は1つのコピーを実行するため、後者のケースは実際には前者よりも高速です。

より優れた関数型言語は、これらの数学的に基づいた関係を認識し、最適化を自動的に実行します。機能的パラダイム専用ではない言語は、おそらく最適化されません。

4
user2074102