web-dev-qa-db-ja.com

Cで関数型プログラミングを学ぶことができますか?

ここでのコメントの議論 の結果として、Cで関数型プログラミングを学ぶことができるかどうか疑問に思いますか?

9
sbi

当然、do関数型プログラミングをCで行うことができます。理論的には学習関数型プログラミングの原則をCで行うこともできますが、この言語では簡単にできません。

私はあなたがOOPのバックグラウンドを少なくとも持っていると思います。その場合、OOPはCで行うことができます。これには、ポリモーフィズム、ゲッター/セッター、可視性ルールなどが含まれますが、そうするのはかなり大変です。 OOPとCの両方を理解する必要があります。FPの場合と同じです。

すべきしていることは、まず関数型プログラミング言語を学ぶことです(それらのほとんどは驚くほど単純な構文規則を持っています;学習を難しくするのは構文ではありません)。そしてthen新しく習得した知恵をCの記述方法に影響を与えます。


リクエストに応じて、FPから学ぶことができ、C、C++、Javaなどに適用できます。

  • 状態、特に共有される変更可能な状態を回避する
  • 純粋な関数を鑑賞する
  • 遅延評価に感謝する
  • 名詞ではなく動詞でモデル化できる
  • 問題に再帰的かつ反復的にアプローチできる
  • 高次関数の使用
  • 関数を別の種類の値と考え、他の値とやり取りして組み合わせることができる
  • オブジェクトベースのポリモーフィズムの代替手段としてのファーストクラス関数の使用
21
tdammers

Cをハッキングして、いくつかの機能的な概念を提供することができます。

この StackOverflowの質問 で詳細がわかります。しかし、Cで関数型プログラミング(またはその大部分)を実行することは可能であるように見えますが、ハックやコンパイラーの拡張など、概念を学ぶための最良の方法ではありません。

実際にlearn関数型プログラミングを行うには、最善の策は [〜#〜] lisp [〜のような著名な関数型プログラミング言語の1つです。 #〜] とその方言( ClojureScheme )、 Erlang 、および Haskell 。これらのいずれも、関数型プログラミングの考え方で機能する完璧なツールです。 F# は、.Netのバックグラウンドがある場合にも適していますが、厳密には関数型プログラミング言語ではなく、マルチパラダイム言語です。


コメントに tdammers のメモとして:

実際、LISP、clojure、schemeもマルチパラダイムです。 Haskellは、純粋でデフォルトの遅延でありながら、モナディックコンテキストで命令型プログラミングを可能にし、並行処理を幅広くサポートしています。これらはすべて、OOP世界-カプセル化、継承、単一の責任、構成など)で収集された知恵の大部分を実装するメカニズムを備えています。言語が他のパラダイムを許可するかどうかについてはそれほどではありません;それは、どのパラダイムが言語の出発​​点を形成するかについてです。

私の知る限り、LISPとその方言およびErlangは他のパラダイムよりも関数型プログラミングを促進するため、F#よりも優れた候補です。 tdammers言語の開始点。 F#には関数型プログラミングが含まれますが、他のサポートされているパラダイムである命令型プログラミングとooプログラミングを超えるものではありません。

11
yannis

TL; DR

関数型プログラミングは、クロージャーとそのアプリケーションに関するものです。誰かがCの降下クロージャーライブラリを表示できない限り、関数型プログラミングを学ぶためにCを使用することを忘れてください。

関数型プログラミングとは何ですか?

関数型プログラミングの基本的な概念は closures の概念であり、おおまかに言って、変数バインディングと一緒に関数をキャプチャします。クロージャーの普及に加えて、関数型プログラミングには、再帰的な関数や不変の値の使用(どちらもうまく機能する)など、他にもいくつかの特徴があります。これらの特性は何よりも文化的な問題であり、事実上すべての言語でそれらを使用するための技術的な障害はありません。これが、私の答えでクロージャーに焦点を当てている理由です。すべての言語で簡単にクロージャーを作成できるわけではありません。

クロージャの有用性を示す3つの図

クロージャの典型的な用途は、プライバシーメカニズムの実装です。たとえば、Javascriptコード–例ではJavascriptを選択しました。これは、いわゆる「Cのような構文」を持つ関数型言語であり、あなたの質問はCに精通していることを示唆しているためです。

create_counter = function()
{
  var x = 0;
  var counter = function()
  {
    ++x;
    return x;
  };
  return counter;
}

次に

a = create_counter();
b = create_counter();

互いに素なコレクションをカウントする2つの関数abがあります。この例の要点は、変数xcounterクロージャを定義するクロージャによってキャプチャされ、新しいcounter Closureis instantiated by the function, it gets its fresh own idea of whatx`がキャプチャされるたびです。

クロージャの別の典型的な使用法は、関数の部分的なアプリケーションの定義です。関数を実装するsyslogと同様のレポート機能があるとします。

var log = function(priority, message) {
…
};

引数prioritymessageは文字列であることが期待され、最初の引数は"debug""info"などのいずれかです。次のようにログファクトリを定義できます。

var logWithPriority = function(priority) {
  return function(message) {
    log(priority, message);
  };
};

それを使用して、ログ機能の特殊なバージョンを定義します。

var debug = logWithPriority("debug");
var info = logWithPriority("info");
…

このようにエラーが発生しやすいfor-ループを書く代わりに、これは非常に便利です

for(i = 0; i < journal.length; ++i) {
   log("info", journal[i]);
}

より簡潔で簡潔な記述ができます(iがないため、much優れています)。

journal.forEach(logWithPriority("info"));

クロージャの3番目の重要なアプリケーション分野は遅延評価の実装です。特別な言語サポートがより良い実装を提供できることに注意してください。

レイジー関数は、直接の計算を実行する代わりに、質問を実行するために呼び出すことができる(または怠惰の専門用語で「強制される」)クロージャーを返します。これを行う動機は、計算の準備と計算の実行を分離することです。これの実用的な例は、正規表現のコンパイルです。プログラムが起動時に多くの正規表現をコンパイルする場合、起動するのに多くの時間が必要になります。代わりに、正規表現を遅延コンパイルして、必要に応じて強制すると、プログラムをすぐに開始できます。もちろん、正規表現は、かなりの初期化時間を必要とする任意の構造で置き換えることができます。

クロージャーで遅延評価を実装する方法を次に示します。配列でmaxを返すarrayMax関数の従来の実装を考えてみましょう:

function arrayMax(array) {
  return array.reduce(function(a, b) {
    return Math.min(a, b);
  };
}

レイジーバリアントは次のようになります。

function arrayMax(array) {
  var memo = null;
  function actuallyCompute() {
    if(memo === null) {
      memo = array.reduce(function(a, b) {
        return Math.min(a, b);
      });
    }
    return memo;
  }
  return actuallyCompute;
}

戻り値は、値を計算したり、すでに計算されている場合はもう一度取得したりするために使用できるクロージャーです。

これらの3つの例では、クロージャとそのアプリケーションが関数型プログラミングの中核であることを確信できます。

結論

関数型プログラミングを学ぶとは、クロージャーを使ってプログラミングする方法を学ぶことです。結果として、関数型プログラミングを研究するための言語を探すときは、クロージャの簡単な操作を可能にする言語、特に関数の部分的な適用を考慮する必要があります。逆に、クロージャを簡単に操作できない言語は、適切な選択ではありません。

Cで関数型プログラミングのすべての側面を学ぶことはできません。しかし、関数型スタイルのプログラミングは、命令型言語で始めることができます。これらの開始ビットは、「プログラミング中に物事を純粋に保つ方法」です。そして、それはまたCを行うことができます。詳細については、このブログ投稿を確認してください-

http://www.johndcook.com/blog/2011/07/24/get-started-functional-programming/

2
Gulshan

使用するツールは学習に大きな影響を与えると思います。使用しているプログラミング言語が利用する手段を提供していないプログラミング概念を学ぶことはほとんど不可能です。もちろん、常にいくつかのことを学ぶことはできますが、適切に学ぶことはできません。

とにかく、それは学術的です。なぜなら、 Martinhoがコメントで述べているように だから、あなたが関数型プログラミングを学んだとしても、あなたすべきであるこれをしようとしないでください、これはが多い言語があるためですより簡単に。

1
sbi

Cで関数型プログラミングを学ぶべきではありませんが、厳密な関数型言語(Haskell、Caml、Erlangなど)で学びます。

関数型を初めて使用する場合は、非関数型言語を使用することはできません。より可能性が高いのは、関数型プログラミングだと思うことをするように訓練し、間違った方法で物事を学ぶことです。また、物事を正しい方法で「再学習」することは、最初に正しい方法で学ぶよりも常に困難です。

とにかく、Cで汎関数を行うことは、すでに汎関数を知っている人にとっては良い練習だと思います。なぜなら、その人は裏で何が起こっているのか、コンピューターが実際に何をしているかを知るからです。

1
deadalnix