1. ホーム
  2. functional-programming

[解決済み] 社会人のためのコンビナート説明会

2023-02-12 21:35:01

質問

コンビネータとは何ですか?

コンビネータとは "自由変数を持たない関数または定義"。 (SOでの定義通り)ですか?

あるいは、次のようにするとどうでしょう。 ジョン・ヒューズ は、彼のよく知られたアローズに関する論文において コンビネータとは、プログラムの断片からプログラムの断片を構築する関数である。 これは、「...コンビネータを使うプログラマは、細部まで手で書くのではなく、目的のプログラムの多くを自動的に構築することができる」ので有利である。 彼はさらに次のように言っている。 mapfilter はそのようなコンビネーターの2つの一般的な例である。

最初の定義にマッチするいくつかのコンビネータ。

2番目の定義にマッチするいくつかのコンビネータ。

  • マップ
  • フィルタ
  • fold/reduce (推定)
  • gt;>=、compose、fmapのいずれか?

私は最初の定義に興味がありません -- これらは私が実際のプログラムを書くのに役立たないでしょう (私が間違っていると納得させられたら+1)。 2番目の定義について理解するのを助けてください . map、filter、reduceは有用だと思います。より高いレベルでのプログラミングを可能にします。 以下は、コンビネータに関する私の具体的な質問です。

  1. mapやfilterのようなコンビネーターの例にはどのようなものがありますか?
  2. プログラミング言語がよく実装するコンビネータは何ですか?
  3. コンビネータは、より良いAPIを設計するためにどのように役立つか?
  4. 効果的なコンビネータを設計するにはどうしたらよいでしょうか。
  5. 非機能的な言語(例えばJava)ではコンビネータは何に似ているのですか?

更新

C. A. McCannに感謝します。A. McCannのおかげで、私は今、コンビネータについていくらか理解することができました。 しかし、1つの疑問はまだ私にとって引っかかる点です。

コンビネータを多用した関数型プログラムと、そうでないプログラムの違いは何でしょうか?

答えは、コンビネーターを多用したバージョンの方が短く、明確で、より一般的であるということだと思いますが、可能であれば、より深い議論をお願いしたいです。

また、複雑なコンビネータ (すなわち fold よりも複雑です)。

どのように解決するのですか?

<ブロッククオート

私は最初の定義には興味がありません -- これらは私が実際のプログラムを書くのに役立たないでしょう (私が間違っていると納得していただければ+1)。2番目の定義について理解する手助けをしてください。map、filter、reduceは有用だと思います。より高いレベルでのプログラミングを可能にし、より少ないミス、より短く明確なコードを提供します。

2つの定義は基本的に同じものです。1つ目は正式な定義に基づくもので、与える例としては プリミティブコンビネーター --であり、可能な限り小さな構成要素です。これを使えば、より洗練されたコンビネータを作ることができるため、実際のプログラムを書くのに役立ちます。SやKのようなコンビネータは、仮想的な「組み合わせコンピュータ」の機械語だと考えてください。もちろん、実際のコンピュータはそのようには動作しませんので、実際には、より高度な演算が裏で別の方法で実装されているのが普通ですが、この概念的な基礎は依然として の意味 を理解するのに役立つツールです。

2つ目の定義はより非公式なもので、他の関数をさまざまな方法で組み合わせる高階関数という形で、より洗練されたコンビネータを使用することに関するものです。基本的な構成要素が上記の原始的なコンビネーターである場合、以下のことに注意してください。 すべて は高階関数であり、コンビネータである。しかし、他のプリミティブが存在する言語では、関数であるかそうでないかの区別があり、その場合、コンビネータは通常、関数でないものを直接操作するのではなく、他の関数を一般的な方法で操作する関数と定義される。

<ブロッククオート

mapやfilterのようなコンビネータの例を教えてください。

挙げるには多すぎますね。どちらも、1つの値に対する振る舞いを記述する関数を、コレクション全体に対する振る舞いを記述する関数に変換します。を変換する関数もあります。 だけ を変換する関数もあります。例えば、端から端まで合成したり、引数を分割して再結合したりします。シングルステップの操作を、コレクションを生成または消費する再帰的な操作に変えるコンビネータを持つことができます。その他にも、本当にいろいろなものがあります。

プログラミング言語がよく実装するコンビネータは何ですか?

それはかなり異なるでしょう。ほとんどの場合、コンビネータは使用されているデータ構造をある程度認識します (たとえデータ構造が他のコンビネータから構築されていたとしても)。(適切に一般化されたバージョンの) map、fold、および unfold が、必要とされるほぼすべてのことを行うのに十分である、ばかげた数のケースがあります。

コンビネータはどのようにしてより良いAPIを設計するのに役立つのでしょうか?

まさにあなたが言ったように、低レベルの詳細ではなく、高レベルの操作とそれらが相互作用する方法の観点から考えることです。

コレクションに対する "for each"- スタイルのループの人気について考えてみてください、これは、コレクションを列挙する詳細を抽象化することができます。これらはほとんどの場合、単なるマップ/フォールド操作であり、(組み込み構文ではなく)コンビネータにすることで、2 つの既存のループを取り、複数の方法でそれらを直接組み合わせることなどができます。

効果的なコンビネータを設計するには?

まず、プログラムが使用するデータに対して、どのような演算が意味を持つかを考えてください。次に、これらの操作を一般的な方法で有意義に組み合わせる方法と、操作をより小さな断片に分解して再び接続する方法について考えます。主なものは 変換 操作 は、直接ではなく アクション . 複雑な機能を不透明な方法で実行し、消化された結果を吐き出すだけの関数があったとしても、それに対してできることは多くありません。最終的な結果は を使用します。 最終的な結果は、プロセスの開始や終了を期待するものではなく、A 地点から B 地点に移動させるものが必要です。

非機能的な言語(たとえばJava)ではコンビネータは何に似ていますか、あるいはこれらの言語ではコンビネータの代わりに何を使っていますか?

アハハハ。オブジェクトはいくつかのデータを持ちますが、たくさんのオペレーションも持ち運びます。そして、良いOOP設計を構成するもののかなり多くは、「オブジェクトは通常、データ構造ではなく、コンビネーターのように動作すべきである」ということに帰着します。

ですから、おそらくここでの最善の答えは、コンビネーターのようなものの代わりに、多くのゲッターとセッターメソッドまたはパブリックフィールドを持つクラスと、主に不透明で定義済みのアクションを行うことからなるロジックを使用することです。