web-dev-qa-db-ja.com

OOPそれらがもたらす利点について混乱する価値のある機能的機能は何ですか?

HaskellとF#で関数型プログラミングを学んだ後、OOPパラダイムは、クラス、インターフェイス、オブジェクトで逆に見えます。FPのどの側面を機能させることができますか私の同僚が理解できること?FPスタイルを使用できるようにチームを再訓練することについて上司に話す価値があるか?

FPの考えられる側面:

  • 不変性
  • 部分塗布とカレー
  • ファーストクラス関数(関数ポインター/関数オブジェクト/戦略パターン)
  • 遅延評価(およびモナド)
  • 純粋な機能(副作用なし)
  • 式(対ステートメント-コードの各行は、副作用の代わりに、またはそれに加えて、値を生成します)
  • 再帰
  • パターンマッチング

これは、プログラミング言語がサポートする制限まで、プログラミング言語がサポートすることは何でもできる無料のツールですか?または、より良いガイドラインはありますか?

13
Trident D'Gao

関数型プログラミングは、オブジェクト指向プログラミングとは異なるパラダイムです(異なる考え方、プログラムについての異なる考え方)。ここでは、問題とその解決策について考える方法が複数ある(オブジェクト指向)ことに気づき始めています。他にもあります(手続き型プログラミングと汎用プログラミングが頭に浮かびます)。これらの新しいツールやアプローチを受け入れてスキルセットに統合するかどうかにかかわらず、この新しい知識にどのように反応するかによって、成長してより完全でスキルのある開発者になるかどうかが決まります。

私たちは全員、処理するための訓練を受けており、ある程度の複雑さには慣れています。私はこれを人のhrair制限と呼びたいです(Watership Downから、どれくらいの高さを数えることができますか)。それはあなたの心を拡大し、より多くのオプションを検討し、問題に取り組み、解決するためのより多くのツールを持つ能力を持つことは素晴らしいことです。しかし、それは変化であり、それはあなたをあなたの快適ゾーンから引き離します。

遭遇する可能性のある1つの問題は、「すべてがオブジェクトである」群衆をフォローするコンテンツが少なくなることです。ソフトウェア開発への機能的アプローチが特定の問題に対してうまく機能する理由を理解していない(または理解したい)人々と一緒に作業するときは、忍耐力を養う必要があるかもしれません。一般的なプログラミング手法が特定の問題に対してうまく機能するのと同じです。

幸運!

13
ChuckCottrill

関数型プログラミングは、日常のコード作成において非常に実用的で実用的な生産性をもたらします。一部の機能は簡潔さを優先します。これは、記述するコードが少ないほど、失敗が少なく、必要なメンテナンスが少ないため、優れています。

数学者なので、ファンシーな機能は非常に魅力的だと思いますが、通常、アプリケーションを設計するときに役立ちます。これらの構造は、プログラム構造でプログラムの多くの不変量をエンコードできますが、これらの不変量を変数で表すことはできません。

私のお気に入りの組み合わせは非常に取るに足らないものに見えるかもしれませんが、それは非常に高い生産性の影響があると思います。この組み合わせは部分的なアプリケーションとカリー化ファーストクラス関数です。これは、ラベルを付け直しますforループを再度作成しない:代わりに、反復関数またはマッピング関数にループします。私は最近C++の仕事に雇われましたが、面白いことに気づきました。書く習慣を完全に失いましたfor-loops!

RecursionPattern Matchingの組み合わせにより、そのVisitor design patternの必要性がなくなります。ブール式のエバリュエーターをプログラムするために必要なコードを比較するだけです。関数型プログラミング言語では、これは約15行のコードになります。OOP 正しいこと =それはVisitorのデザインパターンを使用することであり、これにより、そのおもちゃの例が広範なエッセイで示されます。

6
user40989

スーパーマンが通常の生活の特典を楽しむためにクラークケントのふりをする方法は、仕事で使用する知識のどの部分を制限する必要があるかもしれません。しかし、もっと知ることは決してあなたを傷つけません。とはいえ、関数型プログラミングのいくつかの側面はオブジェクト指向のショップに適しています。他の側面は上司に相談する価値があるかもしれません。そうすれば、ショップの平均知識レベルを上げ、結果としてより良いコードを書くことができます。

FPとOOPは相互に排他的ではありません。Scalaを見てください。FPが不純であるために最悪だと考える人もいますが、同じ理由で最良であると考える人もいます。

1つずつ、OOPでうまく機能するいくつかの側面を次に示します。

  • 純粋な関数(副作用なし)-私が知っているすべてのプログラミング言語がこれをサポートしています。それらはあなたのコードをずっとずっと推論しやすくし、実用的であるときはいつでも使用されるべきです。 FPと呼ぶ必要はありません。それを良いコーディング慣行と呼んでください。

  • 不変性:文字列は間違いなく最も一般的に使用されるJavaオブジェクトであり、不変です。私は Immutable Java Objects および Immutable Java Collections 私のブログにあります。そのいくつかはあなたに当てはまるかもしれません。

  • ファーストクラス関数(関数ポインタ/関数オブジェクト/戦略パターン)-Javaは、バージョン1.1以降、実装されているほとんどのAPIクラス(および数百のクラス)を備えた、これの変更された変異バージョンを持っています)リスナーインターフェース。Runnableはおそらく最も一般的に使用される機能オブジェクトです。ファーストクラス関数は、ネイティブでサポートされていない言語でコードを作成する作業が多くなりますが、コードの他の側面を簡略化する場合は、追加の作業に値することがあります。

  • 再帰は、ツリーの処理に役立ちます。 OOPショップでは、おそらくそれが再帰の主な適切な使用法です。OOPでfunに再帰を使用することは、ほとんどのOOP言語には、これを良いアイデアにするためのデフォルトのスタックスペースがありません。

  • 式(対ステートメント-副作用の代わりに、またはそれに加えて、コードの各行で値が生成されます)-C、C++、およびJavaでの評価演算子は 項演算子 。ブログで適切な使用法について説明します。再利用可能で評価の高い簡単な関数をいくつか記述している場合があります。

  • 遅延評価(およびモナド)-ほとんどがOOPの遅延初期化に制限されています。それをサポートする言語機能がないと、便利なAPIがいくつか見つかるかもしれませんが、独自のAPIを作成することは困難です。代わりにストリームの使用を最大化してください。例については、WriterおよびReaderインターフェースを参照してください。

  • 部分的な適用とカレー-ファーストクラスの機能なしでは実用的ではありません。

  • パターンマッチング-OOPではお勧めできません。

要約すると、プログラミング言語がサポートする限り、プログラミング言語がサポートするすべてのことを実行できる自由な作業は、私にとって仕事ではないと思います。あなたの同僚による可読性は、雇われ用に作成されたコードのリトマステストでなければなりません。それが最もあなたを摩擦する場所で、私はあなたの同僚の視野を広げるために、職場でいくつかの教育を始めることを検討します。

5
GlenPeterson

関数型プログラミングとオブジェクト指向プログラミングに加えて、宣言型プログラミング(SQL、XQuery)もあります。それぞれのスタイルを学ぶことは、あなたが新しい洞察を得るのに役立ちます、そしてあなたは仕事に適切なツールを選ぶことを学ぶでしょう。

しかし、そうです、言語でコードを書くことは非常にイライラすることがあり、何か他のものを使用している場合は、特定の問題ドメインで生産性がはるかに高くなる可能性があることを知っています。ただし、Javaのような言語を使用している場合でも、FPからの概念をJavaコードに適用できますが、遠回りの方法です。たとえばGuavaフレームワークはこれのいくつかを行います。

3
sgwizdak

プログラマーとして、学習を止めるべきではないと思います。とはいえ、学習FPがあなたのOOPスキルを汚染していることは非常に興味深いことです。私は学習をOOP自転車に乗る方法を学ぶ;あなたはそれをする方法を決して忘れません。

FPの詳細を学びながら、自分は数学的にもっと考えるようになり、ソフトウェアを書く方法をよりよく理解できるようになりました。それは私の個人的な経験です。

経験を積むにつれて、コアプログラミングの概念を失うことははるかに難しくなります。 FP OOPの概念があなたの心に完全に固まるまで、気をつけてください。FPは明確なパラダイムシフト。

2
Bobby Gammill

すでに多くの良い答えがありますので、私の質問の一部を取り上げます。つまり、OOPおよび機能の機能は相互に排他的ではないため、私はあなたの質問の前提に恥ずかしく思います。

C++ 11を使用する場合、言語/標準ライブラリに組み込まれているこれらの種類の関数型プログラミング機能の多くは、OOPと(かなり)相乗効果があります。もちろん、上司や同僚がTMPをどの程度うまく受け取るかはわかりませんが、C++などの非機能/ OOP言語でこれらの機能の多くをなんらかの形で入手できることがポイントです。

コンパイル時の再帰でテンプレートを使用するには、最初の3つのポイントが必要です。

  • 不変性
  • 再帰
  • パターンマッチング

そのテンプレート値は不変(コンパイル時の定数)であり、反復は再帰を使用して行われ、分岐は(多かれ少なかれ)パターンマッチングを使用して、オーバーロード解決の形式で行われます。

その他の点については、_std::bind_および_std::function_を使用すると、部分的に関数を適用でき、関数ポインターが言語に組み込まれています。呼び出し可能オブジェクトは、機能オブジェクト(および部分的な関数アプリケーション)です。呼び出し可能オブジェクトとは、operator ()を定義するオブジェクトを意味することに注意してください。

遅延評価と純粋な関数は少し難しいでしょう。純粋な関数の場合、値でのみキャプチャするラムダ関数を使用できますが、これは理想的ではありません。

最後に、部分的な関数の適用でコンパイル時の再帰を使用する例を次に示します。これはやや不自然な例ですが、上記のポイントのほとんどを示しています。特定のタプルの値を特定の関数に再帰的にバインドし、(呼び出し可能な)関数オブジェクトを生成します

_#include <iostream>
#include <functional>

//holds a compile-time index sequence
template<std::size_t ... >
struct index_seq
{};

//builds the index_seq<...> struct with the indices (boils down to compile-time indexing)
template<std::size_t N, std::size_t ... Seq>
struct gen_indices
  : gen_indices<N-1, N-1, Seq ... >
{};

template<std::size_t ... Seq>
struct gen_indices<0, Seq ... >
{
    typedef index_seq<Seq ... > type;
};


template <typename RType>
struct bind_to_fcn
{
    template <class Fcn, class ... Args>
    std::function<RType()> fcn_bind(Fcn fcn, std::Tuple<Args...> params)
    {
        return bindFunc(typename gen_indices<sizeof...(Args)>::type(), fcn, params);
    }

    template<std::size_t ... Seq, class Fcn, class ... Args>
    std::function<RType()> bindFunc(index_seq<Seq...>, Fcn fcn, std::Tuple<Args...> params)
    {
        return std::bind(fcn, std::get<Seq>(params) ...);
    }
};

//some arbitrary testing function to use
double foo(int x, float y, double z)
{
    return x + y + z;
}

int main(void)
{
    //some Tuple of parameters to use in the function call
    std::Tuple<int, float, double> t = std::make_Tuple(1, 2.04, 0.1);                                                                                                                                                                                                      
    typedef double(*SumFcn)(int,float,double);

    bind_to_fcn<double> binder;
    auto other_fcn_obj = binder.fcn_bind<SumFcn>(foo, t);
    std::cout << other_fcn_obj() << std::endl;
}
_
0
alrikai