2009-06-17 6 views
10

D 2.0で、純粋な使用するためにはどうすれば以下のような問題点を発見しました。 はD 2.0で遊んでいる間

私は私がこれを動作させることはできません見つけるクラスで文字列配列をラップしよう

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関数のそれについて不平を言うことはありませんか?

+0

[タグ:pure]タグをクリーンアップしようとしました。時には純粋な仮想関数を参照することがあります。時には[pure](http://beebole.com/pure/) [pure](http://ja.wikipedia.org/wiki/Pure_(programming_language)) - 他のものの中から。しかし、私は[タグ:d2]について何も知らない。私のタグ編集が適切かどうか確認できますか?このタグは[タグ:pure-function]を作成しました。[tag:pure-function]を作成したので、既存のタグを使用する方が良いと思います。 –

答えて

6

v2.050以降、Dはpureの定義を緩和し、いわゆる「弱い純粋な」関数も受け入れました。これは、 "do not read or write any global mutable state"という関数を指します。弱く純粋な関数は、であり、関数言語の意味での純粋な関数と同じではないです。唯一の関係は、彼らが本当の純粋な関数を作っているということです。OPの例のように弱いものを呼び出すことができる「強く純粋な」関数です。これにより

ローカル変数のみthis.msgが変更されているので、addMsgは、pure(弱い)としてマークすることができます。

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; 
} 
+1

非常にクールです。スタックオーバーフローの時代遅れになる回答を扱うための良い方法がないのは面白いです。 –

+0

wait、弱く純粋な関数はクラスインスタンス変数を変更できますか?この場合、msgは関数 "addMsg"のスコープから外れていないので、変更できません。これが可能であれば、純粋な関数を書くことや別の関数の中にデリゲートすることで、その関数の状態を変更するのは間違っていますか? –

+0

@Andrew:範囲外ではありません。メンバー関数には暗黙の 'this'パラメータがあり、' msg'は 'this'から到達可能です。 – kennytm

3

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

ピュアな機能は同じ引数で同じ結果を生成する関数です。そのために、純粋な機能:

  • はすべて不変であるか
  • を不変に暗黙的に変換されているパラメータは効果の

一つの任意のグローバル可変状態を読み書きしない持っています純粋な関数を使用することは、それらを安全に並列化できることです。ただし、クラスインスタンスを同時に変更して同期問題を引き起こす可能性があるため、関数の複数のインスタンスを同時に実行することは安全ではありません。

+0

"クラスのインスタンスを同時に変更することもできます。"どうやって? – hasen

+0

auto tp = new TestPure; void threadFunc(){tp.addMsg( "Hello world"!); } 新しいスレッド(&threadFunc); 新しいスレッド(&threadFunc); threadFuncを実行している2つのスレッドが同時にtpインスタンスに書き込もうとしないことを保証することは不可能です。したがって、addMsgは純粋ではありません。 –

+0

Ugh ...上記のコメントを除いて、一緒に悩んでいない:http://dump.thecybershadow.net/ca761137c80da1cee3f2657b215f758d/00000039.txt –

-1

ちょうどいいですが、この関数は常に同じ結果を返すとは限りません。

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

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

戻り値の一部は、グローバル状態に依存するオブジェクトのメモリアドレスです。関数の出力がグローバル状態に依存する場合は、グローバル状態に依存しません。ピュア。地獄、それにも依存する必要はありません。関数がグローバルな状態を読み取る限り、それは純粋ではありません。 "new"を呼び出すことによって、あなたはグローバルな状態を読み込んでいます。

+0

あなたのロジックは、純粋な関数は、プロセッサのレジスタに収まる値の型だけを返すことができなければならないことを意味します。 –

+0

以下はコンパイルし、呼び出しごとに異なるオブジェクトが返されることを示しています。 純粋なint * test_pure2() { int * p =新しいint; * p = 42; pを返す。 } int * i1 = test_pure2(); int * i2 = test_pure2(); writeln(i1、i2); –

+0

pure関数は、同じ値の異なるインスタンスを返します。ポインタが2つのインスタンスで同じになることはできません。私はそれがここで何が意味されているのかがはっきりしていると思います - あなたは結果のアドレスを使うべきではありません(そうでなければ上記の私のコメントを参照してください)。 –

0

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

クラスのソースが利用できない場合を考えます。この場合、コンパイラはaddMsgがメンバ変数を変更するだけなので、純関数から呼び出すことはできません。

あなたのケースでは許可するには、このタイプの使用に特別なケース処理が必要です。追加されるすべての特殊なケース規則は、言語はより複雑になります(または文書化されていない放置すれば、それはあまり移植可能)

4

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

純粋にする唯一の方法は、変更内容をカプセル化することです。これを行う最も簡単な方法はリターン突然変異によるもので、これを実装する方法は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); 
    } 
} 

お役に立てば幸いです。

+0

私はこれがおそらく最高のものだと思います。私はちょうど私達がすべてのゴミを生成するのを避けることができ、何とかコピーを作るのに費やす時間を望んでいました。 –

+2

これは面白いことです:ほとんどの関数型言語はまったく同じ理由で膨大な量のゴミを生成します。そのため、ガベージコレクタは効率的な傾向があります。 これは簡単に解決できるものではありません。変異が必要な場合は、純粋なものを使用しないでください。 –

+0

ところで、これはもはや真実ではありません。 – BCS

関連する問題