在使用 D 2.0 时,我发现了以下问题:

示例1:

pure string[] run1()
{
   string[] msg;
   msg ~= "Test";
   msg ~= "this.";
   return msg;
}

这将按预期编译并工作。

当我尝试将字符串数组包装在一个类中时,我发现我无法让它工作:

class TestPure
{
    string[] msg;
    void addMsg( string s )
    {
       msg ~= s;
    }
};

pure TestPure run2()
{
   TestPure t = new TestPure();
   t.addMsg("Test");
   t.addMsg("this.");
   return t;
}

该代码无法编译,因为 addMsg 函数不纯。我无法使该函数变得纯粹,因为它改变了 TestPure 对象。我错过了什么吗?或者这是一个限制?

以下内容可以编译:

pure TestPure run3()
{
    TestPure t = new TestPure();
    t.msg ~= "Test";
    t.msg ~= "this.";
    return t;
}

~= 运算符不会被实现为 msg 数组的不纯函数吗?为什么编译器不会在 run1 函数中抱怨这一点?

有帮助吗?

解决方案

自v2.050起,D放宽了 pure 的定义,接受了所谓的“弱纯粹”。功能也是。这是指“不读取或写入任何全局可变状态的函数&QUOT ;.弱函数与函数语言意义上的纯函数不一样。唯一的关系是他们创造了真正纯粹的功能,a.k.a。“强烈纯粹”。函数可以调用弱函数,就像OP的例子一样。

有了这个, addMsg 可以标记为(弱) pure ,因为只有本地变量 this.msg 被更改:

class TestPure
{
    string[] msg;
    pure void addMsg( string s )
    {
       msg ~= s;
    }
};

当然,现在您可以使用(强)函数 run2 而无需修改。

pure TestPure run2()
{
   TestPure t = new TestPure();
   t.addMsg("Test");
   t.addMsg("this.");
   return t;
}

其他提示

其他人已经指出addMsg不纯,不能纯,因为它会改变对象的状态。

使其纯粹的唯一方法是封装您正在进行的更改。最简单的方法是通过返回变异,有两种方法可以实现这一点。

首先,你可以这样做:

class TestPure
{
    string[] msg;
    pure TestPure addMsg(string s)
    {
        auto r = new TestPure;
        r.msg = this.msg.dup;
        r.msg ~= s;
        return r;
    }
}

您需要复制前一个数组,因为在纯函数中,此引用实际上是const。请注意,您可以通过分配最终大小的新数组然后自行复制元素来更好地进行复制。你会像这样使用这个函数:

pure TestPure run3()
{
    auto t = new TestPure;
    t = t.addMsg("Test");
    t = t.addMsg("this.");
    return t;
}

这样,突变仅限于每个纯函数,并通过返回值传递更改。

编写TestPure的另一种方法是使成员const并在将其传递给构造函数之前完成所有变异:

class TestPure
{
    const(string[]) msg;
    this()
    {
        msg = null;
    }
    this(const(string[]) msg)
    {
        this.msg = msg;
    }
    pure TestPure addMsg(string s)
    {
        return new TestPure(this.msg ~ s);
    }
}

希望有所帮助。

请回顾一下纯函数的定义:

纯函数是对于相同参数产生相同结果的函数。为此,一个纯函数:

  • 具有全部不变或可隐式转换为不变的参数
  • 不读取或写入任何全局可变状态

使用纯函数的效果之一是它们可以安全地并行化。但是,并行执行函数的多个实例并不安全,因为它们可能同时修改类实例,从而导致同步问题。

认为您的代码在概念上是正确的。但是,您可能已经发现编译器的语义分析不如您的大脑那样好的情况。

考虑班级来源不可用的情况。在这种情况下,编译器无法告诉 addMsg 仅修改成员变量,因此它不允许您从纯函数调用它。

要在您的情况下允许它,它必须具有此类用法的特殊情况处理。添加的每个特殊情况规则都会使语言更复杂(或者,如果没有记录,则会降低其可移植性)

只是预感,但此功能并不总是返回相同的结果。

请参阅,它返回对某个对象的引用,虽然该对象将始终包含相同的数据,但是对同一函数的多次调用返回的对象并不相同;也就是说,它们没有相同的内存地址。

当你返回对象的引用时,你实际上是在返回一个内存地址,这个地址在几次调用时会有所不同。

另一种思考方式,返回值的一部分是对象的内存地址,它取决于某些全局状态,如果函数的输出依赖于全局状态,那么它不是纯粹的。天哪,它甚至不必依赖它;只要函数读取全局状态,那么它就不是纯粹的。通过调用“new”,您正在阅读全局状态。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top