函数式编程中如何正确识别纯函数?

Dev*_*lva 2 javascript functional-programming function

这个函数是纯函数还是非纯函数?

function greet(name) {
    return "Hi I'm " + name;
}
Run Code Online (Sandbox Code Playgroud)

纯函数的特性是它没有副作用,并且对于相同的输入总是返回相同的输出。

在这里,它会产生副作用(返回一个字符串)。字符串连接"Hi, I'm " + name引入了对外部状态的依赖。杂质是由“嗨,我是”构成的。

我们可以重写下面的代码以使其更加纯净。

//pure function
function pureGreet(greeting, name) {
    return greeting + name;
}

//console output
const hiGreeting     = "Hi, I'm ";

const actualGreeting = pureGreet(hiGreeting, "John");

console.log(actualGreeting);
Run Code Online (Sandbox Code Playgroud)

这个解释正确吗?

Mar*_*ann 5

不,这个解释不正确。

在这里,它产生副作用(返回一个字符串)

不,返回值不是副作用。这就是函数的作用。

该字符串"Hi I'm "是一个常量,因此不会改变构成引用透明度的特征:

  • 给定相同的输入,它总是返回相同的输出吗?是的。
  • 它有副作用吗?不。

因此它是一个纯函数。

常数只是常数。你可以想象另一个总是在输入上加 2 的函数:

function add2(i) {
    return 2 + i;
}
Run Code Online (Sandbox Code Playgroud)

这也是纯粹的,因为2是一个常数。add2(2)总是返回4,并且add2(3)总是返回5,等等。

字符串连接“Hi, I'm” + name 引入了对外部状态的依赖

该字符串"Hi, I'm "实际上不是函数体外部的,而是一个实现细节。

也就是说,依赖外部状态本身并不是杂质的原因,只要外部状态是不可变的。这就是闭包的作用。


顺便说一句,泰森·威廉姆斯向我指出,纯度引用透明度之间存在微妙的区别。它在像 Haskell 这样的语言中变得很重要,但底线是你可以通过“不纯的实现”拥有引用透明的函数。F# 基础库经常执行此操作。

如果您真的感兴趣,我不知道有什么比泰森·威廉姆斯最初让我意识到这种区别的长线程更好的链接位置了:https: //blog.ploeh.dk/2020/02/24/辨别和维护纯度/#d28c24c07228400f9ed141f97b5c72b5