Pythonが関数型プログラミングに適していないのはなぜですか?[閉まっている]


324

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


2
2018- Coconut (Pythonにコンパイルされる関数型プログラミング言語)は、Pythonの関数型プログラミングを強化します。IBMのこのシリーズの記事も参照してくださいpage1 page2 page3
cssyphus

回答:


393

あなたが参照する質問は、オブジェクト指向プログラミングと関数型プログラミングの両方を促進する言語を尋ねます。Pythonはありません促進し、それにもかかわらず、関数型プログラミングを働くかなりよく。

Python での関数型プログラミングに対する最良の議論は、関数型プログラミングのユースケースではなく、Guidoが命令型/ OOのユースケースを慎重に検討することです。私が命令型Pythonを書くとき、それは私が知っている最も美しい言語の1つです。関数型のPythonを書くと、BDFLがない平均的な言語と同じくらい醜くて不愉快になります。

これは悪いことではありません。関数型プログラミングを促進する言語に切り替えるか、OO Pythonの記述に切り替える場合よりも、もっと努力する必要があるということです。

Pythonで見逃している機能的なものを次に示します。


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

5
末尾の再帰が欠落している場合の+1-ループ構造はこれに取って代わりましたが、PythonとSchemeの間でまだ欠けている価値があります。
new123456 2011

5
完全性と構成については素晴らしい答えです。悲しいかな、強力な機能的背景を持つ非常に多くの回答と同様に、用語のIMO乱用があります。回答の各概念について詳しく説明できないことは承知していますが、「パターンマッチング」、「関数型辞書」、「カリー化」、「遅延リスト」。
ThomasH

4
いい視点ね; 解決策はリンクを追加することだと思います。私の回答を編集するのに十分な担当者はいますか?その場合は、さまざまな概念へのリンクを自由に追加してください。時間のあるときに始めます。
ネイサンShively-Sanders

5
これは5年前のものだと思いますが…これは、関数型言語ではなく、Haskellで見逃していることについてのようです。たとえば、ほとんどのMLとLispの方言と子孫には、自動カリー化、ポイントフリースタイルの過度な冗長化、遅延リストがないなどがあります。したがって、遅延リストの代わりにイテレータを使用すると、Pythonの機能関数言語が悪くなり、どちらも CaMLをひどい関数型言語にしてはならないのですか?
abarnert 2014年

4
@abarnert:Camlには、ライブラリとして利用できる遅延リストを除くすべての箇条書きがあります。私はこの回答を書いたときに時々Camlを使用し、現在はF#を使用しています。どちらも非常に優れた関数型言語です。
ネイサンShively-Sanders

102

グイドはこれについてここで良い説明をしています。最も関連性の高い部分は次のとおりです。

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

...

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

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

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

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

8
あなたはPythonでテールコール最適化をうまく行うことができます。グイドはそれを理解していません。
ジュール、

26
Guido van Rossumは機能的なスタイルに似ていません
Svante、

59
Guido van Rossumは関数型を理解しておらず、なぜPythonがそれらを必要とするのかを理解していないと言ったほうが正確だと思います。2つのことを理解する必要があります。1)プログラミング言語はテクノロジースタックの最下部にあり、それらに基づいて構築されたすべてに影響します。2)他のソフトウェアと同様に、機能を追加するのは、削除するよりも簡単です。したがって、言語デザイナーにとって、このような要求に批判的であることは良質だと思います。
Jason Baker、

8
「当然のことながら、Lispも同様に動的です」->そして、同様に不可欠です!
2013

6
@ Jules、Pythonで末尾呼び出しの最適化を使用するためのガイドラインを共有してもらえますか?いくつかのソースへのポインタが役立つでしょう。
David Shaked 2016年

52

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

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

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

  3. グイドは恐れマップ、フィルタ、および軽減取り外ししばらく一度に

一方、pythonには字句的閉包、ラムダ、リスト内包表記(Guidoが認めるかどうかに関係なく、実際には「機能的」概念です)があります。私はPythonで多くの「関数型」プログラミングを行っていますが、それが理想的であるとはほとんど言えません。


3
Pythonでは、マップ、フィルター、および縮小は実際には必要ありません。それらを使用することで非常に単純化されたコードをまだ見ていません。さらに、Pythonでの関数の呼び出しは負荷が高くなる可能性があるため、通常はリスト/ジェネレーター内包表記またはforループを使用する方が適切です。
Jason Baker、

2
これは、Nathan Sandersが以下のように正確に言っていることです。「Pythonはかなりうまく機能しても、関数型プログラミングを促進しません。」GuidoがPythonを関数型言語にしたい場合、使い捨て関数を使用できるように実装を十分に機能させ、実際に便利な方法でmap / filter / reduceを使用できる範囲でLambdaを不自由にします。一方で、機能的な人々はリストの理解のすばらしさに目覚め始めています。うまくいけば、どちらかを選ぶ必要はありません。
ジェイコブB

7
マップとフィルターは、リスト内包表記に置き換えられます。削減-ほとんどの場合-非効率なので、ジェネレータ関数で置き換える必要があります。
S.Lott、2009年

13
@ S.Lottどうやってreduceをジェネレーターに置き換えますか?
アンチモン2013

17
@JacobBリスト内包表記は、Pythonが発明される約15年前とPythonが機能の実装を取得する25年前の関数型言語で利用できました。Pythonがその普及に影響を与えた、またはfpがこれをPythonから学んだ、または単にfpの世界での人気がPython実装の日付を後付けしたという考えは、単に間違っています。Pythonの実装はHaskellから直接採用されました。多分私はあなたを誤解したかもしれませんが、それはあなたが何を意味していたのかではありませんが、私は「機能的な人々がリスト内包の素晴らしさに目覚め始めている」ことに戸惑います。
itsbruce 2014

23

Pythonを「関数型」と呼ぶことは決してありませんが、Pythonでプログラミングするときはいつでも、コードは必ずほぼ純粋に関数型になります。

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


17

SOの「機能的な」Pythonの質問への回答から取ったコードの一部を使って実演してみましょう

Python:

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

Haskell:

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

ここでの主な違いは、Haskellの標準ライブラリは、関数型プログラミングのための便利な機能を有していることである。この場合ではiterateconcat(!!)


7
次にgrandKids()、ジェネレータ式を使用したbodyの1行の置き換えを示しますreturn reduce(lambda a, v: concat((x for x in kidsFunc(v)) for v in a), xrange(generation), [val])
Lloeki、2011年

6
そして、ここで必要としないものだconcat。どちらかreturn reduce(lambda a, v: (x for v in a for x in kidsFunc(v)), xrange(generation), [val])
Lloeki

9
@Lloeki:iterateはそのコードを大幅に簡略化し、(xはvのvはaのx for kidsFunc(v))はconcatMap(kidsFunc)よりはるかに明確です。Pythonには素敵な高次のビルトインがないため、Haskellに比べて同等のコードがわかりにくく冗長になります。
フォブ2012

2
concatは次のように置き換えることができますitertools.chain.from_iterable
Antimony 2013

@Antimony:知っておくと良い。thx
yairchu 2013

14

この質問(および回答)にとって本当に重要なことの1つは次のとおりです。関数型プログラミングとは一体何であり、最も重要な特性は何ですか。私はそれについて私の見解を与えようとします:

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

関数型プログラミングはそのようなものです。物事を変更するのではなく、方程式(この場合は「プログラム」)を評価して、答えが何であるかを理解します。プログラムは変更されずにまだ残っています。データについても同様です。

私は以下を関数型プログラミングの最も重要な機能としてランク付けします。b)副作用なし-ホワイトボードをどれだけ長く見ていても、別の人が別のホワイトボードを見ている方程式が誤って変更されることはありません。c)関数も値です。これは、他の変数とともに渡したり、他の変数に適用したりできます。d)関数構成。h= g・fを実行して、g(f(..))を呼び出すことと同等の新しい関数h(..)を定義できます。

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

さて、Pythonを使用して、言語とライブラリがこれらの側面をどれだけ適切にサポートし、保証しているのかを確認すると、自分の質問に答える準備が整っています。


2
関数はPythonの第一級です。
カールスミス

@CarlSmithその1つですが、Pythonにはない3/4です。:-\
arya 2016

1
Pythonは関数型プログラミングに適した言語ではないと思います。正直言って、そのコメントが何を意味していたのか、今でもよくわかりません。それは答えに関連していないようです。私はそれを削除しますが、あなたのコメントは文脈から外れます。
カールスミス

1
参照の透明性と不変性は、実際の言語機能ではありません。はい、一部の言語(Haskell)はそれらを強調してそれらを持たないようにすることを難しくしていますが、基本的に任意の言語で参照透過関数または不変オブジェクトを作成できます。標準ライブラリを回避する必要があるだけで、標準ライブラリに違反することがよくあります。
ケビン

また、Pythonはカレーと合成の両方をサポートしていますが、言語レベルではサポートされておらず、標準ライブラリにあります。
Kevin

10

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

それは余分な機能を持っているので、一部の人にとっては十分に純粋ではありません。

また、一部の機能が不足しているため、一部の機能を十分に備えていません。

不足している機能は比較的簡単に記述できます。PythonのFPでこのような投稿を確認してください。


2
ほとんどの場合、私はこの投稿に同意します。しかし、Pythonが関数型言語であると言うことに同意することはできません。それは命令型プログラミングを非常に奨励し、あなたが言及する「追加機能」を使わないようにするのは難しいです。他の投稿で行ったように、Pythonを「関数型ライト」と呼ぶのが最善だと思います。:-)
Jason Baker

8
-1すみません。つまり、ただ、そうではありません。関数型言語は、形式的推論を容易にする構造を提供します:帰納的(ML)、等式的(Haskell)。クロージャーと無名関数だけでも、戦略パターンの構文糖衣です。
2013

8

上記で言及されていないもう1つの理由は、多くの組み込み関数と組み込みタイプのメソッドがオブジェクトを変更するが、変更されたオブジェクトを返さないことです。これらの変更されたオブジェクトが返された場合、機能コードがより簡潔で簡潔になります。たとえば、some_list.append(some_object)がsome_objectを追加したsome_listを返した場合などです。


4

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

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

より優れた関数型言語は、これらの数学に基づく関係を認識し、自動的に最適化を実行します。関数型パラダイムに特化していない言語は、最適化されない可能性があります。


map( foo() , x ) * map( foo(), y ) == map( foo(), x * y )すべての関数に当てはまるわけではありません。たとえばfoo、導関数を計算する場合を考えます。
Eli Korvigo 2016

1
+代わりに彼が意味したと思います*
user1747134

foo()が線形であると想定していますか?
juan Isaza 2018年

そのプロパティはfoo(x)= x + 1には当てはまりません。As(x + 1)*(y + 1)!= x * y + 1.
juan
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.