หนึ่งในความท้าทายการเขียนโค้ดที่น่าสนใจที่สุดที่ฉันได้รับจากการสัมภาษณ์คือการสร้างคิวการทำงาน ความต้องการคือการเรียกเข้าสู่คิวแต่ละครั้งจะสร้างคิวใหม่ที่มีการกำหนดคิวเก่าและรายการใหม่ที่ส่วนท้าย Dequeue จะส่งคืนคิวใหม่และรายการที่ dequeued เป็นพารามิเตอร์ออก
การสร้าง IEnumerator จากการนำไปใช้นี้จะไม่เป็นการทำลาย และให้ฉันบอกคุณว่าการใช้ Functional Queue นั้นทำงานได้ดีนั้นยากกว่าการใช้ Function Stack ที่มีประสิทธิภาพ (Stack Push / Pop ทั้งสองทำงานที่ Tail สำหรับ Queue Enqueue ทำงานที่ส่วนท้าย
ประเด็นของฉันคือ ... มันเป็นเรื่องไม่สำคัญที่จะสร้างตัวแบ่ง Stack แบบไม่ทำลายโดยใช้กลไก Pointer ของคุณเอง (StackNode <T>) และใช้ semantics เชิงหน้าที่ใน 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;
}
}
}
บางสิ่งที่ควรทราบ การเรียกเพื่อพุชหรือป๊อปก่อนคำสั่งปัจจุบัน = _head; การเติมเต็มจะให้สแต็กที่แตกต่างกันสำหรับการแจงนับกว่าที่ไม่มีมัลติเธรด (คุณอาจต้องการใช้ ReaderWriterLock เพื่อป้องกันสิ่งนี้) ฉันสร้างเขตข้อมูลใน StackNode แบบอ่านอย่างเดียว แต่แน่นอนถ้า T เป็นวัตถุที่ไม่แน่นอนคุณสามารถเปลี่ยนค่าได้ หากคุณต้องสร้างตัวสร้างสแต็กที่ใช้ StackNode เป็นพารามิเตอร์ (และตั้งค่าส่วนหัวให้เป็นผ่านในโหนด) สองกองที่สร้างด้วยวิธีนี้จะไม่ส่งผลกระทบซึ่งกันและกัน (ยกเว้น T ที่ไม่แน่นอนดังที่ฉันกล่าวไว้) คุณสามารถผลักดันและป๊อปทั้งหมดที่คุณต้องการในหนึ่งกองอื่น ๆ จะไม่เปลี่ยนแปลง
และเพื่อนของฉันเป็นวิธีที่คุณทำการแจงนับแบบไม่ทำลาย