今天我正在阅读关于纯函数,与它的使用混淆:
如果函数为同一组输入返回相同的值集并且没有任何可观察的副作用,则称该函数是纯的.
例如strlen(),纯粹的功能,而不纯的功能rand().
__attribute__ ((pure)) int fun(int i)
{
return i*i;
}
int main()
{
int i=10;
printf("%d",fun(i));//outputs 100
return 0;
}
Run Code Online (Sandbox Code Playgroud)
上述程序的行为方式与没有pure声明时相同.
将函数声明为pure[如果输出没有变化]有什么好处?
Phi*_*all 144
pure 让编译器知道它可以对函数进行某些优化:想象一下像
for (int i = 0; i < 1000; i++)
{
printf("%d", fun(10));
}
Run Code Online (Sandbox Code Playgroud)
使用纯函数,编译器可以知道它只需要评估fun(10)一次,而不是1000次.对于复杂的功能,这是一个巨大的胜利.
Arj*_*kar 34
当你说一个函数是'纯'时,你保证它没有外部可见的副作用(正如评论所说,如果你撒谎,可能会发生不好的事情).知道函数是"纯粹的"对编译器有好处,编译器可以使用这些知识来进行某些优化.
以下是GCC文档中有关pure属性的内容:
纯
除返回值外,许多函数都没有效果,它们的返回值仅取决于参数和/或全局变量.这样的函数可以像算术运算符那样经受公共子表达式消除和循环优化.应使用pure属性声明这些函数.例如,
Run Code Online (Sandbox Code Playgroud)int square (int) __attribute__ ((pure));
Philip的答案已经表明了如何知道函数是"纯粹的"可以帮助进行循环优化.
这是一个常见的子表达式消除(给定foo是纯粹的):
a = foo (99) * x + y;
b = foo (99) * x + z;
Run Code Online (Sandbox Code Playgroud)
可以变成:
_tmp = foo (99) * x;
a = _tmp + y;
b = _tmp + z;
Run Code Online (Sandbox Code Playgroud)
Fre*_*abe 27
除了可能的运行时优势之外,在阅读代码时,更容易推理纯函数.此外,测试纯函数要容易得多,因为您知道返回值仅取决于参数的值.
Gio*_*gio 15
一个非纯函数
int foo(int x, int y) // possible side-effects
Run Code Online (Sandbox Code Playgroud)
就像一个纯函数的扩展
int bar(int x, int y) // guaranteed no side-effects
Run Code Online (Sandbox Code Playgroud)
除了显式函数参数x,y之外,你还拥有宇宙的其余部分(或计算机可以与之通信的任何东西)作为隐式潜在输入.同样,除了显式整数返回值之外,计算机可以写入的任何内容都隐含地是返回值的一部分.
应该清楚为什么推理纯函数比非纯函数更容易.
就像一个附加组件一样,我想提一下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;
}
Run Code Online (Sandbox Code Playgroud)
对constexpr使用的限制使得该功能可以证明是纯粹的.这样,编译器可以更积极地进行优化(只需确保使用尾递归!)并在编译时而不是运行时评估函数.
因此,要回答你的问题是,如果你是用C++(我知道你说的C,但它们是相关的),在正确的样式写一个纯函数,编译器可以做各种与功能很酷的事情: - )