Eric Lippert's challenge “comma-quibbling”, best answer?

后端 未结 27 2192
日久生厌
日久生厌 2020-12-01 06:59

I wanted to bring this challenge to the attention of the stackoverflow community. The original problem and answers are here. BTW, if you did not follow it before, you should

27条回答
  •  小蘑菇
    小蘑菇 (楼主)
    2020-12-01 07:28

    Disclaimer: I used this as an excuse to play around with new technologies, so my solutions don't really live up to the Eric's original demands for clarity and maintainability.

    Naive Enumerator Solution (I concede that the foreach variant of this is superior, as it doesn't require manually messing about with the enumerator.)

    public static string NaiveConcatenate(IEnumerable sequence)
    {
        StringBuilder sb = new StringBuilder();
        sb.Append('{');
    
        IEnumerator enumerator = sequence.GetEnumerator();
    
        if (enumerator.MoveNext())
        {
            string a = enumerator.Current;
            if (!enumerator.MoveNext())
            {
                sb.Append(a);
            }
            else
            {
                string b = enumerator.Current;
                while (enumerator.MoveNext())
                {
                    sb.Append(a);
                    sb.Append(", ");
                    a = b;
                    b = enumerator.Current;
                }
                sb.AppendFormat("{0} and {1}", a, b);
            }
        }
    
        sb.Append('}');
        return sb.ToString();
    }
    

    Solution using LINQ

    public static string ConcatenateWithLinq(IEnumerable sequence)
    {
        return (from item in sequence select item)
            .Aggregate(
            new {sb = new StringBuilder("{"), a = (string) null, b = (string) null},
            (s, x) =>
                {
                    if (s.a != null)
                    {
                        s.sb.Append(s.a);
                        s.sb.Append(", ");
                    }
                    return new {s.sb, a = s.b, b = x};
                },
            (s) =>
                {
                    if (s.b != null)
                        if (s.a != null)
                            s.sb.AppendFormat("{0} and {1}", s.a, s.b);
                        else
                            s.sb.Append(s.b);
                    s.sb.Append("}");
                    return s.sb.ToString();
                });
    }
    

    Solution with TPL

    This solution uses a producer-consumer queue to feed the input sequence to the processor, whilst keeping at least two elements buffered in the queue. Once the producer has reached the end of the input sequence, the last two elements can be processed with special treatment.

    In hindsight there is no reason to have the consumer operate asynchronously, which would eliminate the need for a concurrent queue, but as I said previously, I was just using this as an excuse to play around with new technologies :-)

    public static string ConcatenateWithTpl(IEnumerable sequence)
    {
        var queue = new ConcurrentQueue();
        bool stop = false;
    
        var consumer = Future.Create(
            () =>
                {
                    var sb = new StringBuilder("{");
                    while (!stop || queue.Count > 2)
                    {
                        string s;
                        if (queue.Count > 2 && queue.TryDequeue(out s))
                            sb.AppendFormat("{0}, ", s);
                    }
                    return sb;
                });
    
        // Producer
        foreach (var item in sequence)
            queue.Enqueue(item);
    
        stop = true;
        StringBuilder result = consumer.Value;
    
        string a;
        string b;
    
        if (queue.TryDequeue(out a))
            if (queue.TryDequeue(out b))
                result.AppendFormat("{0} and {1}", a, b);
            else
                result.Append(a);
    
        result.Append("}");
        return result.ToString();
    }
    

    Unit tests elided for brevity.

提交回复
热议问题