関数pure
を次のように定義する2つの条件は次のとおりです。
- 副作用はありません(つまり、ローカルスコープへの変更のみが許可されます)
- 同じ入力が与えられた場合、常に同じ出力を返します
最初の条件が常に真である場合、2番目の条件が真でない場合はありますか?
つまり、それは本当に最初の条件でのみ必要ですか?
関数pure
を次のように定義する2つの条件は次のとおりです。
最初の条件が常に真である場合、2番目の条件が真でない場合はありますか?
つまり、それは本当に最初の条件でのみ必要ですか?
回答:
外側のスコープを変更しないが、それでも不純であると見なされるいくつかの反例を次に示します。
function a() { return Date.now(); }
function b() { return window.globalMutableVar; }
function c() { return document.getElementById("myInput").value; }
function d() { return Math.random(); }
(これは確かにPRNGを変更しますが、観察可能とは見なされません)非定数の非ローカル変数にアクセスするだけで、2番目の条件に違反することができます。
私は常に、純度の2つの条件を補完的なものと考えています。
副作用という用語は、最初の、非ローカル状態を変更する機能のみを指します。ただし、読み取り操作も副作用と見なされる場合があります。読み取り操作が操作であり、書き込みも含まれる場合、その主な目的が値へのアクセスである場合でも同様です。その例としては、ジェネレータの内部状態を変更する疑似乱数の生成、読み取り位置を進める入力ストリームからの読み取り、または「測定の実行」コマンドを含む外部センサーからの読み取りがあります。
prompt("you choose")
副作用がない場合は、一歩下がって副作用の意味を明確にする必要があります。
純粋関数が何であるかを表現する「通常の」方法は、参照透過性の観点からです。関数が参照透過性である場合、その関数は純粋です。
参照透過性とは、大まかに言って、プログラムの意味を変更せずに、プログラムの任意の時点で関数の呼び出しをその戻り値に、またはその逆に置き換えることができることを意味します。
したがって、たとえば、Cprintf
が参照透過性である場合、これら2つのプログラムは同じ意味を持つ必要があります。
printf("Hello");
そして
5;
次のプログラムはすべて同じ意味を持つ必要があります。
5 + 5;
printf("Hello") + 5;
printf("Hello") + printf("Hello");
printf
は、書き込まれた文字数(この場合は5)を返すためです。
それはvoid
関数でさらに明白になります。私が関数を持っているならvoid foo
、
foo(bar, baz, quux);
と同じである必要があります
;
つまり、 foo
つまり、何も返さない、プログラムの意味を変えずに何にも置き換えることができないはずです。
したがって、どちらprintf
もfoo
参照透過性ではなく、したがってどちらも純粋ではないことは明らかです。実際、void
関数は、no-opでない限り、参照透過性になることはありません。
この定義は、あなたが与えたものよりもはるかに扱いやすいと思います。また、任意の粒度で適用できます。個々の式、関数、プログラム全体に適用できます。たとえば、次のような関数について話すことができます。
func fib(n):
return memo[n] if memo.has_key?(n)
return 1 if n <= 1
return memo[n] = fib(n-1) + fib(n-2)
関数を構成する式を分析すると、可変データ構造、つまりmemo
配列を使用しているため、参照透過性がなく、純粋ではないと簡単に結論付けることができます。ただし、関数を見ると、参照透過性であり、したがって純粋であることがわかります。これは、外部純度と呼ばれることもあります。つまり、外の世界には純粋に見えるが、内部では不純に実装されている機能です。
不純物は周囲のすべてに感染しますが、外部の純粋なインターフェイスは一種の「純度バリア」を構築し、不純物は関数の3行にのみ感染し、プログラムの残りの部分には漏れないため、このような関数は依然として有用です。 。これらの3行は、プログラム全体よりも正確さを分析するのがはるかに簡単です。
memo[n]
等であり、読み取りに失敗するとCPUサイクルが無駄になるだけです。
memo[n] = ...
最初に辞書エントリを作成してから、そのエントリに値を格納する場合があります。これにより、別のスレッドが初期化されていないエントリを確認できるウィンドウが残ります。
あなたが説明した2番目の条件は最初の条件よりも弱い制約であるように私には思えます。
例を挙げましょう。コンソールにもログを記録する関数を追加する関数があるとします。
function addOneAndLog(x) {
console.log(x);
return x + 1;
}
指定した2番目の条件が満たされています。この関数は、同じ入力が与えられたときに常に同じ出力を返します。ただし、コンソールへのログ記録の副作用が含まれているため、純粋関数ではありません。
純粋関数とは、厳密に言えば、次の特性を満たす関数です。 参照透過性の。これは、プログラムの動作を変更せずに、関数適用を生成する値に置き換えることができるプロパティです。
以下を追加するだけの関数があるとします。
function addOne(x) {
return x + 1;
}
私たちは、置き換えることができaddOne(5)
て6
、私たちのプログラムではどこでも、何も変更されます。
対照的にaddOneAndLog(x)
、値で置き換えることはできません6
最初の式ではコンソールに何かが書き込まれるのに対し、2番目の式では書き込まれないため、動作を変更せずにプログラムのどこでもません。
addOneAndLog(x)
出力を返す以外に実行されるこの余分な動作は、副作用と見なされます。
Date.now()
は純粋/参照透過性ではありませんが、副作用があるからではなく、その結果が入力だけではないからです。
システムの外部からランダム性の原因が存在する可能性があります。計算の一部に室温が含まれているとします。次に、関数を実行すると、室温のランダムな外部要素に応じて、毎回異なる結果が得られます。プログラムを実行しても状態は変わりません。
とにかく、私が考えることができるすべて。
FP定義の問題は、それらが非常に人工的であるということです。各評価/計算には、評価者に副作用があります。それは理論的には真実です。これを否定することは、FPの謝罪者が哲学と論理を無視していることだけを示しています。「評価」とは、あるインテリジェント環境(機械、脳など)の状態の変化を意味します。これが評価プロセスの性質です。変更なし-「結石」なし。CPUの加熱またはその障害、過熱した場合のマザーボードのシャットダウンなど、その影響は非常に明白です。
参照透過性について話すとき、そのような透明性に関する情報は、システム全体の作成者および意味情報の保持者として人間が利用でき、コンパイラーは利用できない場合があることを理解する必要があります。たとえば、関数は外部リソースを読み取ることができ、そのシグネチャにIOモナドが含まれますが、常に同じ値を返します(たとえば、の結果current_year > 0
)。コンパイラは、関数が常に同じ結果を返すことを知らないため、関数は不純ですが、参照透過性があり、次のように置き換えることができます。True
定数。
したがって、このような不正確さを回避するには、プログラミング言語で数学関数と「関数」を区別する必要があります。Haskellの関数は常に不純であり、それらに関連する純度の定義は常に非常に条件付きです。それらは実際の副作用と物理的特性を備えた実際のハードウェアで実行されますが、これは数学関数では間違っています。これは、「printf」関数を使用した例が完全に正しくないことを意味します。
しかし、すべての数学関数も純粋であるとはt
限りません。パラメーターとして(時間)を持つ各関数は不純である可能t
性があります。関数のすべての効果と確率的性質を保持します。通常、入力信号があり、実際の値がわからない場合は、次のことができます。ノイズさえあります。
最初の条件が常に真である場合、2番目の条件が真でない場合はありますか?
はい
以下の簡単なコードスニペットを検討してください
public int Sum(int a, int b) {
Random rnd = new Random();
return rnd.Next(1, 10);
}
このコードは、同じ入力セットに対してランダムな出力を返しますが、副作用はありません。
一緒に組み合わせたときのポイント#1と#2の両方の全体的な効果は、次のことを意味します。同じi / pを持つ関数Sum
がプログラムの結果に置き換えられた場合、プログラムの全体的な意味は変わりません。これは参照透過性に他なりません。
rnd
は関数をエスケープしないため、状態が変化するという事実は関数の純度には関係ありませんが、Random
コンストラクターが現在の時刻をシード値として使用するという事実は、a
と以外の「入力」があることを意味しb
ます。