纯function的好处
今天我正在阅读关于纯function的一些内容,对它的使用感到困惑:
如果函数为同一组input返回相同的一组值并且没有任何可观察的副作用,则称该函数是纯粹的。
例如strlen()
是一个纯函数,而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
函数有什么好处(如果输出没有变化)?
pure
允许编译器知道它可以对函数进行某些优化:想象一下类似的代码
for (int i = 0; i < 1000; i++) { printf("%d", fun(10)); }
使用纯函数,编译器可以知道它需要一次而不是一次性评估fun(10)
,而不是1000次。 对于一个复杂的function来说,这是一个巨大的胜利。
当你说一个函数是“纯粹的”时,你保证它没有外部可见的副作用(并且如评论所说,如果你说谎,坏事就会发生)。 知道一个函数是“纯粹的”对于编译器来说是有好处的,编译器可以利用这些知识来进行某些优化。
以下是GCC文档关于pure
:
纯
许多函数除返回值外没有任何影响,它们的返回值仅取决于参数和/或全局variables。 这样的函数可以像算术运算符一样受到常见的子expression式消除和循环优化。 这些函数应该用属性pure声明。 例如,
int square (int) __attribute__ ((pure));
Philip的回答已经显示了如何认识一个函数是“纯粹的”可以帮助循环优化。
这是一个常见的子expression式消除(给foo
是纯的):
a = foo (99) * x + y; b = foo (99) * x + z;
可以变成:
_tmp = foo (99) * x; a = _tmp + y; b = _tmp + z;
除了可能的运行时间的好处之外,在阅读代码时,纯函数更容易推理。 此外,testing纯函数要容易得多,因为您知道返回值仅取决于参数的值。
一个非纯函数
int foo(int x, int y) // possible side-effects
就像一个纯函数的扩展
int bar(int x, int y) // guaranteed no side-effects
除了明确的函数参数x,y,宇宙的其余部分(或者你的计算机可以与之通信的任何东西)之外,还有一个隐含的潜在input。 同样,除了显式的整型返回值之外,您的计算机可以写入的任何内容都是隐式返回值的一部分。
应该清楚,为什么比单纯的函数更容易推理纯函数。
就像一个插件一样,我想提一下,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的使用的限制使得这个函数可以certificate是纯粹的。 这样,编译器可以更积极地进行优化(请确保使用尾recursion,请!),并在编译时而不是运行时评估函数。
所以,要回答你的问题,如果你正在使用C ++(我知道你说过C,但是它们是相关的),那么用正确的风格编写一个纯函数就可以让编译器用这个函数做各种很酷的事情: – )
一般来说,纯函数比编译器可以利用的不纯函数有三个优点:
高速caching
可以说,你有纯函数f
被称为100000次,因为它是确定性的,只依赖于它的参数,编译器可以计算一次它的值,并在必要时使用它
排比
纯函数不读取或写入任何共享内存,因此可以在单独的线程中运行,而不会有任何意外的后果
通过引用传递
函数f(struct t)
通过值得到它的参数t
,另一方面,如果编译器被声明为纯的,那么编译器可以通过引用f
来传递t
,同时保证t
的值不会改变并且具有性能增益
除了编译时间的考虑之外,纯函数可以很容易地testing:只需调用它们。
不需要构build对象或模拟连接到DB /文件系统。