web-dev-qa-db-ja.com

関数が純粋かどうかを計算する

ウィキペディアによると:

コンピュータプログラミングでは、関数に関するこれらのステートメントが両方とも成立する場合、関数は純粋であると説明できます。関数は常に、同じ引数値が与えられた場合、同じ結果値を評価します。関数の結果値は、プログラムの実行が進むにつれて、またはプログラムの異なる実行間で変化する可能性のある非表示の情報や状態に依存したり、I/Oデバイスからの外部入力に依存したりすることはできません。結果の評価は、変更可能なオブジェクトの変異やI/Oデバイスへの出力など、意味的に観察可能な副作用や出力を引き起こしません。

関数が純粋かどうかを計算する関数を書くことができるかどうか疑問に思っています。 JavaScriptのコード例:

function sum(a,b) {
    return a+b;
}

function say(x){
    console.log(x);
}

isPure(sum) // True
isPure(say) // False
11
Oni

はい、言語によっては可能です。

JavaScriptでは、次の基準によって関数が純粋かどうかを判断できます。

  • パラメータとローカルのみを読み取ります。

  • ローカルのみを書き込みます。

  • 非ローカルでは、純粋な関数のみを呼び出します。

  • 暗黙的に呼び出すすべての関数は純粋です。たとえば、toString;そして

  • 非ローカルをエイリアスしない場合にのみ、ローカルのプロパティを書き込みます。

エイリアス は、通常、動的にオブジェクトのプロパティを検索できるため(object["property"])、JavaScriptで判別することはできません。あなたがそれを決してやらず、そしてあなたがプログラム全体の源を持っているならば、私は問題が扱いやすいと思います。また、console.logなど、DOMに関連するほとんどすべての副作用など、どのネイティブ関数に副作用があるかについての情報も必要です。

「純粋」という用語は、いくつかの説明を使用することもできます。すべての関数が参照透過的である、強く静的に型付けされた純粋に関数型のプログラミング言語であっても、関数の終了に失敗する可能性があります。したがって、id :: a -> aについて話すとき、私たちが実際に言っているのは次のことではありません。

タイプaの値を指定すると、関数idはタイプaの値を生成します。

むしろ:

タイプaの値を指定すると、関数idnotを実行してnotタイプaの。

idの有効な実装はerror "Not implemented!"だからです。 Peterisが指摘しているように、この非全体性は一種の不純なものと見なすことができます。 Koka はJavaScriptでモデル化された構文を備えた関数型プログラミング言語であり、発散(非終了)、参照透過性、例外のスロー、I/Oアクションなどの起こり得る影響を推測できます。

17
Jon Purdy

いいえ。JonPurdyの回答で説明されているように、関数が「純粋に安全な」操作のみを実行するかどうかを簡単に確認できますが、それはIMOでは質問に答えるには不十分です。

この関数を考えてみましょう:

_function possiblyPure(x) {
    if (someCheck(x)) {
        return x+1; // pure code path
    }
    else {
        console.log("I'm so unpure..."); // unpure code path
    }
}
_

明らかに、someCheckが不純であれば、possiblyPureも不純です。ただし、someCheckが純粋であり、trueのすべての可能な値に対してxを返す場合、未精製のコードパスに到達できないため、possiblyPureは純粋です。

そして、ここで難しい部分があります:someCheckがすべての可能な入力に対してtrueを返すかどうかを決定します。その質問にすぐに答えようとすると、停止の問題と同様の決定不可能な問題の領域にあなたを導きます。

編集:不可能であることの証明

純粋な関数がすべての可能な入力で終了しなければならないかどうか、不確実性があります。ただし、どちらの場合でも、停止問題を使用して、純粋性チェックが不可能であることを示すことができます。

ケースA)考えられるすべての入力で終了するために純粋な関数が必要な場合、haveを使用して停止問題を解決し、関数が純粋かどうかを判断します。これは不可能であることがわかっているため、この定義では、純粋さを計算できません。

ケースB)純粋な関数が一部の入力で終了しないことが許可されている場合、次のようなものを作成できます。fが純粋な関数を定義する文字列である場合にisPure(f)が計算するとします。

_function halts(f) {
   var fescaped = f.replace(/\"/g, '\\"');
   var upf = 'function() { '+f+'("'+fescaped+'\); console.log("unpure"); }';
   return isPure(upf);
}
_

ここでisPureは、自身のソースを入力としてfが停止するかどうかを判断する必要があります。停止した場合、upfは純粋ではありません。終了しない場合、upffが純粋な場合に限り純粋です。

isPureが期待どおりに機能した場合(正しい結果が返され、すべての入力で終了します)、停止の問題は解決されます(*)!これは不可能であることがわかっているため、isPureは存在できません。

(*)純粋なJavaScript関数の場合、チューリングマシンでもこれを解決するのに十分です。

11
user281377

このstackoverflow質問 は、ここに関連するyfeldblumによる回答があります。 (そして、なんらかの理由でダウンボットがあります。3歳の何かをアップボットするのは悪いエチケットでしょうか?)彼は、関数が純粋であるかどうかがコメントで停止問題に還元可能であることの証明を与えます。

実用的な観点からすると、一部の言語では、関数に「はい」、「いいえ」、または多分返すようにさせても、それほど難しくはないと思います。私は数日前にClojureに関するビデオを見ていましたが、スピーカーは約4つの異なる文字列( "ref"など)を検索して、コードベース内の不純なインスタンスのカウントを行いました。 Clojureは、純粋でないものの純度と分離に重点を置いているため、取るに足らないことでしたが、探しているものとまったく同じではありませんでした。

ですから、理論的には不可能であり、少し微調整すれば実際には可能であり、どれだけ難しいかは言語に大きく依存すると思います。不変性と優れたリフレクションに重点を置いた、よりシンプルでクリーンな言語の方が簡単でしょう。

1
Michael Shaw

すばらしい質問です。

可能な限り何回も関数を呼び出すためのI/Oアクションをリッスンする能力がないと想定して、実際に実行できる最善の方法。次に、戻り値が一貫しているかどうかを確認します。

しかし、これを一般的に行うことはできません。間違いなく、停止しないプログラムは純粋ではなく、停止の問題を決定することはできません。

0
Peteris

一般的なケースでは不可能です。 停止問題 を参照してください。簡単に言えば、任意の関数と入力を与えられて、プログラムが停止するか永久に実行されるかを決定するプログラムを書くことは不可能です。それが永遠に実行される場合、それはあなたが与えた定義に適合する純粋な関数ではありません。

0
dbkk