10

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関数でそれについて文句を言わないのですか?

4

5 に答える 5

6

v2.050以降、Dはpure、いわゆる「弱純粋」関数も受け入れるように定義を緩和しました。これは、「グローバルな可変状態を読み書きしない」関数を指します。弱く純粋関数は、関数型言語の意味での純粋関数と同じではありません。唯一の関係は、OPの例のように、実際の純粋関数、別名「非常に純粋な」関数が弱い関数を呼び出すことができるようにすることです。

これにより、ローカル変数のみが変更されるため、 (弱く)としてマークaddMsg できます。purethis.msg

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

pureそしてもちろん、これで(強力に)関数を変更せずに使用できるようになりましたrun2

pure TestPure run2()
{
   TestPure t = new TestPure();
   t.addMsg("Test");
   t.addMsg("this.");
   return t;
}
于 2011-03-07T21:15:58.390 に答える
4

addMsgは純粋ではなく、オブジェクトの状態を変更するため純粋ではないことをすでに指摘している人もいます。

それを純粋にする唯一の方法は、行っている変更をカプセル化することです。これを行う最も簡単な方法はreturnmutationを使用することであり、これを実装する方法は2つあります。

まず、次のように行うことができます。

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);
    }
}

お役に立てば幸いです。

于 2009-06-18T04:19:46.793 に答える
3

純粋関数の定義を確認してください。

純粋関数は、同じ引数に対して同じ結果を生成する関数です。そのために、純粋関数:

  • すべて不変であるか、暗黙的に不変に変換可能なパラメーターがあります
  • グローバルな可変状態を読み書きしません

純粋関数を使用することの効果の1つは、それらを安全に並列化できることです。ただし、関数の複数のインスタンスを並行して実行することは安全ではありません。両方がクラスインスタンスを同時に変更し、同期の問題を引き起こす可能性があるためです。

于 2009-06-17T18:36:43.720 に答える
0

あなたのコードは概念的に正しいと思います。ただし、コンパイラのセマンティック分析が脳の分析ほど良くない場合があります。

クラスのソースが利用できない場合を考えてみましょう。その場合、コンパイラーはaddMsgメンバー変数を変更するだけであると判断する方法がないため、純粋関数から呼び出すことはできません。

あなたのケースでそれを可能にするために、それはこのタイプの使用法のための特別なケース処理を持たなければならないでしょう。追加されたすべての特殊なケースのルールは、言語をより複雑にします(または、文書化されていない場合は、移植性が低下します)

于 2009-06-17T22:05:21.957 に答える
-1

ただの予感ですが、この関数が常に同じ結果を返すとは限りません。

参照してください。オブジェクトへの参照が返されます。オブジェクトには常に同じデータが含まれますが、同じ関数への複数の呼び出しによって返されるオブジェクトは同一ではありません。つまり、それらは同じメモリアドレスを持っていません。

オブジェクトへの参照を返すと、基本的にメモリアドレスが返されます。これは、複数の呼び出しで異なります。

別の見方をすれば、戻り値の一部はオブジェクトのメモリアドレスであり、これはいくつかのグローバル状態に依存します。関数の出力がグローバル状態に依存する場合、それは純粋ではありません。地獄、それはそれに依存する必要さえありません。関数がグローバル状態を読み取る限り、それは純粋ではありません。「new」と呼ぶことで、あなたはグローバルな状態を読んでいます。

于 2009-06-17T18:42:30.560 に答える