ケース:私は会社で働いており、Pythonでアプリケーションを作成しています。これは配列内の大量のデータを処理しています。このプログラムの開発者は私だけです。現時点では、おそらく他のプログラマーによって将来(1〜3年)使用/変更/拡張されますが、現時点では私にはわかりません。おそらく、直接その場にいるわけではありませんが、時間があればメールでサポートします。
したがって、関数型プログラミング(Haskell)を学んだ開発者として、たとえば、次のようなフィルタリングを解決する傾向があります。
filtered = filter(lambda item: included(item.time, dur), measures)
コードの残りの部分はOOです。私によれば、コードがはるかに単純で美しいため、このように解決したいいくつかの小さなケースにすぎません。
質問:このようなコードを書いても大丈夫ですか?
行が何をするかを子供に説明するようなドキュメントを書くべきですか?
# Filter out the items from measures for which included(item.time, dur) != True
私は上司に尋ねたところ、彼は「FPは黒魔術ですが、それが機能し、最も効率的なソリューションであれば、それを使用しても問題ありません」と言っています。
これについてどう思いますか?非FPプログラマーとして、コードにどのように反応しますか?あなたはそれが何をするか理解できるようにコードは「グーグル」であるか?これについてのフィードバックが欲しいです。
読みやすいですか?
私にとって:はい、しかし私は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()
を使用する理由はほとんどまたはまったくありません。
変更可能ですか?
はい、確かに、しかし、それをより簡単にするために一つのことがあります:それをラムダではなく関数にします。
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()
はextremelysimpleです。モナドを理解するように頼むようなものではありません。そういうわけで私はあなたがそのようなコメントを書く必要があるとは思いません。適切な変数名と関数名を使用すると、コードはほとんど自明です。開発者が知らない言語機能を予測することはできません。ご存じのとおり、次の開発者は辞書が何であるかを知らないかもしれません。
私たちが理解していないことは通常、私たちには判読できません。したがって、これまでどちらも見たことがない場合は、リスト内包と同じくらい読みやすいと主張できます。しかし、ジョシュアが彼のコメントで述べたように、他の開発者が使用しているものと一貫性があることが重要だと私は信じています-少なくとも代替案が実質的な利点を提供しない場合。
開発者コミュニティは関数型プログラミングへの関心を取り戻しているため、元々完全にオブジェクト指向であった言語で関数型プログラミングが見られることは珍しくありません。良い例が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))
);
これは私に次のことを考えさせます:
あなたのコードを保守する次の開発者が十分な全体的な経験と関数型プログラミングのある程度の知識を持っていることを知っていても心配する必要はありませんが、
それ以外の場合は、関数型プログラミングを回避するか、構文、利点、および非関数型プログラミングのアプローチと比較して考えられる注意事項を説明すると同時に、詳細なコメントを付けます。
私は非FPプログラマーであり、最近、JavaScriptで同僚のコードを変更することになりました。含まれているステートメントによく似たコールバックを含むHttpリクエストがありました。私はそれをすべて理解するのに少し時間がかかったと言わざるを得ません(コード全体はそれほど大きくありませんでした)。
コメントはなかったし、必要もなかったと思う。私は同僚に彼のコードを理解するのを助けるように頼むことすらしませんでした。
私が約1.5年間働いていることを考慮すると、私がそうしたので、ほとんどのプログラマーはそのようなコードを理解し、変更することができると思います。
さらに、Joachim Sauerがコメントで述べたように、C#(indexOfなど)のような多くの言語ではFP)の断片がしばしばあります。FP以外のプログラマーの多くは、これを頻繁に扱います。そして、あなたが含めたコードスニペットは恐ろしいものでも理解できないものでもありません。
私は間違いなくそう言うでしょう!
関数型プログラミングには多くの側面があり、例のように高次関数を使用することはその1つにすぎません。
たとえば、私は pure functions を書くことは、任意の言語で書かれたソフトウェアにとって非常に重要であると考えています(「純粋」とは副作用がないことを意味します)。
また、値や変数の変更を避けることもよくあります。これは、FPから借用した別の概念です。
これらの手法はどちらもPythonおよび一般に機能的として分類されないその他の言語で正常に動作します。これらは多くの場合、言語自体(つまり、Javaのfinal
変数)でもサポートされています)したがって、将来のメンテナンスプログラマは、コードを理解するための大きな障壁に直面することはありません。
私は昨年私が働いた会社についても同じ議論をしました。
議論は「魔法のコード」とそれが奨励されるべきかどうかに関するものでした。もう少し詳しく調べてみると、実際には「魔法のコード」とは非常に異なる見解を持っていたようです。議論を取り上げた人は、関数型スタイルを使用する(PHPの)式が「魔法のコード」であることを主に意味しているようですが、コードでより多くのFPスタイルを使用する他の言語を起源とする開発者は、魔法のコードは、ファイル名などを介してファイルを動的に含めるときに作成されたものだと考えてください。
私たちがこれについて良い結論に至ったことはありません。見知らぬものに見えるコードは「魔法」であるか、または読みにくいと人々が考えている以上のことです。それで、他のユーザーにとってなじみのないコードを避けるのは良い考えでしょうか?どこで使うかによると思います。私は、データが明確で読みやすく、直感的な方法でトンネルされるメインメソッド(またはアプリケーションの重要な中心部分)でfpスタイルの式(動的ファイルのインクルードなど)を使用しないようにします。一方、私はエンベロープをプッシュすることを恐れるべきではないと思います。他の開発者は、おそらくFPコードに直面し、いくつかの優れたコードを持っている場合、FPをすぐに学ぶでしょう。問題について相談する社内リソース。
TL; DR:アプリケーションの高レベルな中央部分(アプリケーションの機能の概要について読む必要がある)は避けてください。それ以外の場合は使用してください。
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++開発者に受け入れられるようです。ただし、高次のラムダについては意見をサンプリングしていません。
Pythonほど流暢ではない開発者にコードスニペットを投稿し、コードをチェックして5分で彼/彼女が理解しているかどうかを確認できるかどうか尋ねます。
はいの場合、あなたはおそらく良いです。そうでない場合は、より明確にする必要があります。
あなたの同僚は行き当たりばったりで、明白なはずの何かを理解できませんか?はい。ただし、常にKISSに従ってプログラミングする必要があります。
たぶん、あなたのコードは、より単純でばかを証明するアプローチよりも効率的/見栄え/エレガントです?次に、あなた自身に尋ねる必要があります:これを行う必要がありますか?繰り返しになりますが、答えが「いいえ」の場合は、行わないでください。
結局のところ、あなたがまだあなたが必要だと思っていて、それをしたいFP方法なら、ぜひともそれを実行してください。あなたの本能を信じてください。任意のフォーラム:)