问题
Is the following a pure function?
function test(min,max) {
return Math.random() * (max - min) + min;
}
My understanding is that a pure function follows these conditions:
- It returns a value computed from the parameters
- It doesn't do any work other than calculating the return value
If this definition is correct, is my function a pure function? Or is my understanding of what defines a pure function incorrect?
回答1:
No, it's not. Given the same input, this function will return different values. And then you can't build a 'table' that maps the input and the outputs.
From the Wikipedia article for Pure function:
The function always evaluates the same result value given the same argument value(s). The function result value cannot depend on any hidden information or state that may change while program execution proceeds or between different executions of the program, nor can it depend on any external input from I/O devices
Also, another thing is that a pure function can be replaced with a table which represents the mapping from the input and output, as explained in this thread.
If you want to rewrite this function and change it to a pure function, you should pass the random value as an argument too
function test(random, min, max) {
return random * (max - min) + min;
}
and then call it this way (example, with 2 and 5 as min and max):
test( Math.random(), 2, 5)
回答2:
The simple answer to your question is that Math.random()
violates rule #2.
Many other answers here have pointed out that the presence of Math.random()
means that this function is not pure. But I think it's worth saying why Math.random()
taints functions that use it.
Like all pseudorandom number generators, Math.random()
starts with a "seed" value. It then uses that value as the starting point for a chain of low-level bit manipulations or other operations that result in an unpredictable (but not really random) output.
In JavaScript, the process involved is implementation-dependent, and unlike many other languages, JavaScript provides no way to select the seed:
The implementation selects the initial seed to the random number generation algorithm; it cannot be chosen or reset by the user.
That's why this function isn't pure: JavaScript is essentially using an implicit function parameter that you have no control over. It's reading that parameter from data calculated and stored elsewhere, and therefore violates rule #2 in your definition.
If you wanted to make this a pure function, you could use one of the alternative random number generators described here. Call that generator seedable_random
. It takes one parameter (the seed) and returns a "random" number. Of course, this number isn't really random at all; it is uniquely determined by the seed. That's why this is a pure function. The output of seedable_random
is only "random" in the sense that predicting the output based on the input is difficult.
The pure version of this function would need to take three parameters:
function test(min, max, seed) {
return seedable_random(seed) * (max - min) + min;
}
For any given triple of (min, max, seed)
parameters, this will always return the same result.
Note that if you wanted the output of seedable_random
to be truly random, you'd need to find a way to randomize the seed! And whatever strategy you used would inevitably be non-pure, because it would require you to gather information from a source outside your function. As mtraceur and jpmc26 remind me, this includes all physical approaches: hardware random number generators, webcams with lens caps, atmospheric noise collectors -- even lava lamps. All of these involve using data calculated and stored outside the function.
回答3:
A pure function is a function where the return value is only determined by its input values, without observable side effects
By using Math.random, you are determining its value by something other than input values. It's not a pure function.
source
回答4:
No, it isn't a pure function because its output doesn't depend only on the input provided (Math.random() can output any value), while pure functions should always output the same value for same inputs.
If a function is pure, it's safe to optimize away multiple calls with the same inputs and just reuse the result of an earlier call.
P.S for me at least and for many others, redux made the term pure function popular. Straight from the redux docs:
Things you should never do inside a reducer:
Mutate its arguments;
Perform side effects like API calls and routing transitions;
Call non-pure functions, e.g. Date.now() or Math.random().
回答5:
From mathematical point of view, your signature is not
test: <number, number> -> <number>
but
test: <environment, number, number> -> <environment, number>
where the environment
is capable of providing results of Math.random()
.
And actually generating the random value mutates the environment as a side effect, so you also return a new environment, which is not equal to the first one!
In other words, if you need any kind of input that does not come from initial arguments (the <number, number>
part), then you need to be provided with execution environment (that in this example provides state for Math
). The same applies to other things mentioned by other answers, like I/O or such.
As an analogy, you can also notice this is how object-oriented programming can be represented - if we say, e.g.
SomeClass something
T result = something.foo(x, y)
then actually we are using
foo: <something: SomeClass, x: Object, y: Object> -> <SomeClass, T>
with the object that has its method invoked being part of the environment. And why the SomeClass
part of result? Because something
's state could have changed as well!
回答6:
Pure functions always return the same value for same input. Pure functions are predictable and are referential transparent which means that we can replace the function call with the output returned and it will not change the working of the program.
https://github.com/MostlyAdequate/mostly-adequate-guide/blob/master/ch3.md
回答7:
In addition to the other answers that correctly point out how this function is non-deterministic, it also has a side-effect: it will cause future calls to math.random()
to return a different answer. And a random-number generator that doesn’t have that property will generally perform some kind of I/O, such as to read from a random device provided by the OS. Either is verboten for a pure function.
回答8:
No, it isn't. You can't figure out the result at all, so this piece of code can't be tested. To make that code testable, you need to extract the component that generates the random number:
function test(min, max, generator) {
return generator() * (max - min) + min;
}
Now, you can mock the generator and test your code properly:
const result = test(1, 2, () => 3);
result == 4 //always true
And in your "production" code:
const result = test(1, 2, Math.random);
回答9:
Would you be fine with the following:
return ("" + test(0,1)) + test(0,1);
be equivalent to
var temp = test(0, 1);
return ("" + temp) + temp;
?
You see, the definition of pure is a function whose output does not change with anything other than its inputs. If we say that JavaScript had a way to tag a function pure and take advantage of this, the optimizer would be allowed to rewrite the first expression as the second.
I have practical experience with this. SQL server allowed getdate()
and newid()
in "pure" functions and the optimizer would dedupe calls at will. Sometimes this would do something dumb.
来源:https://stackoverflow.com/questions/47035166/is-a-function-that-calls-math-random-pure