インタビューのために私に与えられた最も興味深いコーディングの課題の1つは、機能的なキューを作成することでした。要件は、エンキューを呼び出すたびに、古いキューと末尾の新しいアイテムを含む新しいキューが作成されることでした。また、デキューは、新しいキューとデキューされたアイテムを出力パラメータとして返します。
この実装からIEnumeratorを作成することは非破壊的です。また、パフォーマンスの高い機能スタックを実装するよりも、パフォーマンスの高い機能キューを実装する方がはるかに難しいことを教えてください(スタックプッシュ/ポップの両方がテールで機能し、キューエンキューがテールで機能し、デキューがヘッドで機能します)。
私のポイントは...独自のPointerメカニズム(StackNode <T>)を実装し、Enumeratorで機能セマンティクスを使用して非破壊的なStack Enumeratorを作成するのは簡単です。
public class Stack<T> implements IEnumerator<T>
{
private class StackNode<T>
{
private readonly T _data;
private readonly StackNode<T> _next;
public StackNode(T data, StackNode<T> next)
{
_data=data;
_next=next;
}
public <T> Data{get {return _data;}}
public StackNode<T> Next{get {return _Next;}}
}
private StackNode<T> _head;
public void Push(T item)
{
_head =new StackNode<T>(item,_head);
}
public T Pop()
{
//Add in handling for a null head (i.e. fresh stack)
var temp=_head.Data;
_head=_head.Next;
return temp;
}
///Here's the fun part
public IEnumerator<T> GetEnumerator()
{
//make a copy.
var current=_head;
while(current!=null)
{
yield return current.Data;
current=_head.Next;
}
}
}
注意すべき点がいくつかあります。ステートメントcurrent = _headの前にプッシュまたはポップする呼び出し。completesは、マルチスレッドがない場合とは異なる列挙型のスタックを提供します(ReaderWriterLockを使用してこれを防ぐことができます)。StackNodeのフィールドを読み取り専用にしましたが、もちろんTが可変オブジェクトである場合、その値を変更できます。StackNodeをパラメーターとして使用するStackコンストラクターを作成する場合(および、ノードに渡されたものにheadを設定する場合)。この方法で構築された2つのスタックは、互いに影響しません(前述の可変Tを除く)。1つのスタックで必要なすべてをプッシュおよびポップできますが、他のスタックは変更されません。
そして、私の友人は、スタックを非破壊的に列挙する方法です。