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


11

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

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

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

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

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

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

7
これはComputer Science Stack Exchangeの候補かもしれません。これは、実用的で設計よりも理論の領域(「可能」)に近い傾向があります。

...そして、それが「可能」および「理論」であっても、証拠を含む可能性のある本当の答え(Q&Aに適している)が存在する可能性が非常に高い... 。

定義で呼び出される関数のタイプを見ることで、関数が純粋かどうかを判断できると思います。つまり、関数の構造に関する帰納法によって純度を定義できます。
ジョルジオ

7
プラットフォーム固有のリフレクションハックなしでは実行できません。関数がブラックボックスの場合、それが純粋であることを証明できる実験はありません。たとえば、のようなものがある場合if (rand(1000000)<2) return WRONG_ANSWER、一貫した動作のために関数を何度もプローブしても役に立ちません。ただし、関数定義にアクセスできる場合、証明は簡単です。
SKロジック

1
@ user470365 純粋な関数の定義から-「結果の評価は、可変オブジェクトの変更やI / Oデバイスへの出力など、意味的に観察可能な副作用や出力を引き起こしません。」-IOに書き込むものはすべて定義上不純です。したがって、この例では、不純なsay呼び出しも不純です。console.logsay

回答:


17

はい、言語に応じて可能です。

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

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

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

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

  • 暗黙的に呼び出す関数はすべて純粋toStringです。そして

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

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

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

typeの値を指定するaと、関数idはtypeの値を生成しますa

むしろ:

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

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


1 - Peterisによって答えは.....非常に多くのupvotesを持って考える
mattnz

「純粋な関数のみを呼び出す」を基準のリストに追加する必要があると思います。
グレッグヒューギル

1
@GregHewgill:良いキャッチ。それに応じて答えを更新しました。副作用がない限り、ローカルで変更関数を呼び出すことは問題ありません。「純粋な」用語が過負荷になりすぎています...
ジョンパーディ

またtoString、文字列として使用するオブジェクトが純粋かどうかも確認する必要があります。
オレグV.ボルコフ

実際にこれらの決定を行うことができる状況のサブセットしかないため、この答えは正しいとは思わない。考慮:この関数は純粋ですか?function a (o) { return o.method(); }- o渡されるパラメーターに依存するため、実際にこれに答えることはできません。また、以前に認証された純粋な関数が非純粋な実装に変更された場合に何が起こるかを説明することはできません。これは常にjavascriptの潜在的な問題です。
ジュール

11

いいえ。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)すべての可能な入力で終了するために純粋な関数が必要な場合、関数が純粋であるかどうかを判断するために停止の問題を解決する必要あります。これは不可能であることが知られているため、この定義では、純度を計算することはできません。

純粋な関数がいくつかの入力に終了しないように許可されている場合ケースB)は、我々はそのような何かを構築することができます:レッツは、その前提としisPure(f)た場合を計算しf、純粋な関数を定義する文字列です。

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

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

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

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


3
本当です。保守的な分析を行うことは常に可能です-関数が完全に純粋かどうかをチェックすることはできますが、完全に純粋でないかどうかをチェックすることはできません。
SKロジック

多くの場合、簡単に決定できます。JonPurdyによって記述された純粋な関数、または無条件に汚いことをする純粋でない関数。しかし、一般的に、決定できないケースを作成できます。
user281377

1

このstackoverflowの質問には、ここに関連するyfeldblumによる答えがあります。(そして、どういうわけか私は推測できないダウン票を持っています。3歳の何かをアップ票するのは悪いエチケットでしょうか?)彼は、関数が純粋であるかどうかがコメントの停止問題に還元可能であるという証拠を与えます。

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

したがって、質問を少し微調整すれば理論的には不可能、実際には可能です。それがどれほど難しいかは、言語に大きく依存すると思います。不変性と適切なリフレクションに焦点を当てた、よりシンプルでクリーンな言語がより簡単になります。


間違っているので、それはダウン投票されていたと思います。bottom有効なファーストクラスの値であり、このように差別されるに値しません。
SKロジック

0

いい質問ですね。

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

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


1
-1:このテストに合格し、純粋ではない関数を記述するのは簡単ではありません。
mattnz

3
このロジックにより、どのvoid関数も「純粋」になりますが、これは明らかに間違っています。
グレッグヒューギル

1
@Greg:拡​​張により、void foo(void)も純粋でなければなりません。
mattnz

0

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


5
永遠に実行しても、関数が純粋な関数の基準に適合することを軽視することはないようです。
whatsisname

+1:関数が「...は常に同じ結果値を評価して...」で終了することを暗黙的に要求します。
mattnz

2
状態を変更せずに一貫して永久に実行することは、完全に「純粋」です。しかし、もちろん、ここでは用語の問題です。
SKロジック

@mattnz、そのような関数は常にbottom値に評価されます。
SKロジック

1
用語の問題がどこから来ているかを見ることができます。いくつかの解釈では、「純粋な」関数は決定論的であり、実行中に外部と状態または値を決して通信しません。他の解釈では、停止が要件に追加されます。最初の解釈では、関数が純粋かどうかを簡単に判断できます。特定の言語を実行するマシンは、その言語のプログラムが外部と通信するかどうかを判断できるはずです。
-rwong
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.