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

后端 未结 27 2183
日久生厌
日久生厌 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:23

    Another variant - separating punctuation and iteration logic for the sake of code clarity. And still thinking about perfomrance.

    Works as requested with pure IEnumerable/string/ and strings in the list cannot be null.

    public static string Concat(IEnumerable<string> strings)
    {
        return "{" + strings.reduce("", (acc, prev, cur, next) => 
                   acc.Append(punctuation(prev, cur, next)).Append(cur)) + "}";
    }
    private static string punctuation(string prev, string cur, string next)
    {
        if (null == prev || null == cur)
            return "";
        if (null == next)
            return " and ";
        return ", ";
    }
    
    private static string reduce(this IEnumerable<string> strings, 
        string acc, Func<StringBuilder, string, string, string, StringBuilder> func)
    {
        if (null == strings) return "";
    
        var accumulatorBuilder = new StringBuilder(acc);
        string cur = null;
        string prev = null;
        foreach (var next in strings)
        {
            func(accumulatorBuilder, prev, cur, next);
            prev = cur;
            cur = next;
        }
        func(accumulatorBuilder, prev, cur, null);
    
        return accumulatorBuilder.ToString();
    }
    

    F# surely looks much better:

    let rec reduce list =
        match list with
        | []          -> ""
        | head::curr::[]  -> head + " and " + curr
        | head::curr::tail  -> head + ", " + curr :: tail |> reduce
        | head::[] -> head
    
    let concat list = "{" + (list |> reduce )  + "}"
    
    0 讨论(0)
  • 2020-12-01 07:23

    Just for fun, using the new Zip extension method from C# 4.0:

    private static string CommaQuibbling(IEnumerable<string> list)
    {
        IEnumerable<string> separators = GetSeparators(list.Count());
        var finalList = list.Zip(separators, (w, s) => w + s);
        return string.Concat("{", string.Join(string.Empty, finalList), "}");
    }
    
    private static IEnumerable<string> GetSeparators(int itemCount)
    {
        while (itemCount-- > 2)
            yield return ", ";
    
        if (itemCount == 1)
            yield return " and ";
    
        yield return string.Empty;
    }
    
    0 讨论(0)
  • 2020-12-01 07:24

    If I was doing a lot with streams which required first/last information, I'd have thid extension:

    [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 ;
            }
        }
    }
    

    Then the simplest (not the quickest, that would need a couple more handy extension methods) solution will be:

    public static string Quibble (IEnumerable<string> strings)
    {
        return "{" + String.Join ("", strings.MapWithPositions ((pos, item) => (
           (pos &  StreamPosition.First) != 0      ? "" : 
            pos == StreamPosition.Last   ? " and " : ", ") + item)) + "}" ;
    }
    
    0 讨论(0)
  • 2020-12-01 07:24

    Here's a simple F# solution, that only does one forward iteration:

    let CommaQuibble items =
        let sb = System.Text.StringBuilder("{")
        // pp is 2 previous, p is previous
        let pp,p = items |> Seq.fold (fun (pp:string option,p) s -> 
            if pp <> None then
                sb.Append(pp.Value).Append(", ") |> ignore
            (p, Some(s))) (None,None)
        if pp <> None then
            sb.Append(pp.Value).Append(" and ") |> ignore
        if p <> None then
            sb.Append(p.Value) |> ignore
        sb.Append("}").ToString()
    

    (EDIT: Turns out this is very similar to Skeet's.)

    The test code:

    let Test l =
        printfn "%s" (CommaQuibble l)
    
    Test []
    Test ["ABC"]        
    Test ["ABC";"DEF"]        
    Test ["ABC";"DEF";"G"]        
    Test ["ABC";"DEF";"G";"H"]        
    Test ["ABC";null;"G";"H"]        
    
    0 讨论(0)
  • 2020-12-01 07:25
    public static string CommaQuibbling(IEnumerable<string> items)
    {
      int count = items.Count();
      string answer = string.Empty;
      return "{" + 
          (count==0)  ?  ""  :  
             (  items[0] + 
                 (count == 1 ? "" :  
                     items.Range(1,count-1).
                         Aggregate(answer, (s,a)=> s += ", " + a) +
                     items.Range(count-1,1).
                         Aggregate(answer, (s,a)=> s += " AND " + a) ))+ "}";
    }
    

    It is implemented as,

    if count == 0 , then return empty,
    if count == 1 , then return only element,
    if count > 1 , then take two ranges, 
       first 2nd element to 2nd last element
       last element
    
    0 讨论(0)
  • 2020-12-01 07:25

    It hasn't quite been a decade since the last post so here's my variation:

        public static string CommaQuibbling(IEnumerable<string> items)
        {
            var text = new StringBuilder();
            string sep = null;
            int last_pos = items.Count();
            int next_pos = 1;
    
            foreach(string item in items)
            {
                text.Append($"{sep}{item}");
                sep = ++next_pos < last_pos ? ", " : " and ";
            }
    
            return $"{{{text}}}";
        }
    
    0 讨论(0)
提交回复
热议问题