Welcome to MSDN Blogs Sign in | Join | Help

Comma quibbling a la Eric and Jafar [A slightly wacky approach to the problem in C#]

Jafar was teasing me about his F# solution to Eric Lippert's comma quibbling problem, and I decided to retaliate. :) One of the things I didn't like about the five-or-so solutions I'd seen [including Jafar's :P ] was that they were doing a bunch of work to detect special cases on every pass through the loop. That seemed silly to me because there are really only two special cases and they only come into play at the very end of the operation.

So I threw together the following method which has a fairly efficient inner loop which saves the special cases for the end. I also made it generic so it'll take an input stream of any type - then defer to StringBuilder (or the object itself!) for proper formatting. Oh, and the code is both compact and commented. :)

The downside - and it's a big one - is that the input stream is enumerated three times instead of just one. :(

Oh well, when you're out for a quick bit of revenge you can't accomplish everything... :)

/// <summary>
/// Solve comma quibbling posed by Eric Lippert at:
/// http://blogs.msdn.com/ericlippert/archive/2009/04/15/comma-quibbling.aspx.
/// </summary>
/// <typeparam name="T">Type of input.</typeparam>
/// <param name="input">Stream of input elements (ex: string).</param>
/// <returns>Quibbled string.</returns>
/// <remarks>
/// Good points:
/// * Generic type support (StringBuilder formats for output)
/// * Special-case logic is not run every time through the loop
/// Bad points:
/// * Input stream is traversed three times :(
/// </remarks>
private static string CommaQuibbling<T>(IEnumerable<T> input)
{
    // Capture stream
    var a = input.GetEnumerator();
    var b = input.Skip(1).GetEnumerator();
    var c = input.Skip(2).GetEnumerator();
    // Prefix the result
    var sb = new StringBuilder("{");
    // Process the "normal" leading elements
    while (c.MoveNext() && b.MoveNext() && a.MoveNext())
    {
        sb.Append(a.Current).Append(", ");
    }
    // Process the non-Oxford comma scenario
    if (b.MoveNext() && a.MoveNext())
    {
        sb.Append(a.Current).Append(" and ");
    }
    // Process the remaining element
    if (a.MoveNext())
    {
        sb.Append(a.Current);
    }
    // Postfix the result and return it
    return sb.Append("}").ToString();
}
Published Thursday, April 16, 2009 4:29 PM by Delay
Filed under:

Comments

# re: Comma quibbling a la Eric and Jafar [A slightly wacky approach to the problem in C#]

Saturday, April 18, 2009 2:34 AM by XIU

Easy to fix, you always have to loop through it once anyways so...

input = input.ToArray();

in the beginning of the method.

# re: Comma quibbling a la Eric and Jafar [A slightly wacky approach to the problem in C#]

Sunday, April 19, 2009 2:15 AM by Delay

XIU,

You're right - that would be an improvement. However, Jafar and I were both coming at this problem from the direction of specifically NOT forcing the entire list to live in memory at once (as .ToArray will do). The reasons for avoiding this are pretty weak in this specific case because we're building up a string of each of those array elements anyway, but in general you can see significant memory benefits from NOT realizing an entire array and instead processing the elements as you need them. As I point out, my solution loses because it walks those elements three times - but if I'd managed to come up with a short, elegant way of creating those 3 Enumerators in such a way that the stream was traversed only once, I'd have solved both problems. And while I could write that code pretty easily, it added too much to what was supposed to be a simple solution, so I wimped out and didn't... :)

Thanks for the feedback!!

# re: Comma quibbling a la Eric and Jafar [A slightly wacky approach to the problem in C#]

Sunday, April 19, 2009 2:16 AM by Jimmy May

I appreciate your thoughtful optimization, David.  Such efficiencies translate to performant, scalable code.  Well done!

Anonymous comments are disabled
 
Page view tracker