Eric Lippert挑战"逗号 - 狡辩",最佳答案?

MMi*_*ind 23 c# puzzle programming-languages

我想把这个挑战引起stackoverflow社区的注意.原始问题和答案在这里.顺便说一句,如果你之前没有按照它,你应该尝试阅读Eric的博客,这是纯粹的智慧.

摘要:

编写一个带有非null IEnumerable的函数,并返回一个具有以下特征的字符串:

  1. 如果序列为空,则生成的字符串为"{}".
  2. 如果序列是单个项"ABC",则结果字符串是"{ABC}".
  3. 如果序列是两个项目序列"ABC","DEF",则结果字符串是"{ABC和DEF}".
  4. 如果序列具有两个以上的项目,例如"ABC","DEF","G","H",则结果字符串为"{ABC,DEF,G和H}".(注意:没有牛津逗号!)

你甚至可以看到我们自己的Jon Skeet(是的,众所周知,他可以同时在两个地方)发布了一个解决方案,但他的(恕我直言)并不是最优雅的,尽管你可能无法击败它性能.

你怎么看?那里有很好的选择.我真的很喜欢其中一种涉及选择和聚合方法的解决方案(来自Fernando Nicolet).Linq非常强大,并且花了一些时间来应对这样的挑战让你学到很多东西.我扭曲了一下,所以它更高效一点(通过使用Count并避免反向):

public static string CommaQuibbling(IEnumerable<string> items)
{
    int last = items.Count() - 1;
    Func<int, string> getSeparator = (i) => i == 0 ? string.Empty : (i == last ? " and " : ", ");
    string answer = string.Empty;

    return "{" + items.Select((s, i) => new { Index = i, Value = s })
                      .Aggregate(answer, (s, a) => s + getSeparator(a.Index) + a.Value) + "}";
}
Run Code Online (Sandbox Code Playgroud)

dbk*_*bkk 33

效率不高,但我想清楚.

public static string CommaQuibbling(IEnumerable<string> items)
{
    List<String> list = new List<String>(items);
    if (list.Count == 0) { return "{}"; }
    if (list.Count == 1) { return "{" + list[0] + "}"; }

    String[] initial = list.GetRange(0, list.Count - 1).ToArray();
    return "{" + String.Join(", ", initial) + " and " + list[list.Count - 1] + "}";
}
Run Code Online (Sandbox Code Playgroud)

如果我维护代码,我更喜欢这个更聪明的版本.

  • 这比上面的所有答案都要好得多.我想没有多少SO用户阅读Eric的博客 - 他强调可读的解决方案.这就是为什么这个是最好的. (5认同)
  • 在.Net 4中,你可以在倒数第二行丢失`.ToArray()`,然后它变得更有效率. (3认同)

Mar*_*ell 28

这种方法怎么样?纯粹累积 - 没有回溯,只迭代一次.对于原始性能,我不确定你会用LINQ等做得更好,无论LINQ的答案是多么"漂亮".

using System;
using System.Collections.Generic;
using System.Text;

static class Program
{
    public static string CommaQuibbling(IEnumerable<string> items)
    {
        StringBuilder sb = new StringBuilder('{');
        using (var iter = items.GetEnumerator())
        {
            if (iter.MoveNext())
            { // first item can be appended directly
                sb.Append(iter.Current);
                if (iter.MoveNext())
                { // more than one; only add each
                  // term when we know there is another
                    string lastItem = iter.Current;
                    while (iter.MoveNext())
                    { // middle term; use ", "
                        sb.Append(", ").Append(lastItem);
                        lastItem = iter.Current;
                    }
                    // add the final term; since we are on at least the
                    // second term, always use " and "
                    sb.Append(" and ").Append(lastItem);
                }
            }
        }
        return sb.Append('}').ToString();
    }
    static void Main()
    {
        Console.WriteLine(CommaQuibbling(new string[] { }));
        Console.WriteLine(CommaQuibbling(new string[] { "ABC" }));
        Console.WriteLine(CommaQuibbling(new string[] { "ABC", "DEF" }));
        Console.WriteLine(CommaQuibbling(new string[] {
             "ABC", "DEF", "G", "H" }));
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @Akash - 告诉我它在哪里提到LINQ作为*要求*? (5认同)

Ant*_*hyy 5

如果我在使用需要第一个/最后一个信息的流做了很多事情,我会有扩展名:

[Flags]
public enum StreamPosition
{
   First = 1, Last = 2
}

public static IEnumerable<R> MapWithPositions<T, R> (this IEnumerable<T> stream, 
    Func<StreamPosition, T, R> map)
{
    using (var enumerator = stream.GetEnumerator ())
    {
        if (!enumerator.MoveNext ()) yield break ;

        var cur   = enumerator.Current   ;
        var flags = StreamPosition.First ;
        while (true)
        {
            if (!enumerator.MoveNext ()) flags |= StreamPosition.Last ;
            yield return map (flags, cur) ;
            if ((flags & StreamPosition.Last) != 0) yield break ;
            cur   = enumerator.Current ;
            flags = 0 ;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

那么最简单的(不是最快的,需要一些更方便的扩展方法)解决方案将是:

public static string Quibble (IEnumerable<string> strings)
{
    return "{" + String.Join ("", strings.MapWithPositions ((pos, item) => (
       (pos &  StreamPosition.First) != 0      ? "" : 
        pos == StreamPosition.Last   ? " and " : ", ") + item)) + "}" ;
}
Run Code Online (Sandbox Code Playgroud)