Comma quibbling

From Rosetta Code
Revision as of 06:09, 7 October 2013 by rosettacode>Paddy3118 (→‎{{header|Python}}: Second example without the need to reverse a string.)
Task
Comma quibbling
You are encouraged to solve this task according to the task description, using any language you may know.

Comma quibbling is a task originally set by Eric Lippert in his blog.

The task is to write a function to generate a string output which is the concatenation of input words from a list/sequence where:

  1. An input of no words produces the output string of just the two brace characters "{}".
  2. An input of just one word, e.g. ["ABC"], produces the output string of the word inside the two braces, e.g. "{ABC}".
  3. An input of two words, e.g. ["ABC", "DEF"], produces the output string of the two words inside the two braces with the words separated by the string " and ", e.g. "{ABC and DEF}".
  4. An input of three or more words, e.g. ["ABC", "DEF", "G", "H"], produces the output string of all but the last word separated by ", " with the last word separated by " and " and all within braces; e.g. "{ABC, DEF, G and H}".

Test your function with the following series of inputs showing your output here on this page:

  • [] # (No input words).
  • ["ABC"]
  • ["ABC", "DEF"]
  • ["ABC", "DEF", "G", "H"]

Note: Assume words are non-empty strings of uppercase characters for this task.

AWK

<lang awk>function quibble(a, n, i, s) { for (i = 1; i < n - 1; i++) s = s a[i] ", " i = n - 1; if (i > 0) s = s a[i] " and " if (n > 0) s = s a[n] return "{" s "}" }

BEGIN { print quibble(a, 0) n = split("ABC", b); print quibble(b, n) n = split("ABC DEF", c); print quibble(c, n) n = split("ABC DEF G H", d); print quibble(d, n) }</lang>

Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

C#

<lang csharp>public static string Quibble(string[] input) {

   var len = input.Length;
   return "{" +
       String.Join("", input.Take(len - 2).Select(n => n + ", ")
       .Concat(input.Skip(len < 2 ? len : len - 2).Take(1).Select(n => n + " and ")))
               + (input.LastOrDefault() ?? "") + "}";

}

static void Main(string[] args) {

   Console.WriteLine(Quibble(new string[] { }));
   Console.WriteLine(Quibble(new[] { "A" }));
   Console.WriteLine(Quibble(new[] { "A", "B" }));
   Console.WriteLine(Quibble(new[] { "A", "B", "C" }));
   Console.WriteLine(Quibble(new[] { "A", "B", "C", "D" }));
   Console.WriteLine(Quibble(new[] { "A", "B", "C", "D", "E" }));

}</lang>

Output:
{}
{A}
{A and B}
{A, B and C}
{A, B, C and D}
{A, B, C, D and E}

C++

<lang cpp>#include <iostream>

template<class T> void quibble(std::ostream& o, T i, T e) {

 o << "{";
 if (e != i) {
   T n = i++;
   const char* more = "";
   while (e != i) {
     o << more << *n;
     more = ", ";
     n = i++;
   }
   o << (*more?" and ":"") << *n;
 }
 o << "}";

}

int main(int argc, char** argv) {

 char const* a[] = {"ABC","DEF","G","H"};
 for (int i=0; i<5; i++) {
   quibble(std::cout, a, a+i);
   std::cout << std::endl;
 }
 return 0;

}</lang>

Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF and G}
{ABC, DEF, G and H}

Clojure

This example is incorrect. Please fix the code and remove this message.

Details: Wrong output.

<lang clojure> (defn quibbling [sequence]

 (let [sep (if (> (count sequence) 1) " and " "")]
   (apply str (concat
                (interpose ", " (butlast sequence))
                [sep (last sequence)]))))

(map quibbling [[]

               ["ABC"]
               ["ABC", "DEF"]
               ["ABC", "DEF", "G", "H"]])

</lang>

Output:
("" "ABC" "ABC and DEF" "ABC, DEF, G and H")

D

<lang d>import std.stdio, std.string;

string quibbler(in string[] seq) pure /*nothrow*/ {

   if (seq.length <= 1)
       return format("{%-(%s, %)}", seq);
   else
       return format("{%-(%s, %) and %s}", seq[0 .. $-1], seq[$-1]);

}

void main() {

   //foreach (immutable test; [[],
   foreach (const test; [[],
                         ["ABC"],
                         ["ABC", "DEF"],
                         ["ABC", "DEF", "G", "H"]])
       test.quibbler.writeln;

}</lang>

Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

F#

This example does not show the output mentioned in the task description on this page (or a page linked to from here). Please ensure that it meets all task requirements and remove this message.
Note that phrases in task descriptions such as "print and display" and "print and show" for example, indicate that (reasonable length) output be a part of a language's solution.


<lang fsharp>let quibble list =

   let rec inner = function
       | [] -> ""
       | [x] -> x
       | [x;y] -> sprintf "%s and %s" x y
       | h::t -> sprintf "%s, %s" h (inner t)
   sprintf "{%s}" (inner list)

// test interactively quibble [] quibble ["ABC"] quibble ["ABC"; "DEF"] quibble ["ABC"; "DEF"; "G"] quibble ["ABC"; "DEF"; "G"; "H"]</lang>

Perl

Translation of: Perl 6

<lang perl>sub comma_quibbling(@) {

   return "{$_}" for
       @_ < 2 ? "@_" :
       join(', ', @_[0..@_-2]) . ' and ' . $_[-1];

}

print comma_quibbling(@$_), "\n" for

   [], [qw(ABC)], [qw(ABC DEF)], [qw(ABC DEF G H)];</lang>
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

Perl 6

<lang perl6>sub comma-quibbling(@A) {

   <{ }>.join: @A < 2 ?? @A !! "@A[0..*-2].join(', ') and @A[*-1]";

}

say comma-quibbling($_) for

   [], [<ABC>], [<ABC DEF>], [<ABC DEF G H>];</lang>
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

Python

replace() whilst reversed

replace(..) can only replace the first X occurrences not the last hence the replace is done on the reverse of the intermediate string then reversed back. <lang python>>>> def strcat(sequence):

   return '{%s}' % ', '.join(sequence)[::-1].replace(',', 'dna ', 1)[::-1]

>>> for seq in ([], ["ABC"], ["ABC", "DEF"], ["ABC", "DEF", "G", "H"]):

   print('Input: %-24r -> Output: %r' % (seq, strcat(seq)))


Input: [] -> Output: '{}' Input: ['ABC'] -> Output: '{ABC}' Input: ['ABC', 'DEF'] -> Output: '{ABC and DEF}' Input: ['ABC', 'DEF', 'G', 'H'] -> Output: '{ABC, DEF, G and H}' >>> </lang>

Counted replacement

replace() will replace nothing if the count of items to replace is zero, (and negative integer counts act to replace all occurrences). This combines with the length of the input sequence to allow this to work: <lang python>def commaQuibble(s):

   return '{%s}' % ' and '.join(s).replace(' and ', ', ', len(s) - 2)

for seq in ([], ["ABC"], ["ABC", "DEF"], ["ABC", "DEF", "G", "H"]): print('Input: %-24r -> Output: %r' % (seq, commaQuibble(seq)))</lang>

Output:
Input: []                       -> Output: '{}'
Input: ['ABC']                  -> Output: '{ABC}'
Input: ['ABC', 'DEF']           -> Output: '{ABC and DEF}'
Input: ['ABC', 'DEF', 'G', 'H'] -> Output: '{ABC, DEF, G and H}'

PL/I

<lang pli>*process or(!);

quib: Proc Options(main);
/*********************************************************************
* 06.10.2013 Walter Pachl
*********************************************************************/
  put Edit(quibbling())(Skip,a);
  put Edit(quibbling('ABC'))(Skip,a);
  put Edit(quibbling('ABC DEF'))(Skip,a);
  put Edit(quibbling('ABC DEF G H'))(Skip,a);
  return;
quibbling: proc(s) Returns(Char(100) Var);
  Dcl s Char(*);
  Dcl result Char(100) Var;
  Dcl (wi,p) Bin Fixed(31);
  If s= Then result=;
  Else Do;
    Do wi=1 By 1 While(s^=);
      p=index(s,' ');
      If p=0 Then Do;
        If wi>1 Then
          result=result!!' and '!!s;
        else
          result=s;
        s=;
        End;
      Else Do;
        If wi=1 Then
          result=left(s,p-1);
        Else
          result=result!!', '!!left(s,p-1);
        s=substr(s,p+1);
        End;
      End;
    End;
  Return('{'!!result!!'}');
  End;
End;</lang>
Output:
{}
{ABC}
{ABC, DEF}
{ABC, DEF, G, H}   

REXX

<lang rexx>say quibbling() say quibbling('ABC') say quibbling('ABC DEF') say quibbling('ABC DEF G H') exit

quibbling: procedure

   parse arg list
   Select
     When list= Then result=
     When words(list)=1 then result=word(list,1)
     Otherwise result=translate(strip(subword(list,1,words(list)-1)),',',' '),
       'and' word(list,words(list))
     End
   Return '{'result'}'</lang>
Output:
{}
{ABC}
{ABC and DEF}
{ABC,DEF,G and H}

Rust

<lang Rust>fn quibble(seq: &[~str]) -> ~str {

   match seq {
       [] => ~"{}",
       [ref w] => fmt!("{%s}", *w),
       [..ws, ref w] => fmt!("{%s and %s}", ws.connect(", "), *w),
   }

}

fn main() {

   println(quibble([]));
   println(quibble([~"ABC"]));
   println(quibble([~"ABC", ~"DEF"]));
   println(quibble([~"ABC", ~"DEF", ~"G", ~"H"]));

}</lang>

Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

Scala

<lang scala>def quibble( s:List[String] ) = s match {

 case m if m.isEmpty => "{}"
 case m if m.length < 3 => m.mkString("{", " and ", "}")
 case m => "{" + m.init.mkString(", ") + " and " + m.last + "}"

}

// A little test... {

 println( quibble( List() ) )
 println( quibble( List("ABC") ) )
 println( quibble( List("ABC","DEF") ) )
 println( quibble( List("ABC","DEF","G","H") ) )

}</lang>

Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

Tcl

<lang tcl>proc commaQuibble {lst} {

   return \{[join [lreplace $lst end-1 end [join [lrange $lst end-1 end] " and "]] ", "]\}

}

foreach input { {} {"ABC"} {"ABC" "DEF"} {"ABC" "DEF" "G" "H"} } {

   puts [commaQuibble $input]

}</lang>

Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

UNIX Shell

Translation of: AWK

<lang bash>quibble() { # Here awk(1) is easier than sed(1). awk 'BEGIN { for (i = 1; i < ARGC - 2; i++) s = s ARGV[i] ", " i = ARGC - 2; if (i > 0) s = s ARGV[i] " and " i = ARGC - 1; if (i > 0) s = s ARGV[i] printf "{%s}\n", s exit 0 }' "$@" }

quibble quibble ABC quibble ABC DEF quibble ABC DEF G H</lang>

Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}