纯功能的好处

Gre*_*lin 79 c pure-virtual

今天我正在阅读关于纯函数,与它的使用混淆:

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

例如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)

http://ideone.com/33XJU

上述程序的行为方式与没有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次.对于复杂的功能,这是一个巨大的胜利.

  • 因为您可以修改字符串(从某个地址开始的字符序列)而不修改输入(指向字符串开始的地址的指针),即您无法记住它.它只是一个具有不可变字符串的语言中的纯函数(比如Java). (15认同)
  • 那么`strlen`就不是一个纯函数的好例子 (6认同)
  • @KonradRudolph:想象一下长度为1000的字符串.在它上面调用`strlen`.然后又来了.同样的事情是吗?现在将第二个字符修改为`\ 0`.`strlen`现在仍然返回1000吗?起始地址相同(==输入相同)但该函数现在返回不同的值. (5认同)
  • @mob这是一个很好的反对意见,显然你是对的.我甚至误以为[偶数书](http://books.google.co.uk/books?id=k_ocKY0iegsC&lpg=PA341&dq=pure%20strlen&pg=PA341#v=onepage&q=pure%20strlen&f=false)声称`strlen`(在GCC/glibc中)实际上是纯粹的.但是看看glibc的实现表明这是错误的. (5认同)

Arj*_*kar 34

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

以下是GCC文档中有关pure属性的内容:

除返回值外,许多函数都没有效果,它们的返回值仅取决于参数和/或全局变量.这样的函数可以像算术运算符那样经受公共子表达式消除和循环优化.应使用pure属性声明这些函数.例如,

          int square (int) __attribute__ ((pure));
Run Code Online (Sandbox Code Playgroud)

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)

  • 我不确定是否有这样做,但纯函数也允许编译器在函数被调用时重新排序,如果它认为重新排序将是有益的.当存在副作用的可能性时,编译器需要更加保守. (3认同)

Fre*_*abe 27

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

  • +1,你对测试的观点很有意思.无需设置和拆卸. (2认同)

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之外,你还拥有宇宙的其余部分(或计算机可以与之通信的任何东西)作为隐式潜在输入.同样,除了显式整数返回值之外,计算机可以写入的任何内容都隐含地是返回值的一部分.

应该清楚为什么推理纯函数比非纯函数更容易.


Rob*_*son 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;
}
Run Code Online (Sandbox Code Playgroud)

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

因此,要回答你的问题是,如果你是用C++(我知道你说的C,但它们是相关的),在正确的样式写一个纯函数,编译器可以做各种与功能很酷的事情: - )