5

TDPL,第 167:

只要函数中的可变状态是完全暂时的(即,分配在堆栈上)和私有的(即,不通过引用可能污染它的函数传递),那么该函数就可以被认为是纯的。

import std.stdio : writeln;

struct M{
  int[4] _data;

  pure ref int opIndex(size_t i){ return _data[i]; }
}

pure M foo(ref M m){

  m[0] = 1234;
  return m;
}

void main(){

  M m1 = M([7, 7, 7, 7]);

  writeln(m1);
  foo(m1);
  writeln(m1);
}

// output:
// M([7, 7, 7, 7])
// M([1234, 7, 7, 7])

可变状态是暂时的,因为它在堆栈上,对吗?但这不是私人的。那么如何foo()允许修改m1呢?

4

2 回答 2

6

pure自 TDPL 发布以来已经扩展了一点,因为pure正如 TDPL 所描述的那样,事实证明它过于严格,无法在简单的数学函数等之外使用。您可以查看当前定义的在线文档,但基本上可以归结为:

  1. pure函数不能访问任何可以在程序过程中发生变化的模块级或静态变量(它们必须是const值类型或immutablepure函数中访问)。

  2. pure函数不能调用任何不是pure.

  3. pure函数不能执行 I/O。

就是这样。没有其他限制。但是,如果要优化一个函数,即使在一个语句中多次使用它,它也只会被调用一次,则需要额外的限制。pure即:

  • 函数的参数必须可以immutable或隐式转换为immutable.

从理论上讲,这可以扩展到要求函数的参数必须是immutable或隐式转换为immutable(以便const在给定参数时可以优化带有参数的immutable函数),但目前情况并非如此。

这样pure的功能有时被称为“强” pure,而那些无法优化的则被称为“弱” pure。TDPL 描述了强pure函数。添加了弱pure功能以使其pure更普遍可用。

虽然弱pure函数可以改变它们的参数,但它们不能改变全局状态,所以当它们被强pure函数(不能改变它们的参数)调用时,保证强pure函数的返回值对于相同的函数总是相同的争论仍然成立。本质上,因为弱pure函数不能改变全局状态,所以pure它们是调用它们的强函数的私有状态的一部分。因此,它非常符合 Andrei 在第5.11.1.1 节pure中描述的pureTDPL中的情况,只是函数的私有状态已扩展为允许在不改变全局状态的情况下更改其私有状态的函数。

自 TDPL 以来添加的另一件重要的事情pure是函数属性推断。pure, nothrow, 和@safe被推断为模板函数(虽然不是普通函数)。因此,如果模板化函数可以pure,那么现在就是 pure. 它的纯度取决于它的实例化。因此,可以pure与模板函数一起使用,而以前通常不能,因为如果你做了它pure,它将无法与不纯函数一起使用。但是如果你没有做到pure,那么你就不能用一个pure函数来使用它,所以这是一个主要的问题pure. 幸运的是,属性推断现在解决了这个问题。只要模板化函数在实例化时遵循上面列出的规则,那么它就被认为是pure.

于 2011-12-20T08:57:57.697 回答
5

引用被认为是函数参数的this一部分,由于函数是弱纯的,你可以修改参数。在this考虑部分输入的状态下,函数仍然满足具有相同输出和相同输入的条件。

考虑这个完全合法的例子,它输出2

import std.stdio : writeln;

struct S
{
    int foo = 0;
    pure void set(size_t i){ foo = i; }
}


void main()
{
    S s;
    s.set(2);
    writeln(s.foo);
}

据我所知,TDPL 发布后,pure 的定义得到了扩展。这本书描述了强纯函数。在那之后,发生了两个发展:添加了弱纯函数,允许改变它们的参数。此外,还为模板函数添加了纯度推断,因此您可以使用模板函数的实例化,只要它是纯的,即使模板函数没有用pure.

于 2011-12-20T08:13:55.533 回答