本日、 Microsoft.Bcl.Immutable NuGet パッケージの安定版のリリースを発表する投稿を見ました。
私は疑問に思っています:不変のスタックの使用法は何ですか? それが役立つ例やシナリオが見つかりません。不変性についての私の理解かもしれません。なぜこれが役立つのか、できれば具体的な例を挙げて説明していただければ幸いです。
本日、 Microsoft.Bcl.Immutable NuGet パッケージの安定版のリリースを発表する投稿を見ました。
私は疑問に思っています:不変のスタックの使用法は何ですか? それが役立つ例やシナリオが見つかりません。不変性についての私の理解かもしれません。なぜこれが役立つのか、できれば具体的な例を挙げて説明していただければ幸いです。
リクエストが 1 つのパイプラインで受信され、最後のリクエストが優先度として扱われるため、スタックに格納されるシナリオを想像してみてください。次に、スレッド化されたリクエスト コンシューマのセットがあり、それぞれが特定のタイプのリクエストを処理します。メインのマーシャリング コードがスタックを構築し、すべてのコンシューマの準備が整ったら、そのスタックをコンシューマに渡し、新しいスタックを開始します。
スタックが変更可能な場合、各コンシューマーは並行して実行され、他のコンシューマーのために任意の段階で変更される可能性がある同じスタックを共有します。アイテムがすべて同期されていることを確認するために、スタックからアイテムをポップすることを制御するには、ロックが必要です。コンシューマーがスタックがロックされているアクションを完了するのに長い時間がかかる場合、他のすべてのアクションは保留されます。
不変のスタックを持つことで、各コンシューマーは、他のコンシューマーを気にすることなく、スタックに対して自由に行うことができます。アイテムをスタックからポップすると、新しいアイテムが作成され、元のアイテムは変更されません。したがって、ロックは必要なく、各スレッドは他のスレッドを気にせずに自由に実行できます。
私はイミュータブル コレクションが大好きですが、問題が必要な場合の解決策のように感じることがよくあります。実装方法が原因で、予想外に遅くなる可能性があります。ループやインデクサーをアルゴリズム的により複雑にするという犠牲を払って、メモリを効率的に使用しようとします。メモリが豊富なため、そのコストを正当化するのは難しい場合があります。不変コレクション ( 以外) の使用の成功について、Web 上で見つけられる情報が不足しています。それを修正するために、私が本当に役立つとわかったケースについて、この 3 年以上前の質問に回答を追加しようと思いました。foreach
ImmutableArray<T>
ImmutableStack<T>
この非常に基本的なツリーのような構造を考えてみましょう:
public class TreeNode
{
public TreeNode Parent { get; private set; }
public List<TreeNode> ChildNodes { get; set; }
public TreeNode(TreeNode parent)
{
Parent = parent;
}
}
私はこの基本的なパターンに従うクラスを何度も作成してきましたが、実際には常にうまくいきました。最近では、次のようなことを始めました。
public class TreeNode
{
public ImmutableStack<TreeNode> NodeStack { get; private set; }
public ImmutableStack<TreeNode> ParentStack { get { return NodeStack.IsEmpty ? ImmutableStack<TreeNode>.Empty : NodeStack.Pop(); } }
public TreeNode Parent { get { return ParentStack.IsEmpty ? null : ParentStack.Peek(); } }
public ImmutableArray<TreeNode> ChildNodes { get; set; }
public TreeNode(TreeNode parent)
{
if (parent == null)
NodeStack = ImmutableStack<TreeNode>.Empty;
else
NodeStack = parent.NodeStack.Push(this);
}
}
さまざまな理由から、単にインスタンスへの参照ではなく、ルートまでたどることができるインスタンスの格納ImmutableStack<T>
を好むようになりました。TreeNode
Parent
ImmutableStack<T>
の実装は基本的に連結リストです。の各インスタンスは、ImmutableStack<T>
実際にはリンク リスト上のノードです。そのため、ParentStack
プロパティPop
はNodeStack
. ParentStack
と同じインスタンスを返しImmutableStack<T>
ますParent.NodeStack
。Parent
TreeNode
、ルート ノードの検索などの操作が終了しない原因となる循環ループが簡単に作成され、スタック オーバーフローまたは無限ループが発生します。ImmutableStack<T>
一方、 は -ing によって昇格でき、終了するPop
ことが保証されます。
ImmutableStack<T>
) を使用するということはparent
TreeNode
、TreeNode
.それはちょうど始めることです。ImmutableStack<T>
これにより、ツリー操作がより簡単かつ安全になると思います。その上で(安全に)LINQを使用できます。TreeNode
ある人が別の人の子孫かどうか知りたいですか? あなたは単にすることができますParentStack.Any(node => node == otherNode)
より一般的には、このImmutableStack<T>
クラスは、Stack<T>
絶対に変更されないことを保証できる が必要な場合、または の「スナップショット」を取得する簡単な方法が必要であるStack<T>
が、 のコピーの束を作成したくない場合に使用します。そのスタック。ネストされた一連のステップがある場合は、 を使用しImmutableStack<T>
て進行状況を追跡できます。したがって、ステップが完了すると、親ステップに作業を引き渡すことができます。その不変の性質は、作業を並行して行う場合に特に役立ちます。