83

今天我在阅读纯函数,对它的使用感到困惑:

如果一个函数为相同的输入集返回相同的值集并且没有任何可观察到的副作用,则该函数被称为纯函数。

egstrlen()是纯函数,rand()而是不纯函数。

__attribute__ ((pure)) int fun(int i)
{
    return i*i;
}

int main()
{
    int i=10;
    printf("%d",fun(i));//outputs 100
    return 0;
}

http://ideone.com/33XJU

上述程序的行为方式与没有pure声明的情况相同。

将函数声明为pure[如果输出没有变化] 有什么好处?

4

6 回答 6

146

pure让编译器知道它可以对函数进行某些优化:想象一下像这样的代码

for (int i = 0; i < 1000; i++)
{
    printf("%d", fun(10));
}

使用纯函数,编译器可以知道它只需要计算fun(10)一次,而不是 1000 次。对于一个复杂的功能,这是一个巨大的胜利。

于 2012-06-22T09:48:38.393 回答
34

当你说一个函数是“纯的”时,你保证它没有外部可见的副作用(正如评论所说,如果你撒谎,可能会发生坏事)。知道一个函数是“纯的”对编译器有好处,它可以利用这些知识进行某些优化。

以下是GCC 文档关于该pure属性的说明:

纯的

许多函数除了返回值外没有任何影响,它们的返回值仅取决于参数和/或全局变量。这样的函数可以像算术运算符一样进行公共子表达式消除和循环优化。这些函数应该用纯属性声明。例如,

          int square (int) __attribute__ ((pure));

菲利普的回答已经表明,知道一个函数是“纯的”如何有助于循环优化。

这是一个用于消除常见子表达式的方法(给定foo是纯的):

a = foo (99) * x + y;
b = foo (99) * x + z;

可以变成:

_tmp = foo (99) * x;
a = _tmp + y;
b = _tmp + z;
于 2012-06-22T09:49:19.873 回答
29

除了可能的运行时好处之外,纯函数在阅读代码时更容易推理。此外,测试纯函数要容易得多,因为您知道返回值仅取决于参数的值。

于 2012-06-22T09:56:18.780 回答
15

非纯函数

int foo(int x, int y) // possible side-effects

就像一个纯函数的扩展

int bar(int x, int y) // guaranteed no side-effects

在其中,除了显式函数参数 x、y 之外,宇宙的其余部分(或您的计算机可以与之通信的任何东西)作为隐含的潜在输入。同样,除了显式的整数返回值之外,您的计算机可以写入的任何内容都是返回值的隐含部分。

应该清楚为什么推理纯函数比推理非纯函数要容易得多。

于 2012-06-22T15:08:37.497 回答
7

作为一个附加组件,我想提一下,C++11 使用 constexpr 关键字在某种程度上对事物进行了编码。例子:

#include <iostream>
#include <cstring>

constexpr unsigned static_strlen(const char * str, unsigned offset = 0) {
        return (*str == '\0') ? offset : static_strlen(str + 1, offset + 1);
}

constexpr const char * str = "asdfjkl;";

constexpr unsigned len = static_strlen(str); //MUST be evaluated at compile time
//so, for example, this: int arr[len]; is legal, as len is a constant.

int main() {
    std::cout << len << std::endl << std::strlen(str) << std::endl;
    return 0;
}

对 constexpr 使用的限制使得该函数可以证明是纯的。这样,编译器可以更积极地优化(请确保您使用尾递归!)并在编译时而不是运行时评估函数。

因此,要回答您的问题,如果您使用的是 C++(我知道您说的是 C,但它们是相关的),以正确的样式编写纯函数允许编译器使用该函数执行各种很酷的事情: -)

于 2012-06-22T15:40:24.760 回答
4

一般来说,纯函数比编译器可以利用的非纯函数有 3 个优势:

缓存

假设您有f被调用 100000 次的纯函数,因为它是确定性的并且仅取决于其参数,编译器可以计算其值一次并在必要时使用它

并行性

纯函数不会读取或写入任何共享内存,因此可以在单独的线程中运行而不会产生任何意外后果

通过引用传递

一个函数通过值f(struct t)获取它的参数t,另一方面,编译器可以通过t引用传递f它是否声明为纯,同时保证的值t不会改变并有性能提升


除了编译时间的考虑之外,纯函数可以很容易地测试:只需调用它们。

无需构造对象或模拟与数据库/文件系统的连接。

于 2015-05-13T18:53:32.733 回答