FP以外の人が少量の関数型プログラミングを理解できるか?[閉まっている]


43

事例:私は会社で働いており、配列で大量のデータを処理するPythonでアプリケーションを作成しています。私は現時点でこのプログラムの唯一の開発者ですが、おそらく他のプログラマーによって将来(1〜3年)に使用/変更/拡張される可能性があります。私はおそらく直接その場にいませんが、時間があれば電子メールでサポートを提供するかもしれません。

そのため、関数型プログラミング(Haskell)を学んだ開発者として、たとえば次のようなフィルタリングを解決する傾向があります。

filtered = filter(lambda item: included(item.time, dur), measures)

コードの残りの部分はオブジェクト指向です。私にとっては、はるかに単純で美しいため、このように解決したいのはほんの一部のケースです。

質問:今日このようなコードを書いても大丈夫ですか?

  • FPを作成/学習していない開発者は、このようなコードにどのように反応しますか?
  • 読みやすいですか?
  • 変更可能ですか?
  • 行が何をするのかを子供に説明するようなドキュメントを書くべきですか?

     # Filter out the items from measures for which included(item.time, dur) != True

上司に聞いたところ、「FPは黒魔術ですが、うまく機能し、最も効率的なソリューションであれば、使用しても構いません」と彼は言います。

これについてのあなたの意見は何ですか?非FPプログラマーとして、コードにどのように反応しますか?コードは「グーグル可能」であるため、その機能を理解できますか?これに関するフィードバックをお待ちしています。


14
私の意見では、多くの近代的な言語ができるように、いくつかの関数型プログラミングの程度や使用しているが、ますます一般的になります。個人的には(少なくとも比較的単純なフィルタリング/マッピングタスクには)行ってください。
ヨアヒムザウアー

FPが好きなので、影響を受けない答えをすることはできません。しかし、コメントを追加するのは良い考えです。
ブルーノシェーパー

3
あなたのコメントがより明確になります。フィルターには、テストがTrueである要素が含まれているため、コメントは次のようになります:# Select the item's from measures for which included(item.time, dur) == True、二重否定を避けることで常に理解が向上します。
マーティンピーターズ

6
代わりにリスト内包表記の使用を検討しましたか?それらは、多くの場合、マップ/フィルターよりも「pythonic」と見なされます。
phant0m

2
@MatjazMuhicまあ、それは美しい...;)
kd35a

回答:


50

読みやすいですか?

私にとって:はい、しかし、Pythonコミュニティはリストの内包表記をmap()/ を使用するよりもクリーンなソリューションと考えることが多いようfilter()です。

実際、GvRはこれらの機能を完全に削除することを検討しました。

このことを考慮:

filtered = [item for item in measures if included(item.time, dur)]

さらに、これにはリスト内包表記が常にリストを返すという利点があります。map()そしてfilter()他方でPython 3にイテレータを返します。

注:代わりにイテレータが必要な場合は、次のように置き換えるだけ[]で簡単()です。

filtered = (item for item in measures if included(item.time, dur))

正直に言うと、Python を使用しmap()たりfilter()Python を使用したりする理由はほとんどありません。

変更可能ですか?

はい、確かに、それを簡単にするための1つのことがあります。ラムダではなく関数にすることです。

def is_included(item):
    return included(item.time, dur)
filtered = filter(is_included, measures)
filtered = [item for item in measures if is_included(item)]

条件がより複雑になった場合、これははるかに簡単にスケーリングできます。また、チェックを再利用できます。(他の関数内に関数を作成できることに注意してください。これにより、関数を使用場所に近づけることができます。)

FPを作成/学習していない開発者は、このようなコードにどのように反応しますか?

彼はPythonドキュメントをグーグルで検索し、5分後にどのように機能するかを知っています。そうでなければ、彼はPythonでプログラミングすべきではありません。

map()そして、filter()されている非常にシンプル。モナドを理解するように彼らに求めているのではありません。だから、そのようなコメントを書く必要はないと思います。適切な変数名と関数名を使用すると、コードはほとんど自明です。開発者が知らない言語機能を予測することはできません。あなたが知っているすべてのために、次の開発者は辞書が何であるかを知らないかもしれません。

私たちが理解していないことは、通常私たちにとって読めません。したがって、どちらも見たことがない場合は、リストの理解よりも読みやすいと主張できます。しかし、Joshuaが彼のコメントで述べたように、私も他の開発者が使用しているものと一貫性を保つことが重要であると信じています-少なくとも代替手段が実質的な利点を提供しない場合。


5
それは価値があるにも言及するかもしれませんジェネレータ式として、リストの内包表記ではなく、リストのリターン発電機と同じように働き、mapそしてfilterパイソン3で行う
ジェームズ・

@James:素晴らしいアイデアです。投稿に追加しました。ありがとう!
phant0m

2
通常、インラインジェネレーターまたはリスト内包表記に置き換えるのは比較的難しいため、リスト内包引数を常に使用できることのreduce反例として取り上げますreduce
小次郎

4
問題の言語の優先イディオムに注目して+1。私見の一貫性には大きな価値があり、「スピーカー」の大部分が行うような方法で言語を使用することで、コメントよりも保守性を高めることができます。
ジョシュアドレーク

4
「彼はPythonのドキュメントをグーグルで検索し、5分後にはどのように機能するかを知っています。そうでなければ、Pythonでプログラミングすべきではありません。」
ビャルケフロイントハンセン

25

開発者コミュニティは関数型プログラミングへの関心を取り戻しているため、元々完全にオブジェクト指向であった言語の関数型プログラミングを目にすることは珍しくありません。良い例はC#です。ここでは、匿名型とラムダ式により、関数型プログラミングによりはるかに短く表現力豊かになります。

そうは言っても、関数型プログラミングは初心者にとっては奇妙です。たとえば、トレーニングコース中に、C#で関数型プログラミングを使用してコードを強化する方法を初心者に説明しましたが、一部は納得できず、一部は元のコードを理解しやすいと述べました。私が彼らに与えた例は次のとおりです。

リファクタリング前のコード:

var categorizedProducts = new Dictionary<string, List<Product>>();

// Get only enabled products, filtering the disabled ones, and group them by categories.
foreach (var product in this.Data.Products)
{
    if (product.IsEnabled)
    {
        if (!categorizedProducts.ContainsKey(product.Category))
        {
            // The category is missing. Create one.
            categorizedProducts.Add(product.Category, new List<Product>());
        }

        categorizedProducts[product.Category].Add(product);
    }
}

// Walk through the categories.
foreach (var productsInCategory in categorizedProducts)
{
    var minimumPrice = double.MaxValue;
    var maximumPrice = double.MinValue;

    // Walk through the products in a category to search for the maximum and minimum prices.
    foreach (var product in productsInCategory.Value)
    {
        if (product.Price < minimumPrice)
        {
            minimumPrice = product.Price;
        }

        if (product.Price > maximumPrice)
        {
            maximumPrice = product.Price;
        }
    }

    yield return new PricesPerCategory(category: productsInCategory.Key, minimum: minimumPrice, maximum: maximumPrice);
}

FPを使用してリファクタリングした後の同じコード:

return this.Data.Products
    .Where(product => product.IsEnabled)
    .GroupBy(product => product.Category)
    .Select(productsInCategory => new PricesPerCategory(
              category: productsInCategory.Key, 
              minimum:  productsInCategory.Value.Min(product => product.Price), 
              maximum:  productsInCategory.Value.Max(product => product.Price))
    );

これは私にそれを考えさせます:

  • コードを保守する次の開発者が全体的な十分な経験と関数型プログラミングの知識を持っていることを知っていれば心配する必要はありませんが、

  • そうでない場合は、関数型プログラミングを避けるか、構文、利点、および非関数型プログラミングに対するアプローチの考えられる警告を同時に説明する冗長なコメントを入れてください。


8
FPがコードを誰にも読みやすくしない場合は、+ 1します。間違っています。
ジェルジAndrasek

7
前者が前者のスニペットを後者より読みやすいと考える前に、誰かがFPを一度も見たことがありません。しかし、これを100倍するとどうなりますか?何が何千行の配管方法のコードであるを説明する、ほぼ英語に似た短い100の短い文章を読んでください。どれだけ馴染みのある大量のコードでも、使い慣れたものがそれほど大きな勝利ではないほど圧倒されるはずです。ある時点で、誰もが最初にFPの要素に慣れてから、数千行の馴染みのあるコードを読むのではなく、数行を読むのが簡単でなければなりません。
エサイリジャ

7
最も気になるのは、開発者が機能的なスタイルを学ばなくても許容できると信じていることです。利点は何度も証明されており、パラダイムは主流の言語でサポートされています。人々が機能的なテクニックを理解する能力に欠けている場合、たとえそれを使用するつもりがなくても、彼らは教えられるべきであるか、自分で教えるべきです。燃えるような情熱を持ってXSLTを嫌いますが、それでもプロとしてそれを学ぶのを止めませんでした。
スプレーグ

2
@MainMaあなたのポイントは完全に有効です、私はもっと具体的にすべきでした。つまり、特定の言語の開発者は、それらの言語の機能を効果的に使用することを期待されるべきです。たとえば、C#で(機能スタイル)を使用する(フィルター/マップ/リデューススタイルプログラミングを使用する、または関数を渡す/返す)ことは新しいことではないため、このようなステートメントを読み取れないC#開発者は、スキル...おそらく非常に迅速。確かに、すべての開発者は少しのHaskellを学ぶべきだと思いますが、それは学術的な理由のためだけです。これは別のことです。
スプレーグ

2
@Sprague:あなたの意見を聞き、それに同意します。たとえば、C#プログラマーがFPのパラダイムを使用し始めるのは、それらのプログラマーの多くがジェネリックを使用していなくてもほとんど期待できないことです。非常に多くの未熟なプログラマーが登場するまで、私たちの職業の水準は低くなります。特に技術的なバックグラウンドを持たないほとんどの人は、開発が何であるかをまったく理解していないからです。
Arseni Mourzenko

20

私は非FPプログラマであり、最近JavaScriptで同僚のコードを変更しました。含まれているステートメントによく似たコールバックを持つHttpリクエストがありました。すべてを理解するのに時間がかかりました(30分など)(すべてのコードはそれほど大きくありませんでした)。

コメントはありませんでしたし、何も必要はなかったと思います。私は同僚に彼のコードを理解するのを手伝ってもらおうとはしませんでした。

私が約1.5年間働いていることを考慮すると、ほとんどのプログラマーは、私がそうしたコードを理解し、修正できると思います。

さらに、Joachim Sauerがコメントで述べたように、多くの場合、C#(indexOfなど)のような多くの言語でFPの断片があります。非常に多くの非FPプログラマーがこれを非常に頻繁に扱っており、あなたが含めたコードスニペットは恐ろしいものでも理解できないものでもありません。


1
ご意見をありがとうございます!他の観点についての詳細情報が得られました。ホームブラインドになるのは簡単ですし、FPを知らなかったときの様子がわかりません:)
kd35a

1
@ kd35a、どういたしまして。幸いなことに、私はここで客観的になることができます))
superM

1
多くの言語にFPの要素が含まれるようになったことを指摘した+1。
ジョシュアドレイク

LINQはC#でFPの主な例である
コンラッドMorawski

3

はい、間違いなく言うでしょう!

関数型プログラミングには多くの側面がありますが、あなたの例のように、高階関数を使用することはそのうちの1つにすぎません。

たとえば、純粋な関数を記述することは、あらゆる言語で書かれたソフトウェアにとって非常に重要であると考えています(「純粋」とは副作用を意味しません)。

  • ユニットテストが簡単です
  • 副作用のある関数よりもはるかに構成可能です
  • デバッグが簡単です

また、値や変数の変更を避けることもよくあります。これは、FPから借用した別の概念です。

これらの手法はどちらも、Pythonおよび一般に機能として分類されていない他の言語で正常に機能します。多くの場合、言語自体(つまりfinal、Javaの変数)でもサポートされています。したがって、将来のメンテナンスプログラマは、コードを理解する上で大きな障壁に直面することはありません。


2

去年働いていた会社についても同じ議論をしました。

議論は「魔法のコード」と、それが奨励されるべきかどうかに関するものでした。少し詳しく調べてみると、実際に「魔法のコード」とは何かについて人々は非常に異なった見方をしているように見えました。議論をもたらしたのは、機能スタイルを使用した式(PHP)が「魔法のコード」であったことを意味するようでしたが、コードでより多くのFPスタイルを使用した他の言語からの開発者は、魔法のコードはむしろファイル名などでファイルを動的に含める場合。

私たちはこれについて良い結論に至りませんでした。それは、人々がほとんど見慣れないコードは「魔法のような」または読みにくいと思っている以上です。他のユーザーにはなじみのないコードを避けるのは良い考えですか?私はそれがどこで使用されているかに依存すると思います。明確で読みやすく、直感的な方法でデータをトンネルするメインメソッド(またはアプリケーションの重要な中心部分)でfpスタイルの式(動的ファイルインクルードなど)を使用することは控えます。一方、私は、エンベロープをプッシュすることを恐れるべきではないと考えています。他の開発者は、FPコードに直面している場合、おそらく問題を相談するための社内リソースを持っている場合、FPをすぐに学ぶでしょう。

TL; DR:アプリケーションの高レベルの中央部分(アプリケーションの機能の概要について読む必要がある)を避けます。それ以外の場合は使用します。


より高いレベルのコードで使用しないでください。
kd35a

さまざまなタイプのブロック(静的メソッド、パブリックメソッド、コンストラクターなど)でプログラミングの手法を定義する際に、形式主義の欠如と非常に多くの機会があると常に考えてきました。良い答え...それらの考えの。
スプレーグ

2

C ++コミュニティも最近ラムダを取得しましたが、ほぼ同じ質問があると思います。ただし、答えは同じではない場合があります。C ++に相当するものは次のとおりです。

std::copy_if(measures.begin(), measures.end(), inserter(filter),
  [dur](Item i) { return included(i, dur) } );

現在std::copyは新しいものではなく、_ifバリアントも新しいものではありませんが、ラムダは新しいものです。しかし、それはコンテキストでかなり明確に定義されています。durキャプチャされ、したがって一定Item iであり、ループ内で変化し、単一のreturnステートメントがすべての作業を行います。

これは多くのC ++開発者に受け入れられるようです。しかし、私は高次のラムダについての意見をサンプリングしていません。


興味深い点は、それがどの言語にあるかによって異なる答えが存在する可能性があることです。おそらく、@ ChristopherKäckが、PHPコーダーがこの種のものに関してPythonコーダーよりも多くの問題を抱えていることに関する投稿に関連しています。
kd35a

0

Pythonに堪能でない仲間の開発者にコードスニペットを投稿し、5分間コードをチェックして理解できるかどうかを確認してもらえるかどうかを尋ねます。

はいの場合は、おそらく行ってもいいでしょう。そうでない場合は、明確にする必要があります。

あなたの同僚は一生懸命で、自明であるべきことを理解できませんか?はい。ただし、常にKISSに従ってプログラムする必要があります。

あなたのコードは、より簡単で馬鹿なアプローチよりも効率的/格好良い/エレガントなのでしょうか?それから、あなたは自問する必要があります:私はこれをする必要がありますか?繰り返しますが、答えが「いいえ」の場合、それをしないでください!

それでもやはり、FP方式で必要であり、それを実行したい場合は、必ず実行してください。あなたの本能を信じてください、彼らはどんなフォーラムのほとんどの人々よりあなたのニーズにより適しています:)


downvoteの理由を説明して自由に感じる
Arnabダッタ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.