<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Immutability in C# Part Eleven: A working double-ended queue</title><link>http://blogs.msdn.com/ericlippert/archive/2008/02/12/immutability-in-c-part-eleven-a-working-double-ended-queue.aspx</link><description>At long last, let's get to that double-ended queue. Sorry for leaving you in suspense! Last time we came up with a non-solution -- as commenters immediately discovered, the given algorithm and data structure are deeply inefficient. However, the basic</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP1 (Build: 61025.2)</generator><item><title>BioSensorAB &amp;raquo; Immutability in C# Part Eleven: A working double-ended queue</title><link>http://blogs.msdn.com/ericlippert/archive/2008/02/12/immutability-in-c-part-eleven-a-working-double-ended-queue.aspx#7657778</link><pubDate>Wed, 13 Feb 2008 03:24:32 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:7657778</guid><dc:creator>BioSensorAB » Immutability in C# Part Eleven: A working double-ended queue</dc:creator><description>&lt;p&gt;PingBack from &lt;a rel="nofollow" target="_new" href="http://www.biosensorab.org/2008/02/12/immutability-in-c-part-eleven-a-working-double-ended-queue/"&gt;http://www.biosensorab.org/2008/02/12/immutability-in-c-part-eleven-a-working-double-ended-queue/&lt;/a&gt;&lt;/p&gt;
</description></item><item><title>re: Immutability in C# Part Eleven: A working double-ended queue</title><link>http://blogs.msdn.com/ericlippert/archive/2008/02/12/immutability-in-c-part-eleven-a-working-double-ended-queue.aspx#7663567</link><pubDate>Wed, 13 Feb 2008 07:54:56 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:7663567</guid><dc:creator>Chris Okasaki</dc:creator><description>&lt;p&gt;Thanks for the kind words about my book. &amp;nbsp;I actually just wrote a 10 year retrospective for it on my blog (linked above).&lt;/p&gt;
&lt;p&gt;As I'm sure you realize, there's a minor problem with this version of deques, which is that the amortized bounds can break down when you take advantage of the immutability to go back and work with old instances. &amp;nbsp;For example, suppose you've just built up a big deque where inserting one more item is going to a cause an O(log n) cascade of inserts into deeper and deeper levels. &amp;nbsp;Call that deque D. &amp;nbsp;Now, if you keep inserting into D, throwing away the new deque, and going back to insert into D again, you'll hit the O(log n) case every time. &amp;nbsp;Of course, this pathological case is unlikely in practice.&lt;/p&gt;
&lt;p&gt;There are ways around this. &amp;nbsp;For example, when inserting into a deque that has a Four on the right, you could begin by modifying the existing deque, peeling off the Three, adding it to the middle, and leaving a One on the right. &amp;nbsp;Then continue the insert by making a new deque with a Two on the right. &amp;nbsp;Modifying the existing deque goes against the idea of immutability, but can be ok since you're not changing the elements of the existing deque, merely reorganizing them. &amp;nbsp;But of course, this kind of in-place update can interfere with some of the reasons for wanting the immutable deque in the first place, such as thread safety.&lt;/p&gt;
&lt;p&gt;Even that can be worked around by fiddling with the algorithm even more.&lt;/p&gt;
</description></item><item><title>re: Immutability in C# Part Eleven: A working double-ended queue</title><link>http://blogs.msdn.com/ericlippert/archive/2008/02/12/immutability-in-c-part-eleven-a-working-double-ended-queue.aspx#7666218</link><pubDate>Wed, 13 Feb 2008 10:19:51 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:7666218</guid><dc:creator>Eric Lippert</dc:creator><description>&lt;p&gt;Hi, how good to hear from you.&lt;/p&gt;
&lt;p&gt;Indeed, throughout this series I've been concentrating more on just getting across the &amp;quot;flavour&amp;quot; of working with immutable data structures, as it is a coding style which most C# programmers are unfamiliar with, but which we on the compiler team increasingly use ourselves. &lt;/p&gt;
&lt;p&gt;As my former professor P L Ragde pointed out in a comment a few episodes back, there are all kinds of ways that the simple &amp;quot;two stacks&amp;quot; queue can end up having large costs when you end up with that one &amp;quot;dangerous&amp;quot; structure that is going to be expensive to use, and then you use it over and over again in that state. This deque certainly has the same problem, as you point out.&lt;/p&gt;
&lt;p&gt;The depth of analysis that you go into in your book on how to measure these costs, track &amp;quot;credits&amp;quot;, eliminate dangerous situations, and so on, is fascinating but far beyond the level at which I wanted to pitch this series of articles, so I deliberately held back from mentioning it directly. &lt;/p&gt;
&lt;p&gt;I shall certainly check out your blog. Thanks again for the note.&lt;/p&gt;
&lt;p&gt;Cheers,&lt;/p&gt;
&lt;p&gt;Eric&lt;/p&gt;
</description></item><item><title>re: Immutability in C# Part Eleven: A working double-ended queue</title><link>http://blogs.msdn.com/ericlippert/archive/2008/02/12/immutability-in-c-part-eleven-a-working-double-ended-queue.aspx#7677770</link><pubDate>Wed, 13 Feb 2008 20:07:04 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:7677770</guid><dc:creator>Erik</dc:creator><description>&lt;p&gt;Excellent series! I like reading about these sorts of structures particularly because I never really thought about it before. That said, at the moment they do seem more like a thought exercise than something that can be used in everyday code. I would like to see some practical uses for these sorts of structures (and this style of coding in general).&lt;/p&gt;
</description></item><item><title>re: Immutability in C# Part Eleven: A working double-ended queue</title><link>http://blogs.msdn.com/ericlippert/archive/2008/02/12/immutability-in-c-part-eleven-a-working-double-ended-queue.aspx#7679881</link><pubDate>Wed, 13 Feb 2008 22:56:53 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:7679881</guid><dc:creator>DRBlaise</dc:creator><description>&lt;p&gt;Very nice, great series.&lt;/p&gt;
&lt;p&gt;I understand Chris Okasaki's comment about being in a dangerous state, but with the Deque structure, the danger is never greater than O(log n). &amp;nbsp;The danger in the Double Stack queue implementation could grow to O(n). &lt;/p&gt;
&lt;p&gt;I can definitely see benefit in the simple logic of the immutible data structures and the inherent thread safety they provide. &amp;nbsp;Thanks for the mind stimulation!&lt;/p&gt;
</description></item><item><title>Immutability in C#</title><link>http://blogs.msdn.com/ericlippert/archive/2008/02/12/immutability-in-c-part-eleven-a-working-double-ended-queue.aspx#7680347</link><pubDate>Wed, 13 Feb 2008 23:41:20 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:7680347</guid><dc:creator>&lt;a href="http://weblogs.asp.net/bleroy"&gt;Tales from the Evil Empire&lt;/a&gt;</dc:creator><description>&lt;p&gt;For some reason, there's been a lot of buzz lately around immutability in C#. If you're interested in&lt;/p&gt;
</description></item><item><title>re: Immutability in C# Part Eleven: A working double-ended queue</title><link>http://blogs.msdn.com/ericlippert/archive/2008/02/12/immutability-in-c-part-eleven-a-working-double-ended-queue.aspx#7689823</link><pubDate>Thu, 14 Feb 2008 13:57:26 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:7689823</guid><dc:creator>Franck Jeannin</dc:creator><description>&lt;p&gt;Hi Eric,&lt;/p&gt;
&lt;p&gt;Fantastic work that would really make a great book…&lt;/p&gt;
&lt;p&gt;I’m sure you have plenty of ideas about what to write about next but I’m going to risk a suggestion: trading CPU for memory and vice versa.&lt;/p&gt;
&lt;p&gt;Ok, this is not a new concept, this is what caches are all about but with the rise of multi-core on one side and 64 bits addressing (more memory) on the other side, I think it’s about time with find a better way to deal with it.&lt;/p&gt;
&lt;p&gt;Maybe this is a CLR issue more than a language issue but .NET offers very little support for managing memory pressure. You can have a strong reference to some object or a weak one (which is unlikely to last very long) and nothing in between.&lt;/p&gt;
&lt;p&gt;So, you end up with selfish applications that take memory and never give it back even if another application could make better use of it. Virtual memory and swapping on disk with a 100 times penalty compared to Ram is clearly not a good solution…&lt;/p&gt;
&lt;p&gt;So, what if through a language construct or a .NET feature we could say that this object (block of memory) is worth that much to me (in CPU units multiplied by number of accesses maybe). The garbage collector may reclaim that object is someone else would be prepared to pay a higher price for it (to store the result of a more expensive calculation). But in any case, the object would know how to recreate itself (re-running the code associated with it) if the object was needed again after the memory was reclaimed.&lt;/p&gt;
&lt;p&gt;I hope you find this topic interesting or point me in the direction of another blogger that does…&lt;/p&gt;
&lt;p&gt;Thanks,&lt;/p&gt;
&lt;p&gt;Franck&lt;/p&gt;
</description></item><item><title>re: Immutability in C# Part Eleven: A working double-ended queue</title><link>http://blogs.msdn.com/ericlippert/archive/2008/02/12/immutability-in-c-part-eleven-a-working-double-ended-queue.aspx#7696852</link><pubDate>Thu, 14 Feb 2008 20:35:34 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:7696852</guid><dc:creator>Jay Bazuzi</dc:creator><description>&lt;p&gt;Cool.&lt;/p&gt;
&lt;p&gt;So, why Four, and not Three or Five?&lt;/p&gt;
</description></item><item><title>re: Immutability in C# Part Eleven: A working double-ended queue</title><link>http://blogs.msdn.com/ericlippert/archive/2008/02/12/immutability-in-c-part-eleven-a-working-double-ended-queue.aspx#7697510</link><pubDate>Thu, 14 Feb 2008 21:16:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:7697510</guid><dc:creator>Eric Lippert</dc:creator><description>&lt;p&gt;Good question Jay. &amp;nbsp;This question leads to a great many more questions. I will try to break them down and answer them one at a time.&lt;/p&gt;
&lt;p&gt;The short answer is &amp;quot;because the concatenation algorithm which I did not show requires this&amp;quot;.&lt;/p&gt;
&lt;p&gt;The long answer is:&lt;/p&gt;
&lt;p&gt;* Why not bound the endpoint dequelettes at Three?&lt;/p&gt;
&lt;p&gt;If we bound them at Three then the inner deque must contain only Twos. We don't want that.&lt;/p&gt;
&lt;p&gt;* Why does bounding the end points at Three imply that the inner must contain only Twos?&lt;/p&gt;
&lt;p&gt;Suppose the inner deque contains Threes and the endpoints are bounded at Three. When the endpoint has a Three and you enqueue, that Three has to be moved to the inner deque; the endpoint is replaced with a One. That's an &amp;quot;expensive&amp;quot; operation, potentially O(lg n) in the number of total elements stored in the deque (if the inner deque is also in a &amp;quot;dangerous&amp;quot; condition, we could end up pushing items into inner deques all the way to the bottom.)&lt;/p&gt;
&lt;p&gt;Now suppose you immediately dequeue it. The One at the end goes away and is replaced with a Three from the inner store. &amp;nbsp;Which is _also_ potentially an O(lg n) operation, to undo all that work we just did. And now we're back in the bad state for enqueueing again. &lt;/p&gt;
&lt;p&gt;Essentially what we've got here is a deque which is in a state where if you alternate enqueue-dequeue-enqueue-dequeue-enqueue-dequeue, each one might cost O(lg n). &amp;nbsp;My earlier analysis that &amp;quot;at least every other operation is always cheap&amp;quot; just went out the window -- here we have a situation where potentially every operation is maximally expensive. &lt;/p&gt;
&lt;p&gt;Now suppose that instead, when we enqueue on a &amp;quot;dangerous&amp;quot; deque, we stick a Two in the inner store and replace the endpoint Three with a Two. &amp;nbsp;If you do that then every expensive operation is always followed by at least one cheap operation, so you get back the amortized good performance even for pathological cases. &amp;nbsp;(Leaving aside Chris Okasaki's comments above about reusing a dangerous deque in the same state over and over again. &amp;nbsp;Fixing that requires considerably more advanced analysis.)&lt;/p&gt;
&lt;p&gt;* OK, so why don't we want to stick only Twos in there?&lt;/p&gt;
&lt;p&gt;Because of the feature that I mentioned but did not implement. &lt;/p&gt;
&lt;p&gt;Consider how you would concatenate two deques each with many items in them. &amp;nbsp;You've got (Left1 Middle1 Right1) concatenated with (Left2 Middle2 Right2). &amp;nbsp;Clearly the concatenated deque has the form (Left1 Something Right2), but how to compute the Something? &lt;/p&gt;
&lt;p&gt;Well, its a deque itself, so use recursion. Somehow stuff the contents of Right1 and Left2 into Middle1, and then recursively concatenate the &amp;quot;new&amp;quot; Middle1 with Middle2.&lt;/p&gt;
&lt;p&gt;What exactly do we stuff into Middle1? &amp;nbsp;If the endpoints are bounded at Three then Right1 and Left2 have between two and six items amongst them. Suppose they have three or five items. That cannot be broken up into any number of Twos, and therefore we will have to stuff either a Three or a One into Middle1, which then brings back the potential for pathological cases we were in before. &amp;nbsp;&lt;/p&gt;
&lt;p&gt;However, if we bound endpoints at Four then the middle bit can safely contain Twos or Threes with no pathological cases. We have a Empty and Single deques specifically built for the case where the middle bit contains zero or one element. Every other possible number of elements that can be in the middle can be represented as a deque of some combination of Twos and Threes. (Because 2 = 2, 3 = 3, 4 = 2 + 2, 5 = 2 + 3, 6 = 2 + 2 + 2, 7 = 2 + 2 + 3, ...)&lt;/p&gt;
&lt;p&gt;* That explains why you want at least Four. Why not Five?&lt;/p&gt;
&lt;p&gt;With Fours, the concatenation algorithm has to be able to handle sixteen cases for the sizes of Right1 and Left2. &amp;nbsp;With Fives, the concatenation would have to handle 25 cases. Why add nine more complicated cases when the algorithm doesn't need them?&lt;/p&gt;
</description></item><item><title>Community Convergence XLI</title><link>http://blogs.msdn.com/ericlippert/archive/2008/02/12/immutability-in-c-part-eleven-a-working-double-ended-queue.aspx#8160907</link><pubDate>Wed, 12 Mar 2008 00:35:43 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8160907</guid><dc:creator>Charlie Calvert's Community Blog</dc:creator><description>&lt;p&gt;Welcome to the forty-first Community Convergence. The big news this week is that we have moved Future&lt;/p&gt;
</description></item><item><title>re: Immutability in C# Part Eleven: A working double-ended queue</title><link>http://blogs.msdn.com/ericlippert/archive/2008/02/12/immutability-in-c-part-eleven-a-working-double-ended-queue.aspx#8373323</link><pubDate>Thu, 10 Apr 2008 00:34:09 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8373323</guid><dc:creator>Robin D.</dc:creator><description>&lt;p&gt;ummm. If you occur o(log n) operation speed on every other operation, that's still an o(log n) operation! This algorithm isn't -- strictly speaking -- an improvement.&lt;/p&gt;
</description></item><item><title>re: Immutability in C# Part Eleven: A working double-ended queue</title><link>http://blogs.msdn.com/ericlippert/archive/2008/02/12/immutability-in-c-part-eleven-a-working-double-ended-queue.aspx#8373356</link><pubDate>Thu, 10 Apr 2008 00:48:35 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8373356</guid><dc:creator>Eric Lippert</dc:creator><description>&lt;p&gt;Robin, you are not working through the analysis.&lt;/p&gt;
&lt;p&gt;Suppose there are two operations. The cost of the first is 1. The cost of the second is 1 + 1. &amp;nbsp;Total cost, 3.&lt;/p&gt;
&lt;p&gt;Suppose there are four operations. The costs are (1) + (1 + 1) + (1) + (1 + 1 + 1). &amp;nbsp;Total cost, 7.&lt;/p&gt;
&lt;p&gt;Suppose there are 8. &amp;nbsp;(1) + (1 + 1) + (1) + (1 + 1 + 1) + (1) + (1 + 1) + (1) + (1 + 1 + 1 + 1). &amp;nbsp;Total cost, 15.&lt;/p&gt;
&lt;p&gt;Suppose there are 16... total cost, 31.&lt;/p&gt;
&lt;p&gt;When the cost is log(n), and only every other calculation is expensive, then the total cost is O(2n), so the amortized cost is O(2n/n) = O(1).&lt;/p&gt;
</description></item></channel></rss>