【问题标题】:How is this pure function able to modify non-private state?这个纯函数如何能够修改非私有状态?
【发布时间】:2012-01-24 05:40:43
【问题描述】:

TDPL, p. 167:

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

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呢?

【问题讨论】:

  • 我一直在尝试清理pure 标签,因为它有时指的是纯虚函数,有时指的是pure,有时指的是pure - 等等。但我对d 一无所知。您能否确认我的标签编辑是否合适? purely-functional 可以解决这个问题吗?我创建了pure-function,所以如果purely-functional 有效,我认为使用现有标签会更好。
  • @RichardJPLeGuen Pure 和functional purity 一样,所以纯函数可以解决这个问题。纯粹的功能,不是那么多。
  • .. 虽然Purely Functional 列在该维基百科页面的相关链接中。

标签: d pure-function


【解决方案1】:

pure 自 TDPL 发布以来已有所扩展,因为 TDPL 所描述的 pure 限制性太强,无法用于简单的数学函数等。您可以查看the online documentation 的当前定义,但它基本上归结为:

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

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

  3. pure 函数无法执行 I/O。

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

  • 函数的参数必须是immutable 或隐式转换为immutable

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

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

虽然弱pure 函数可以改变它们的参数,但它们不能改变全局状态,所以当它们被强pure 函数调用时(不能 em> 更改它们的参数),保证强 pure 函数的返回值对于相同的参数将始终相同。本质上,因为弱pure 函数不能改变全局状态,它们是调用它们的强pure 函数的私有状态的一部分。因此,它非常符合 Andrei 在第 5.11.1.1 节中描述的内容 pure is as pure Does 在 TDPL 中,除了函数的私有状态已扩展为允许以下函数可以在不更改全局状态的情况下更改其私有状态。

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

【讨论】:

  • 我想我必须让这一切陷入困境并习惯pure void opIndexAssign(T value, size_t i){ ... }pure T opIndex(size_t i) const{ ... }之类的东西
  • 只要把pure想成是函数不能访问可变全局状态,然后让编译器在可以的时候进行优化。是的,pure 修饰符最终比功能上的 pure 更多的功能,但 仍然是使功能上的 pure 功能成为可能和可能被优化的原因。
【解决方案2】:

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 修饰,只要它是纯粹的,您就可以使用模板函数的实例化。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-12-02
    • 1970-01-01
    • 2013-11-09
    • 1970-01-01
    • 1970-01-01
    • 2018-12-31
    • 1970-01-01
    • 2018-03-17
    相关资源
    最近更新 更多