Mã tiêu thụ chuỗi là tốt. Khi người tiêu dùng chỉ ra, mã tạo ra kiểu liệt kê có thể có vấn đề về hiệu năng nếu cây bị sâu.
Giả sử tại điểm sâu nhất cây của bạn là bốn sâu; suy nghĩ về những gì xảy ra trên các nút có bốn sâu. Để có được nút đó, bạn lặp lại gốc, gọi một trình lặp, gọi một trình lặp, gọi là trình lặp, chuyển nút trở lại mã mà chuyển nút trở lại mã mà chuyển nút trở lại ... Thay vì chỉ bàn giao nút cho người gọi, bạn đã thực hiện một nhóm lữ đoàn nhỏ với bốn người trong đó, và họ đang tramping dữ liệu xung quanh từ đối tượng đến đối tượng trước khi nó cuối cùng được vào vòng lặp mà muốn nó.
Nếu cây chỉ có bốn sâu, không có vấn đề gì lớn. Nhưng giả sử cây là mười nghìn phần tử, và có một nghìn nút tạo thành một danh sách liên kết ở đầu và chín nghìn nốt còn lại ở phía dưới. Bây giờ khi bạn lặp lại chín nghìn nút đó, mỗi nút sẽ phải trải qua một nghìn lần lặp, với tổng cộng chín triệu bản sao để tìm nạp chín nghìn nút. (Tất nhiên, bạn có thể đã nhận được lỗi tràn ngăn xếp và cũng đã làm hỏng quy trình.)
Cách để giải quyết vấn đề này nếu bạn có nó là tự quản lý ngăn xếp thay vì đẩy trình lặp mới trên ngăn xếp .
public IEnumerable<Effect> EffectsNotRecursive()
{
var stack = new Stack<Effect>();
stack.Push(this);
while(stack.Count != 0)
{
var current = stack.Pop();
yield return current;
foreach(var child in current.Effects)
stack.Push(child);
}
}
Việc triển khai ban đầu có độ phức tạp về thời gian trong đó n là số nút và d là độ sâu trung bình của cây; vì d có thể trong trường hợp xấu nhất là O (n), và trong trường hợp tốt nhất là O (lg n), điều đó có nghĩa là thuật toán nằm giữa O (n lg n) và O (n^2) trong thời gian. Nó là O (d) trong không gian heap (cho tất cả các vòng lặp) và O (d) trong không gian ngăn xếp (cho tất cả các cuộc gọi đệ quy.)
Việc thực hiện mới có độ phức tạp thời gian của O (n), và là O (d) trong không gian heap, và O (1) trong không gian ngăn xếp.
Một khía cạnh nhỏ hơn là thứ tự khác nhau; cây được di chuyển từ trên xuống dưới và phải sang trái trong thuật toán mới, thay vì từ trên xuống dưới và trái sang phải. Nếu điều đó làm phiền bạn thì bạn chỉ có thể nói
foreach(var child in current.Effects.Reverse())
thay thế.
Đối với phân tích hơn về vấn đề này, xem bài viết đồng nghiệp của tôi Wes Dyer về đề tài này:
http://blogs.msdn.com/b/wesdyer/archive/2007/03/23/all-about-iterators.aspx
Btw tôi thêm việc thực hiện EffectsRecursive. –
Hãy coi chừng sản lượng đệ quy. Có rất nhiều ma thuật biên dịch đang diễn ra ở đây tạo ra các máy trạng thái. Nếu cấu trúc dữ liệu lớn, điều này có thể kém hiệu quả/phức tạp hơn bạn mong đợi. – spender
@spender: Cảm ơn, có cách nào khác để tôi có thể sử dụng không? –