web-dev-qa-db-ja.com

Math.random()を呼び出す関数は純粋ですか?

以下は純粋な関数ですか?

function test(min,max) {
   return  Math.random() * (max - min) + min;
}

私の理解するところでは、純粋な関数はこれらの条件に従うということです。

  1. パラメータから計算された値を返します。
  2. 戻り値を計算する以外には何もしません。

この定義が正しい場合、私の関数は純粋な関数ですか?それとも、純粋な関数を定義しているものに対する私の理解は間違っていますか?

108
Kiwi Rupela

いいえ、ちがいます。入力が同じ場合、この関数は異なる値を返します。そして、入力と出力をマッピングする「テーブル」を構築することはできません。

ウィキペディアの記事から 純粋な機能

関数は、常に同じ引数値を指定して同じ結果値を評価します。関数の結果値は、プログラムの実行が進行している間、またはプログラムのさまざまな実行の間に変化する可能性のある隠された情報や状態には依存しません。また、I/Oデバイスからの外部入力にも依存しません。

また、純粋な関数は、 このスレッド で説明されているように、入力と出力からのマッピングを表すテーブルで置き換えることができます。

この関数を書き換えて純粋な関数に変更したい場合は、乱数を引数として渡す必要があります。

function test(random, min, max) {
   return random * (max - min) + min;
}

そしてそれを次のようにして呼び出します(たとえば、2と5を最小値と最大値として)。

test( Math.random(), 2, 5)
178

あなたの質問に対する簡単な答えは、Math.random()がルール#2に違反しているということです。

Math.random()の存在はこの関数が純粋ではないことを意味することをここに他の多くの答えが指摘しました。しかし、なぜMath.random()がそれを使う関数を汚染するのかと言う価値があります。

すべての疑似乱数ジェネレータと同様に、Math.random()は "seed"値で始まります。次に、その値を一連の低レベルのビット操作またはその他の操作の開始点として使用し、その結果予測不可能な(しかし実際にはrandomではない)出力が生成されます。

JavaScriptでは、関連するプロセスは実装依存であり、他の多くの言語とは異なり、JavaScriptは シードを選択する方法はありません

実装は乱数生成アルゴリズムに初期シードを選択します。ユーザーが選択したりリセットしたりすることはできません。

それがこの関数が純粋ではない理由です。JavaScriptは本質的にあなたが制御できない暗黙の関数パラメータを使っています。計算されて他の場所に格納されているデータからそのパラメータを読み取っているため、定義内のルール#2に違反しています。

これを純粋な関数にしたい場合は、 here で説明されている代わりの乱数発生器の1つを使用できます。そのジェネレータをseedable_randomと呼びます。 1つのパラメータ(シード)を取り、「乱数」を返します。もちろん、この数はまったくランダムではありません。それは種によって一意に決定されます。これが純粋な関数である理由です。 seedable_randomの出力は、入力に基づいて出力を予測するのが難しいという意味で、単なる「ランダム」です。

この関数の純粋なバージョンはthreeparametersを取る必要があるでしょう。

function test(min, max, seed) {
   return  seedable_random(seed) * (max - min) + min;
}

与えられた(min, max, seed)パラメータのどんなトリプルに対しても、これは常に同じ結果を返します。

seedable_randomの出力を本当にrandomにしたい場合は、シードをランダム化する方法を見つける必要があります。そして、あなたが使ったどんな戦略でも必然的に純粋でないものになるでしょう。なぜならそれはあなたがあなたの機能の外部の情報源から情報を集めることを要求するでしょう。 mtraceur and jpmc26 私に思い出させるように、これにはすべての物理的アプローチが含まれます。 ハードウェア乱数発生器レンズキャップ付きウェブカメラ大気雑音収集装置 - 偶数 溶岩ランプ 。これらすべては、関数の外側で計算され格納されたデータを使用することを含みます。

49
senderle

純粋関数は、観察可能な副作用なしに、戻り値がその入力値によってのみ決定される関数です。

Math.randomを使うことで、あなたは入力値以外の何かによってその値を決定しています。純粋な機能ではありません。

出典

38
TKoL

いいえ、それは純粋な関数ではありません。出力は与えられた入力には依存しないからです(only(Math.random()は任意の値を出力できます))。同じ入力.

関数が純粋であれば、同じ入力を持つ複数の呼び出しを最適化して、以前の呼び出しの結果を再利用するのが安全です。

少なくとも私にとってはP.S、そして他の多くの人にとっては、reduxは純関数という用語を一般的にしました。 reduxのドキュメントから直接

レデューサーの内部では絶対にしてはいけないこと:

  • その引数を変更します。

  • API呼び出しやルーティング遷移などの副作用を実行します。

  • 非純粋関数を呼び出す。 Date.now()またはMath.random()。

25
Shubhnik Singh

数学的な観点からは、あなたの署名は違います。

test: <number, number> -> <number>

しかし

test: <environment, number, number> -> <environment, number>

environmentMath.random()の結果を提供することができます。そして実際に乱数を生成することは副作用として環境を変化させるので、あなたも新しい環境を返します、それは最初のものと等しくありません!

つまり、初期引数(<number, number>部分)から来ない種類の入力が必要な場合は、(この例ではMathの状態を提供する)実行環境を用意する必要があります。同じことが、I/Oなど、他の回答で言及されていることにも当てはまります。


例えとして、これがオブジェクト指向プログラミングがどのように表現されるかであることにも気づくでしょう。

SomeClass something
T result = something.foo(x, y)

実際に使用している

foo: <something: SomeClass, x: Object, y: Object> -> <SomeClass, T>

そのメソッドが呼び出されたオブジェクトは環境の一部です。そしてなぜSomeClassが結果の一部なのでしょうか。 somethingの状態も変更された可能性があるためです。

20

純粋関数は常に同じ入力に対して同じ値を返します。純粋な関数は予測可能で、参照透過的です。つまり、関数呼び出しを返された出力に置き換えることができ、プログラムの動作が変わることはありません。

https://github.com/MostlyAdequate/mostly-adequate-guide/blob/master/ch3.md

11
Rishabh Mishra

この関数がどのように非決定的であるかを正しく指摘している他の答えに加えて、それはまた副作用があります:それはmath.random()への将来の呼び出しが異なる答えを返すようになるでしょう。そして、そのプロパティを持っていない乱数ジェネレータは、一般的に、OSによって提供されたランダムなデバイスから読み取るような、ある種のI/Oを実行します。どちらも純粋な関数に対しては冗長です。

9
Davislor

いいえ、違います。結果をまったく把握できないため、このコードはテストできません。そのコードをテスト可能にするには、乱数を生成するコンポーネントを抽出する必要があります。

function test(min, max, generator) {
  return  generator() * (max - min) + min;
}

これで、ジェネレータをモックしてコードを正しくテストできます。

const result = test(1, 2, () => 3);
result == 4 //always true

そしてあなたの "本番"コードでは:

const result = test(1, 2, Math.random);
7
Héctor

次のことで問題ないでしょうか。

return ("" + test(0,1)) + test(0,1);

と同等である

var temp = test(0, 1);
return ("" + temp) + temp;

おわかりのように、pureの定義は、出力がその入力以外のもので変化しない関数です。 JavaScriptに純粋な関数をタグ付けしてこれを利用する方法があるとしたら、オプティマイザは最初の式を2番目の式に書き換えることができます。

私はこれに関して実際的な経験があります。 SQLサーバーは "純粋な"関数でgetdate()newid()を許可し、オプティマイザは意のままに呼び出しを重複排除します。時にはこれは何かおかしなことをするでしょう。

2
Joshua