Comma quibbling: Difference between revisions

From Rosetta Code
Content added Content deleted
(Added Algol 68 and Algol W)
Line 46: Line 46:
{abc, def, g and h}</pre>
{abc, def, g and h}</pre>


=={{header|ALGOL 68}}==
{{works with|ALGOL 68G|Any - tested with release 2.8.win32}}
<lang algol68># returns a string ( assumed to be of space-separated words ) with the words #
# separated by ", ", except for the last which is separated from the rest by #
# " AND ". The list is enclosed by braces #
PROC to list = ( STRING words ) STRING:
BEGIN
# count the number of words #
INT word count := 0;
BOOL in word := FALSE;
FOR char pos FROM LWB words TO UPB words
DO
IF NOT is upper( words[ char pos ] )
THEN
# not an upper-case letter, possibly a word has been ended #
in word := FALSE
ELSE
# not a delimitor, possibly the start of a word #
IF NOT in word
THEN
# we are starting a new word #
word count +:= 1;
in word := TRUE
FI
FI
OD;

# format the result #
STRING result := "{";
in word := FALSE;
INT word number := 0;
FOR char pos FROM LWB words TO UPB words
DO
IF NOT is upper( words[ char pos ] )
THEN
# not an upper-case letter, possibly a word has been ended #
in word := FALSE
ELSE
# not a delimitor, possibly the start of a word #
IF NOT in word
THEN
# we are starting a new word #
word number +:= 1;
in word := TRUE;
IF word number > 1
THEN
# second or subsequent word - need a separator #
result +:= IF word number = word count
THEN # final word #
" AND "
ELSE # non-final word #
", "
FI
FI
FI;
# add the character to the result #
result +:= words[ char pos ]
FI
OD;

result + "}"
END # to list # ;


# procedure to test the to list PROC #
PROC test to list = ( STRING words ) VOID:
print( ( ( words
+ ": "
+ to list( words )
)
, newline
)
);

# test the to list PROC #
test to list( "" );
test to list( "ABC" );
test to list( "ABC DEF" );
test to list( "ABC DEF G H" )</lang>
{{out}}
<pre>: {}
ABC: {ABC}
ABC DEF: {ABC AND DEF}
ABC DEF G H: {ABC, DEF, G AND H}
</pre>

=={{header|ALGOL W}}==
<lang algolw>begin

% sets list to the words contained in wordString, separated by ", ", %
% except for the last which is separated from the rest by " AND ". %
% The words are enclosed by braces %
procedure toList ( string(256) value words
; string(256) result list
) ;
begin
integer wordCount, wordNumber, listPos;
logical inWord;

% returns true if ch is an upper-case letter, false otherwise %
% assumes the letters are consecutive in the character set %
% (as in ascii) would not be correct if the character set was %
% ebcdic (as in the original implementations of Algol W) %
logical procedure isUpper ( string(1) value ch ) ; ch >= "A" and ch <= "Z" ;

% adds a character to the result %
procedure addChar( string(1) value ch ) ;
begin
list( listPos // 1 ) := ch;
listPos := listPos + 1;
end addChar ;

% adds a string to the result %
procedure addString( string(256) value str
; integer value len
) ;
for strPos := 0 until len - 1 do addChar( str( strPos // 1 ) );

% count the number of words %
wordCount := 0;
inWord := false;
for charPos := 0 until 255
do begin
if isUpper( words( charPos // 1 ) ) then begin
% not an upper-case letter, possibly a word has been ended %
inWord := false
end
else begin
% not a delimitor, possibly the start of a word %
if not inWord then begin
% we are starting a new word %
wordCount := wordCount + 1;
inWord := true
end if_not_inWord
end
end for_charPos;

% format the result %

list := "";
listPos := 0;
inWord := false;
wordNumber := 0;

addChar( "{" );

for charPos := 0 until 255
do begin
if not isUpper( words( charPos // 1 ) ) then begin
% not an upper-case letter, possibly a word has been ended %
inWord := false
end
else begin
% not a delimitor, possibly the start of a word %
if not inWord then begin
% we are starting a new word %
wordNumber := wordNumber + 1;
inWord := true;
if wordNumber > 1 then begin
% second or subsequent word - need a separator %
if wordNumber = wordCount then addString( " AND ", 5 ) % final word %
else addString( ", ", 2 ) % non-final word %
end
end;
% add the character to the result %
addChar( words( charPos // 1 ) )
end
end for_charPos ;

addChar( "}" );

end toList ;


% procedure to test the toList PROC %
procedure testToList ( string(256) value words ) ;
begin
string(256) list;
toList( words, list );
write( s_w := 0
, words( 0 // 32 )
, ": "
, list( 0 // 32 )
)
end testToList ;

% test the toList procedure %
testToList( "" );
testToList( "ABC" );
testToList( "ABC DEF" );
testToList( "ABC DEF G H" );

end.</lang>
{{out}}
<pre>
: {}
ABC : {ABC}
ABC DEF : {ABC AND DEF}
ABC DEF G H : {ABC, DEF, G AND H}
</pre>


=={{header|AutoHotkey}}==
=={{header|AutoHotkey}}==

Revision as of 16:23, 22 July 2015

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.

Ada

<lang Ada>with Ada.Text_IO, Ada.Command_Line; use Ada.Command_Line;

procedure Comma_Quibble is

begin

  case Argument_Count is
     when 0 => Ada.Text_IO.Put_Line("{}");
     when 1 => Ada.Text_IO.Put_Line("{" & Argument(1) & "}");
     when others => 

Ada.Text_IO.Put("{"); for I in 1 .. Argument_Count-2 loop Ada.Text_IO.Put(Argument(I) & ", "); end loop; Ada.Text_IO.Put(Argument(Argument_Count-1) & " and " & Argument(Argument_Count) & "}");

  end case;

end Comma_Quibble;</lang>

Output:
./comma_quibble
{}
./comma_quibble abc
{abc}
./comma_quibble abc def
{abc and def}
./comma_quibble abc def g h
{abc, def, g and h}

ALGOL 68

Works with: ALGOL 68G version Any - tested with release 2.8.win32

<lang algol68># returns a string ( assumed to be of space-separated words ) with the words #

  1. separated by ", ", except for the last which is separated from the rest by #
  2. " AND ". The list is enclosed by braces #

PROC to list = ( STRING words ) STRING:

   BEGIN
       # count the number of words                                           #
       INT  word count := 0;
       BOOL in word    := FALSE;
       FOR char pos FROM LWB words TO UPB words
       DO
           IF NOT is upper( words[ char pos ] )
           THEN
               # not an upper-case letter, possibly a word has been ended    #
               in word := FALSE
           ELSE
               # not a delimitor, possibly the start of a word               #
               IF NOT in word
               THEN
                   # we are starting a new word                              #
                   word count +:= 1;
                   in word     := TRUE
               FI
           FI
       OD;
       # format the result                                                   #
       STRING result    := "{";
       in word          := FALSE;
       INT  word number := 0;
       FOR char pos FROM LWB words TO UPB words
       DO
           IF NOT is upper( words[ char pos ] )
           THEN
               # not an upper-case letter, possibly a word has been ended    #
               in word := FALSE
           ELSE
               # not a delimitor, possibly the start of a word               #
               IF NOT in word
               THEN
                   # we are starting a new word                              #
                   word number +:= 1;
                   in word      := TRUE;
                   IF word number > 1
                   THEN
                       # second or subsequent word - need a separator        #
                       result +:= IF word number = word count
                                  THEN # final word                          #
                                      " AND "
                                  ELSE # non-final word                      #
                                      ", "
                                  FI
                   FI
               FI;
               # add the character to the result                             #
               result +:= words[ char pos ]
           FI
       OD;
       result + "}"
   END # to list # ;


   # procedure to test the to list PROC                                      #
   PROC test to list = ( STRING words ) VOID:
       print( ( ( words
                + ": "
                + to list( words )
                )
              , newline
              )
            );
   # test the to list PROC                                                   #
   test to list( "" );
   test to list( "ABC" );
   test to list( "ABC DEF" );
   test to list( "ABC DEF G H" )</lang>
Output:
: {}
ABC: {ABC}
ABC DEF: {ABC AND DEF}
ABC DEF G H: {ABC, DEF, G AND H}

ALGOL W

<lang algolw>begin

   % sets list to the words contained in wordString, separated by ", ",      %
   % except for the last which is separated from the rest by " AND ".        %
   % The words are enclosed by braces                                        %
   procedure toList ( string(256) value  words
                    ; string(256) result list
                    ) ;
   begin
       integer     wordCount, wordNumber, listPos;
       logical     inWord;
       % returns true if ch is an upper-case letter, false otherwise         %
       %         assumes the letters are consecutive in the character set    %
       %         (as in ascii) would not be correct if the character set was %
       %         ebcdic (as in the original implementations of Algol W)      %
       logical procedure isUpper ( string(1) value ch ) ; ch >= "A" and ch <= "Z" ;
       % adds a character to the result                                      %
       procedure addChar( string(1) value ch ) ;
       begin
           list( listPos // 1 ) := ch;
           listPos := listPos + 1;
       end addChar ;
       % adds a string to the result                                        %
       procedure addString( string(256) value str
                          ; integer     value len
                          ) ;
           for strPos := 0 until len - 1 do addChar( str( strPos // 1 ) );
       % count the number of words                                           %
       
       wordCount := 0;
       inWord    := false;
       for charPos := 0 until 255
       do begin
           if isUpper( words( charPos // 1 ) ) then begin
               % not an upper-case letter, possibly a word has been ended    %
               inWord := false
               end
           else begin
               % not a delimitor, possibly the start of a word               %
               if not inWord then begin
                   % we are starting a new word                              %
                   wordCount := wordCount + 1;
                   inWord    := true
               end if_not_inWord
           end 
       end for_charPos;
       % format the result                                                   %
       list       := "";
       listPos    := 0;
       inWord     := false;
       wordNumber := 0;
       addChar( "{" );
       for charPos := 0 until 255
       do begin
           if not isUpper( words( charPos // 1 ) ) then begin
               % not an upper-case letter, possibly a word has been ended    %
               inWord := false
               end
           else begin
               % not a delimitor, possibly the start of a word               %
               if not inWord then begin
                   % we are starting a new word                              %
                   wordNumber := wordNumber + 1;
                   inWord     := true;
                   if wordNumber > 1 then begin
                       % second or subsequent word - need a separator        %
                       if wordNumber = wordCount then addString( " AND ", 5 ) % final word %
                                                 else addString( ", ",    2 ) % non-final word %
                   end
               end;
               % add the character to the result                             %
               addChar( words( charPos // 1 ) )
           end
       end for_charPos ;
       addChar( "}" );
   end toList ;


   % procedure to test the toList PROC                                      %
   procedure testToList ( string(256) value words ) ;
   begin
       string(256) list;
       toList( words, list );
       write( s_w := 0
            , words( 0 // 32 )
            , ": "
            , list(  0 // 32 )
            )
   end testToList ;
   % test the toList procedure                                              %
   testToList( "" );
   testToList( "ABC" );
   testToList( "ABC DEF" );
   testToList( "ABC DEF G H" );

end.</lang>

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

AutoHotkey

<lang autohotkey>MsgBox % quibble([]) MsgBox % quibble(["ABC"]) MsgBox % quibble(["ABC", "DEF"]) MsgBox % quibble(["ABC", "DEF", "G", "H"])

quibble(d) { s:="" for i, e in d { if (i<d.MaxIndex()-1) s:= s . e . ", " else if (i=d.MaxIndex()-1) s:= s . e . " and " else s:= s . e } return "{" . s . "}" }</lang>

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

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}

Batch File

<lang dos>@echo off setlocal enabledelayedexpansion

THE MAIN THING...

echo. set inp=[] call :quibble set inp=["ABC"] call :quibble set inp=["ABC","DEF"] call :quibble set inp=["ABC","DEF","G","H"] call :quibble echo. pause exit /b

/THE MAIN THING...
THE FUNCTION
quibble

set cont=0 set proc=%inp:[=% set proc=%proc:]=%

for %%x in (%proc%) do ( set /a cont+=1 set x=%%x set str!cont!=!x:"=! ) set /a bef=%cont%-1 set output=%str1% if %cont%==2 (set output=%str1% and %str2%) if %cont% gtr 2 ( for /l %%y in (2,1,%bef%) do ( set output=!output!^, !str%%y! ) set output=!output! and !str%cont%! ) echo {!output!} goto :EOF

/THE FUNCTION</lang>
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

Press any key to continue . . .

C

<lang c>#include <stdio.h>

  1. include <string.h>
  2. include <stdlib.h>

char *quib(const char **strs, size_t size) {

   size_t len = 3 + ((size > 1) ? (2 * size + 1) : 0);
   size_t i;
   for (i = 0; i < size; i++)
       len += strlen(strs[i]);
   char *s = malloc(len * sizeof(*s));
   if (!s)
   {
       perror("Can't allocate memory!\n");
       exit(EXIT_FAILURE);
   }
   strcpy(s, "{");
   switch (size) {
       case 0:  break;
       case 1:  strcat(s, strs[0]);
                break;
       default: for (i = 0; i < size - 1; i++)
                {
                    strcat(s, strs[i]);
                    if (i < size - 2)
                        strcat(s, ", ");
                    else
                        strcat(s, " and ");
                }
                strcat(s, strs[i]);
                break;
   }  
   strcat(s, "}");
   return s;

}

int main(void) {

   const char *test[] = {"ABC", "DEF", "G", "H"};
   char *s;
   for (size_t i = 0; i < 5; i++)
   {
       s = quib(test, i);
       printf("%s\n", s);
       free(s);
   }
   return EXIT_SUCCESS;

}</lang>

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

C#

<lang csharp>using System; using System.Linq;

namespace CommaQuibbling {

   internal static class Program
   {
       #region Static Members
       private static string Quibble( string[] input )
       {
           var len = input.Length;
           return
               "{" +
               ( len > 1 ? ( String.Join( ", ", input.Take( len - 1 ) ) + " and " ) : ( "" ) ) +
               ( input.LastOrDefault() ?? "" ) +
               "}";
       }
       private static void Main()
       {
           Console.WriteLine( Quibble( new string[] {} ) );
           Console.WriteLine( Quibble( new[] {"ABC"} ) );
           Console.WriteLine( Quibble( new[] {"ABC", "DEF"} ) );
           Console.WriteLine( Quibble( new[] {"ABC", "DEF", "G", "H"} ) );
           Console.WriteLine( "< Press Any Key >" );
           Console.ReadKey();
       }
       #endregion
   }

}</lang>

Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}
< Press Any Key >

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

<lang clojure>(defn quibble [sq]

 (let [sep (if (pos? (count sq)) " and " "")]
   (apply str
     (concat "{" (interpose ", " (butlast sq)) [sep (last sq)] "}"))))
Or, using clojure.pprint's cl-format, which implements common lisp's format

(defn quibble-f [& args]

 (clojure.pprint/cl-format nil "{~{~a~#[~; and ~:;, ~]~}}" args))

(def test

 #(doseq [sq [[]
              ["ABC"]
              ["ABC", "DEF"]
              ["ABC", "DEF", "G", "H"]]]
    ((comp println %) sq)))

(test quibble) (test quibble-f)</lang>

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

COBOL

Works with: OpenCOBOL version 2.0

<lang cobol> >>SOURCE FORMAT IS FREE IDENTIFICATION DIVISION. PROGRAM-ID. comma-quibbling-test.

ENVIRONMENT DIVISION. CONFIGURATION SECTION. REPOSITORY.

   FUNCTION comma-quibbling
   .

DATA DIVISION. WORKING-STORAGE SECTION. 01 strs-area.

   03  strs-len                  PIC 9.
   03  strs                      PIC X(5)
                                 OCCURS 0 TO 9 TIMES
                                 DEPENDING ON strs-len.

PROCEDURE DIVISION.

   MOVE "ABC" TO strs (1)
   MOVE "DEF" TO strs (2)
   MOVE "G" TO strs (3)
   MOVE "H" TO strs (4)
   PERFORM VARYING strs-len FROM 0 BY 1 UNTIL strs-len > 4
       DISPLAY FUNCTION comma-quibbling(strs-area)
   END-PERFORM
   .

END PROGRAM comma-quibbling-test.


IDENTIFICATION DIVISION. FUNCTION-ID. comma-quibbling.

DATA DIVISION. LOCAL-STORAGE SECTION. 01 i PIC 9.

01 num-extra-words PIC 9.

LINKAGE SECTION. 01 strs-area.

   03  strs-len                  PIC 9.
   03  strs                      PIC X(5)
                                 OCCURS 0 TO 9 TIMES
                                 DEPENDING ON strs-len.

01 str PIC X(50).

PROCEDURE DIVISION USING strs-area RETURNING str.

   EVALUATE strs-len
       WHEN ZERO
           MOVE "{}" TO str
           GOBACK
       WHEN 1
           MOVE FUNCTION CONCATENATE("{", FUNCTION TRIM(strs (1)), "}")
               TO str
           GOBACK
   END-EVALUATE
   MOVE FUNCTION CONCATENATE(FUNCTION TRIM(strs (strs-len - 1)),
       " and ", FUNCTION TRIM(strs (strs-len)), "}")
       TO str
   IF strs-len > 2
       SUBTRACT 2 FROM strs-len GIVING num-extra-words
       PERFORM VARYING i FROM num-extra-words BY -1 UNTIL i = 0
           MOVE FUNCTION CONCATENATE(FUNCTION TRIM(strs (i)), ", ", str)
               TO str
       END-PERFORM
   END-IF
   
   MOVE FUNCTION CONCATENATE("{", str) TO str
   .

END FUNCTION comma-quibbling.</lang>

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

CoffeeScript

<lang coffeescript>quibble = ([most..., last]) ->

 '{' + 
   (most.join ', ') +
   (if most.length then ' and ' else )  + 
   (last or ) + 
 '}'

console.log quibble(s) for s in [ [], ["ABC"], ["ABC", "DEF"],

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

</lang>

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

Common Lisp

<lang lisp> (defun quibble (&rest args)

 (format t "{~{~a~#[~; and ~:;, ~]~}}" args))

(quibble) (quibble "ABC") (quibble "ABC" "DEF") (quibble "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}

Alternative Version

<lang d>import std.stdio, std.string, std.algorithm, std.conv, std.array;

enum quibbler = (in string[] a) pure =>

   "{%-(%s and %)}".format(a.length < 2 ? a :
                           [a[0 .. $-1].join(", "), a.back]);

void main() {

   [[], ["ABC"], ["ABC", "DEF"], ["ABC", "DEF", "G", "H"]]
   .map!quibbler.writeln;

}</lang>

Output:
["{}", "{ABC}", "{ABC and DEF}", "{ABC, DEF, G and H}"]

DCL

<lang DCL>$ list = "[]" $ gosub comma_quibbling $ write sys$output return_string $ $ list = "[""ABC""]" $ gosub comma_quibbling $ write sys$output return_string $ $ list = "[""ABC"", ""DEF""]" $ gosub comma_quibbling $ write sys$output return_string $ $ list = "[""ABC"", ""DEF"", ""G"", ""H""]" $ gosub comma_quibbling $ write sys$output return_string $ $ exit $ $ comma_quibbling: $ list = list - "[" - "]" $ return_string = "{}" $ if list .eqs. "" then $ return $ return_string = "{" + f$element( 0, ",", list ) - """" - """" $ if f$locate( ",", list ) .eq. f$length( list ) then $ goto done2 $ i = 1 $ loop: $ word = f$element( i, ",", list ) - """" - """" $ if word .eqs. "," then $ goto done1 $ return_string = return_string - "^" + "^," + word $ i = i + 1 $ goto loop $ done1: $ return_string = f$element( 0, "^", return_string ) + " and" + ( f$element( 1, "^", return_string ) - "," ) $ done2: $ return_string = return_string + "}" $ return</lang> {}out}}

$ @comma_quibbling
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

Déjà Vu

<lang dejavu>comma-quibble lst: "}" ) if lst: pop-from lst if lst: " and " pop-from lst for item in lst: item ", " concat( "{"

!. comma-quibble [] !. comma-quibble [ "ABC" ] !. comma-quibble [ "ABC" "DEF" ] !. comma-quibble [ "ABC" "DEF" "G" "H" ]</lang>

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

Eiffel

<lang Eiffel> class APPLICATION

create make

feature

make -- Test of the feature comma_quibbling. local l: LINKED_LIST [STRING] do create l.make io.put_string (comma_quibbling (l) + "%N") l.extend ("ABC") io.put_string (comma_quibbling (l) + "%N") l.extend ("DEF") io.put_string (comma_quibbling (l) + "%N") l.extend ("G") l.extend ("H") io.put_string (comma_quibbling (l) + "%N") end

comma_quibbling (l: LINKED_LIST [STRING]): STRING -- Elements of 'l' seperated by a comma or an and where appropriate. require l_not_void: l /= Void do create Result.make_empty Result.extend ('{') if l.is_empty then Result.append ("}") elseif l.count = 1 then Result.append (l [1] + "}") else Result.append (l [1]) across 2 |..| (l.count - 1) as c loop Result.append (", " + l [c.item]) end Result.append (" and " + l [l.count] + "}") end end

end </lang>

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

Elixir

Translation of: Erlang
<lang elixir>defmodule RC do
 def generate( list ), do: "{#{ generate_content(list) }}"
  
 defp generate_content( [] ), do: ""
 defp generate_content( [x] ), do: x
 defp generate_content( [x1, x2] ), do: "#{x1} and #{x2}"
 defp generate_content( xs ) do
   [last, second_to_last | t] = Enum.reverse( xs )
   with_commas = for x <- t, do: x <> ","
   Enum.join(Enum.reverse([last, "and", second_to_last | with_commas]), " ")
 end

end

Enum.each([[], ["ABC"], ["ABC", "DEF"], ["ABC", "DEF", "G", "H"]], fn list ->

 IO.inspect RC.generate(list)

end)</lang>

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

Erlang

<lang> -module( comma_quibbling ).

-export( [task/0] ).

task() -> [generate(X) || X <- [[], ["ABC"], ["ABC", "DEF"], ["ABC", "DEF", "G", "H"]]].


generate( List ) -> "{" ++ generate_content(List) ++ "}".

generate_content( [] ) -> ""; generate_content( [X] ) -> X; generate_content( [X1, X2] ) -> string:join( [X1, "and", X2], " " ); generate_content( Xs ) -> [Last, Second_to_last | T] = lists:reverse( Xs ), With_commas = [X ++ "," || X <- T], string:join(lists:reverse([Last, "and", Second_to_last | With_commas]), " "). </lang>

Output:
36> comma_quibbling:task().
["{}","{ABC}","{ABC and DEF}","{ABC, DEF, G and H}"]

F#

<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> Output from testing (in F# Interactive 3.0, Open Source version): <lang> > quibble [];; val it : string = "{}" > quibble ["ABC"];; val it : string = "{ABC}" > quibble ["ABC"; "DEF"];; val it : string = "{ABC and DEF}" > quibble ["ABC"; "DEF"; "G"];; val it : string = "{ABC, DEF and G}" > quibble ["ABC"; "DEF"; "G"; "H"];; val it : string = "{ABC, DEF, G and H}"</lang>

Go

The blog mentioned code maintenence. The idea here is to make the code easy for maintainers to understand by making it correspond as directly as possible to the problem description. <lang go>package main

import (

   "fmt"
   "strings"

)

func q(s []string) string {

   switch len(s) {
   case 0:
       return "{}"
   case 1:
       return "{" + s[0] + "}"
   case 2:
       return "{" + s[0] + " and " + s[1] + "}"
   default:
       return "{" +
           strings.Join(s[:len(s)-1], ", ") +
           " and " +
           s[len(s)-1] +
           "}"
   }

}

func main() {

   fmt.Println(q([]string{}))
   fmt.Println(q([]string{"ABC"}))
   fmt.Println(q([]string{"ABC", "DEF"}))
   fmt.Println(q([]string{"ABC", "DEF", "G", "H"}))

}</lang>

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

Groovy

<lang groovy>def commaQuibbling = { it.size() < 2 ? "{${it.join(', ')}}" : "{${it[0..-2].join(', ')} and ${it[-1]}}" }</lang> Testing: <lang groovy>['{}': [], '{ABC}': ['ABC'], '{ABC and DEF}': ['ABC', 'DEF'], '{ABC, DEF, G and H}': ['ABC', 'DEF', 'G', 'H']].each { expected, input ->

   println "Verifying commaQuibbling($input) == $expected"
   assert commaQuibbling(input) == expected

}</lang>

Output:
Verifying commaQuibbling([]) == {}
Verifying commaQuibbling([ABC]) == {ABC}
Verifying commaQuibbling([ABC, DEF]) == {ABC and DEF}
Verifying commaQuibbling([ABC, DEF, G, H]) == {ABC, DEF, G and H}

Haskell

<lang haskell>quibble ws = "{" ++ quibbles ws ++ "}"

 where quibbles [] = ""
       quibbles [a] = a
       quibbles [a,b] = a ++ " and " ++ b
       quibbles (a:bs) = a ++ ", " ++ quibbles bs

main = mapM_ (putStrLn . quibble) $

 [[], ["ABC"], ["ABC", "DEF"], ["ABC", "DEF", "G", "H"]] ++ 
 (map words ["One two three four", "Me myself I", "Jack Jill", "Loner" ])

</lang>

Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}
{One, two, three and four}
{Me, myself and I}
{Jack and Jill}
{Loner}

Icon and Unicon

The following works in both languages: <lang unicon>procedure main()

   every write(quibble([] | ["ABC"] | ["ABC","DEF"] | ["ABC","DEF","G","H"]))

end

procedure quibble(A)

   join := s := ""
   while s := pull(A)||join||s do join := if *join = 0 then " and " else ", "
   return "{"||s||"}"

end</lang>

Sample run:

->cq
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}
->

J

<lang j>quibLast2=: ' and ' joinstring (2 -@<. #) {. ] withoutLast2=: ([: # _2&}.) {. ] quibble=: '{', '}' ,~ ', ' joinstring withoutLast2 , <@quibLast2</lang>

Testing: <lang j> Tests=: (<);(<'ABC');('ABC';'DEF');<('ABC';'DEF';'G';'H')

  quibble every Tests

{} {ABC} {ABC and DEF} {ABC, DEF, G and H}</lang>

Alternative implementation:

<lang j>commaand=: 1 ;@}.&, ] ,.~ 1 |.!.(<' and ') (<', ')"0 quibble=: '{','}',~ commaand</lang>

(same results)

Java

<lang Java>public class Quibbler {

public static String quibble(String[] words) { String qText = "{"; for(int wIndex = 0; wIndex < words.length; wIndex++) { qText += words[wIndex] + (wIndex == words.length-1 ? "" : wIndex == words.length-2 ? " and " : ", "; } qText += "}"; return qText; }

public static void main(String[] args) { System.out.println(quibble(new String[]{})); System.out.println(quibble(new String[]{"ABC"})); System.out.println(quibble(new String[]{"ABC", "DEF"})); System.out.println(quibble(new String[]{"ABC", "DEF", "G"})); System.out.println(quibble(new String[]{"ABC", "DEF", "G", "H"})); } }</lang>

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

JavaScript

<lang javascript>function quibble(words) {

 return "{" + 
   words.slice(0, words.length-1).join(",") +
  (words.length > 1 ? " and " : "") +
  (words[words.length-1] || ) +
 "}";

}

[[], ["ABC"], ["ABC", "DEF"], ["ABC", "DEF", "G", "H"]].forEach(

 function(s) {
   console.log(quibble(s));
 }

);</lang>

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

jq

Works with: jq version 1.4

<lang jq>def quibble:

 if length == 0 then "" 
 elif length == 1 then .[0]
 else (.[0:length-1] | join(", ")) + " and " + .[length-1]
 end
 | "{" + . + "}";</lang>

Example: <lang jq>( [], ["ABC"], ["ABC", "DEF"], ["ABC", "DEF", "G", "H"]) | quibble</lang>

Output:

<lang sh>jq -n -r -f Comma_quibbling.jq {} {ABC} {ABC and DEF} {ABC, DEF, G and H} </lang>

Julia

<lang Julia>quibble(words) =

 "{"* (isempty(words)  ? ""       :
       length(words)==1? words[1] :
       join(words[1:end-1],", ")*" and "*words[end]) *"}"</lang>
Output:
julia> quibble([])
"{}"

julia> quibble(["ABC"])
"{ABC}"

julia> quibble(["ABC","DEF"])
"{ABC and DEF}"

julia> quibble(["ABC","DEF","G","H"])
"{ABC, DEF, G and H}"

Lasso

<lang Lasso>#!/usr/bin/lasso9

local(collection = array( array, array("ABC"), array("ABC", "DEF"), array("ABC", "DEF", "G", "H") ) )

with words in #collection do { if(#words -> size > 1) => { local(last = #words -> last) #words -> removelast stdoutnl('{' + #words -> join(', ') + ' and ' + #last'}') else(#words -> size == 1) stdoutnl('{' + #words -> first + '}') else stdoutnl('{}') }

}</lang>

Output:

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

Liberty BASIC

<lang lb> do

       read in$
       if in$ ="END" then wait
       w =wordCount( in$)
       select case w
           case 0
               o$ ="{}"
           case 1
               o$ ="{" +in$ +"}"
           case 2
               o$ ="{" +word$( in$, 1) +" and " +word$( in$, 2) +"}"
           case else
               o$ ="{"
               o$ =o$ +word$( in$, 1)
               for k =2 to w -1
                   o$ =o$ +", " +word$( in$, k)
               next k
               o$ =o$ +" and " +word$( in$, w) +"}"
       end select
       if w =1 then
           print "'"; in$; "'"; " held "; w; " word. "; tab( 30); o$
       else
           print "'"; in$; "'"; " held "; w; " words. "; tab( 30); o$
       end if
   loop until 0
   wait
   function wordCount( IN$)
       wordCount =1
       for i =1 to len( IN$)
           if mid$( IN$, i, 1) =" " then wordCount =wordCount +1
       next i
   end function
   end
   data ""                 'No input words.
   data "ABC"              'One input word.
   data "ABC DEF"          'Two words.
   data "ABC DEF G"        'Three words.
   data "ABC DEF G H"      'Four words.
   data "END"              'Sentinel for EOD.

</lang>

Output:
'' held 1 word.              {}
'ABC' held 1 word.           {ABC}
'ABC DEF' held 2 words.      {ABC and DEF}
'ABC DEF G' held 3 words.    {ABC, DEF and G}
'ABC DEF G H' held 4 words.  {ABC, DEF, G and H}

<lang Logo>to join :delimiter :list [:result []]

 output cond [
   [ [empty? :list]   :result ]
   [ [empty? :result] (join :delimiter butfirst :list first :list) ]
   [ else             (join :delimiter butfirst :list 
                                       (word :result :delimiter first :list)) ]
 ]

end

to quibble :list

 local "length
 make "length count :list
 make "text (
   ifelse [:length <= 2] [
     (join "\ and\  :list)
   ] [ 
     (join "\ and\  (sentence join ",\  butlast :list last :list))
   ])
 output ifelse [empty? :text] "\{\} [(word "\{ :text "\})]

end

foreach [ [] [ABC] [ABC DEF] [ABC DEF G H] ] [

 print quibble ?

]

bye</lang>

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

Maple

<lang Maple>Quibble := proc( los )

 uses  StringTools;
 Fence( proc()
       if los = [] then
         ""
       elif numelems( los ) = 1 then
         los[ 1 ]
       else
         cat( Join( los[ 1 .. -2 ], ", " ), " and ", los[ -1 ] )
       end if
 end(), "{", "}" )

end proc:</lang>

Check it on the required inputs: <lang Maple>> Quibble([]);

                                 "{}"

> Quibble( [ "ABC" ] );

                               "{ABC}"

> Quibble( [ "ABC", "DEF" ] );

                           "{ABC and DEF}"

> Quibble( ["ABC", "DEF", "G", "H"] );

                        "{ABC, DEF, G and H}"

</lang>

Mathematica

<lang Mathematica>quibble[words___] :=

   ToString@{StringJoin@@
       Replace[Riffle[{words}, ", "],
           {most__, ", ", last_} -> {most, " and ", last}]}</lang>
Output:
In[2]:= quibble[]
Out[2]= {}

In[3]:= quibble["ABC"]
Out[3]= {ABC}

In[4]:= quibble["ABC","DEF"]
Out[4]= {ABC and DEF}

In[5]:= quibble["ABC","DEF","G","H"]
Out[5]= {ABC, DEF, G and H}

MAXScript

<lang MAXScript> fn separate words: = ( if words == unsupplied or words == undefined or classof words != array then return "{}" else ( local toReturn = "{" local pos = 1 while pos <= words.count do ( if pos == 1 then (append toReturn words[pos]; pos+=1) else ( if pos <= words.count-1 then (append toReturn (", "+words[pos]); pos+=1) else ( append toReturn (" and " + words[pos]) pos +=1 ) ) ) return (toReturn+"}") ) ) </lang> Output: <lang MAXScript> separate words:#() "{}" separate words:#("ABC") "{ABC}" separate words:#("ABC","DEF") "{ABC and DEF}" separate words:#("ABC","DEF","G","H") "{ABC, DEF, G and H}" </lang>

NetRexx

<lang NetRexx>/* NetRexx */ options replace format comments java crossref symbols nobinary

runSample(arg) return

-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ method quibble(arg) public static

 parse arg '[' lst ']'
 lst = lst.changestr('"', ).space(1)
 lc = lst.lastpos(',')
 if lc > 0 then
   lst = lst.insert('and', lc).overlay(' ', lc)
 return '{'lst'}'

-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ method runSample(arg) private static

 lists = ['[]', -                     -- {}
          '["ABC"]', -                -- {ABC}
          '["ABC", "DEF"]', -         -- {ABC and DEF}
          '["ABC", "DEF", "G", "H"]'] -- {ABC, DEF, G and H}
 loop lst over lists
   say lst.right(30) ':' quibble(lst)
   end lst
 return

</lang>

Output:
                            [] : {}
                       ["ABC"] : {ABC}
                ["ABC", "DEF"] : {ABC and DEF}
      ["ABC", "DEF", "G", "H"] : {ABC, DEF, G and H}

Nim

<lang nim>proc commaQuibble(s): string =

 result = ""
 for i, c in s:
   if i > 0: result.add (if i < s.high: ", " else: " and ")
   result.add c
 result = "{" & result & "}"

var s = @[@[], @["ABC"], @["ABC", "DEF"], @["ABC", "DEF", "G", "H"]] for i in s:

 echo commaQuibble(i)</lang>

Oforth

<lang Oforth>func: quibbing(l) { | i s |

  StringBuffer new "{" <<
  l size dup 1 - ->s loop: i [ 
     l at(i) <<
     i s < ifTrue: [ ", " << continue ]
     i s == ifTrue: [ " and " << ]
     ]
  "}" << dup freeze

}</lang>

Output:
[ [], ["ABC"], ["ABC", "DEF"], ["ABC", "DEF", "G", "H"] ] map(#quibbing) println
[{}, {ABC}, {ABC and DEF}, {ABC, DEF, G and H}]

PARI/GP

<lang parigp>comma(v)={

 if(#v==0, return("{}"));
 if(#v==1, return(Str("{"v[1]"}")));
 my(s=Str("{",v[1]));
 for(i=2,#v-1,s=Str(s,", ",v[i]));
 Str(s," and ",v[#v],"}")

}; comma([]) comma(["ABC"]) comma(["ABC", "DEF"]) comma(["ABC", "DEF", "G", "H"])</lang> Output:

%1 = "{}"
%2 = "{ABC}"
%3 = "{ABC and DEF}"
%4 = "{ABC, DEF, G and H}"

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 5.01 version and other approach:

<lang perl>use 5.01; sub comma_quibbling{

 my $last = pop // ;
 return '{'. (@_ ? (join ', ', @_).' and '.$last : $last).'}';

}

say for map {comma_quibbling(@$_)}

 [], [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}

PHP

<lang php><?php

function quibble($arr){

 $words = count($arr);
 if($words == 0){
   return '{}';
 }elseif($words == 1){
   return '{'.$arr[0].'}';
 }elseif($words == 2){
   return '{'.$arr[0].' and '.$arr[1].'}';
 }else{
   return '{'.implode(', ',  array_splice($arr, 0, -1) ). ' and '.$arr[0].'}';
 }

}


$tests = [

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

];

foreach ($tests as $test) {

 echo quibble($test) . PHP_EOL;

}</lang>

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

PicoLisp

<lang PicoLisp>(for L '([] ["ABC"] ["ABC", "DEF"] ["ABC", "DEF", "G", "H"])

  (let H (head -1 L)
     (prinl
        "{"
        (glue ", " H)
        (and H " and ")
        (last L)
        "}" ) ) )</lang>

Output:

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


PL/I

<lang pli>*process or(!);

quib: Proc Options(main);
/*********************************************************************
* 06.10.2013 Walter Pachl
*********************************************************************/
  put Edit*process or(!);
quib: Proc Options(main);
/*********************************************************************
* 06.10.2013 Walter Pachl
* 07.10.2013 -"- change "Oxford comma" to and
*********************************************************************/
  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 Init();
  Dcl word(10) 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;
        word(wi)=s;
        s=;
        End;
      Else Do;
        word(wi)=left(s,p-1);
        s=substr(s,p+1);
        End;
      end;
    wn=wi-1;
    result=word(1);
    Do i=2 To wn-1;
      result=result!!', '!!word(i);
      End;
    If wn>1 Then
      result=result!!' and '!!word(wn);
    End;
  Return('{'!!result!!'}');
  End;
End;</lang>
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}      

Prolog

Works with: SWI-Prolog version 7.1

<lang prolog>words_series(Words, Bracketed) :-

   words_serialized(Words, Serialized),
   atomics_to_string(["{",Serialized,"}"], Bracketed).

words_serialized([], ""). words_serialized([Word], Word) :- !. words_serialized(Words, Serialized) :-

   append(Rest, [Last], Words),                                  %% Splits the list of *Words* into the *Last* word and the *Rest* 
   atomics_to_string(Rest, ", ", WithCommas),                     
   atomics_to_string([WithCommas, " and ", Last], Serialized).


test :-

   forall( member(Words, [[], ["ABC"], ["ABC", "DEF"], ["ABC", "DEF", "G", "H"]]),
           ( words_series(Words, Series),
             format('~w ~15|=> ~w~n', [Words, Series]))
         ).</lang>
Output:

<lang prolog>?- test. [] => {} [ABC] => {ABC} [ABC,DEF] => {ABC and DEF} [ABC,DEF,G,H] => {ABC, DEF, G and H} true.</lang>

Python

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>

Python: Counted replacement

(Possible)

Translation of: Tcl

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}'

Python: Functional

<lang python>>>> def quibble(s):

   return ('{' +
               (', '.join(s[:-1]) + ' and ' if len(s) > 1 else ) +

(s[-1] if s else ) + '}')

>>> for seq in ([], ["ABC"], ["ABC", "DEF"], ["ABC", "DEF", "G", "H"]): print('Input: %-24r -> Output: %r' % (seq, quibble(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>

Racket

<lang Racket>(define (quibbling words)

 (define (sub-quibbling words)
   (match words
     ['() ""]
     [(list a) a]
     [(list a b) (format "~a and ~a" a b)]
     [(list a b ___) (format "~a, ~a" a (sub-quibbling b))]))
 (format "{~a}" (sub-quibbling words)))

(for ((input '([] ["ABC"] ["ABC" "DEF"] ["ABC" "DEF" "G" "H"])))

 (printf "~s\t->\t~a~%" input (quibbling input)))</lang>
Output:
()	->	{}
("ABC")	->	{ABC}
("ABC" "DEF")	->	{ABC and DEF}
("ABC" "DEF" "G" "H")	->	{ABC, DEF, G and H}

REBOL

Straightforward implementation

<lang REBOL>Rebol []

comma-quibbling: func [block] [

   rejoin [
       "^{"
       to-string use [s] [
           s: copy block
           s: next s
           forskip s 2 [insert s either tail? next s [" and "] [", "]]
           s: head s
       ]
       "^}"
   ]

]

foreach t [[] [ABC] [ABC DEF] [ABC DEF G H]] [print comma-quibbling t] </lang>

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

Alternative (more efficient) version with oxford comma switch

<lang REBOL>Rebol []

builds string instead of using an intermediate block

comma-quibbling: func [block /oxford /local s length] [

   length: length? block
   rejoin [
       "^{"
       either length < 2 [to-string block] [
           s: to-string block/1
           for n 2 (length - 1) 1 [repend s [", " pick block n]]
           if all [oxford (length > 2)] [append s ","]
           repend s [" and " last block]
       ]
       "^}"
   ]

]

test: [[] [ABC] [ABC DEF] [ABC DEF G H]] foreach t test [print comma-quibbling t] print "Now with Oxford comma" foreach t test [print comma-quibbling/oxford t] </lang>

Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}
Now with Oxford comma
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G, and H}

REXX

version 1:

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

version 2:

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

 parse arg list
 If list= Then result=
 Else Do
   Do wi=1 By 1 while list<>
     Parse Var list word.wi ' ' list
     End
   wn=wi-1
   result=word.1
   Do wi=2 To wn-1
     result=result', 'word.wi
     End
   If wn>1 Then
     result=result 'and' word.wn
   End
 Return '{'result'}'</lang>     
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}    

version 3:

Translation of: NetRexx

<lang Rexx>/* Rexx */

i_ = 0 i_ = i_ + 1; lists.0 = i_; lists.i_ = '[]' i_ = i_ + 1; lists.0 = i_; lists.i_ = '["ABC"]' i_ = i_ + 1; lists.0 = i_; lists.i_ = '["ABC", DEF]' i_ = i_ + 1; lists.0 = i_; lists.i_ = '[ABC, DEF, G, H]'

say do i_ = 1 to lists.0

 list = lists.i_
 say right(list, 30) ':' quibbling03(list)
 end i_

exit

quibbling03: procedure

 parse arg '[' lst ']'
 lst = changestr('"', changestr("'", lst, ), ) -- remove double & single quotes
 lc = lastpos(',', lst)
 if lc > 0 then
   lst = overlay(' ', insert('and', lst, lc), lc)
 lst = space(lst, 1) -- remove extra spaces
 return '{'lst'}'

</lang>

Output:
                            [] : {}
                       ["ABC"] : {ABC}
                ["ABC", 'DEF'] : {ABC and DEF}
              [ABC, DEF, G, H] : {ABC, DEF, G and H}

Ruby

Translation of: Perl 6

<lang ruby>def comma_quibbling(a)

 %w<{ }>.join(a.length < 2 ? a.first :
              "#{a[0..-2].join(', ')} and #{a[-1]}")

end

[[], %w<ABC>, %w<ABC DEF>, %w<ABC DEF G H>].each do |a|

 puts comma_quibbling(a)

end</lang>

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


Run BASIC

<lang runbasic>wrds$ = "[] [""ABC""] [""ABC"", ""DEF""] [""ABC"", ""DEF"", ""G"", ""H""] " while word$(wrds$,j+1,chr$(13)) <> ""

 a$ = word$(wrds$,j+1,chr$(13))
 print a$;" ==> ";
 a$ = "{"+mid$(a$,2,len(a$)-2)+"}"
 j = j + 1
 for i = len(a$) to 1 step -1
   if mid$(a$,i,1) = "," then 
      a$ =  left$(a$,i-1) + " and " + mid$(a$,i+2) 
      exit for
   end if
 next i
 print a$

WEND</lang>

Output:
[] ==> {}
["ABC"] ==> {"ABC"}
["ABC", "DEF"] ==> {"ABC" and  "DEF"}
["ABC", "DEF", "G", "H"] ==> {"ABC", "DEF", "G" and  "H"}

Rust

<lang Rust>// rust 0.9-pre

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

   match seq {
       [] => ~"{}",
       [ref word] => "{" + *word + "}",
       [..words, ref word] => "{" + words.connect(", ") + " and " + *word + "}",
   }

}

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}

Seed7

<lang Seed7>$ include "seed7_05.s7i";

const func string: quibble (in array string: input) is func

 result
   var string: quibble is "{";
 begin
   case length(input) of
     when {0}:  quibble &:= "}";
     when {1}:  quibble &:= input[1] & "}";
     otherwise: quibble &:= join(input[.. pred(length(input))], ", ") &
                            " and " & input[length(input)] & "}";
   end case;
 end func;

const proc: main is func

 begin
   writeln(quibble(0 times ""));
   writeln(quibble([] ("ABC")));
   writeln(quibble([] ("ABC", "DEF")));
   writeln(quibble([] ("ABC", "DEF", "G", "H")));
 end func;</lang>
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

Sidef

<lang ruby>func comma_quibbling(words) {

   '{' + ([words.ft(0, -2).join(', ')]-[] + [words.last] -> join(' and ')) + '}';

}

[<>, <ABC>, <ABC DEF>, <ABC DEF G H>].each { |w|

   say comma_quibbling(w);

}</lang>

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

Standard ML

<lang sml>local

 fun quib []      = ""
   | quib [x]     = x
   | quib [x0,x1] = x0 ^ " and " ^ x1
   | quib (x::xs) = x ^ ", " ^ quib xs

in

 fun quibble xs = "{" ^ quib xs ^ "}"

end

(* Tests: *) val t_quibble_0 = quibble [] = "{}" val t_quibble_1 = quibble ["ABC"] = "{ABC}" val t_quibble_2 = quibble ["ABC", "DEF"] = "{ABC and DEF}" val t_quibble_3 = quibble ["ABC", "DEF", "G", "H"] = "{ABC, DEF, G and H}" </lang>

Swift

<lang Swift>let inputs = [[], ["ABC"], ["ABC", "DEF"], ["ABC", "DEF", "G", "H"]]

func quibbling(var words:[String]) {

   if words.count == 0 {
       println("{}")
   } else if words.count == 1 {
       println("{\(words[0])}")
   } else if words.count == 2 {
       println("{\(words[0]) and \(words[1])}")
   } else {
       var output = "{"
       while words.count != 2 {
           output += words.removeAtIndex(0) + ", "
       }
       output += "\(words.removeAtIndex(0)) and \(words.removeAtIndex(0))}"
       
       println(output)
   }

}

for word in inputs {

   quibbling(word)

}</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}

VBScript

<lang vb>Function Quibble(s) arr = Split(s,",") If s = "" Then Quibble = "{}" ElseIf UBound(arr) = 0 Then Quibble = "{" & arr(0) & "}" Else Quibble = "{" For i = 0 To UBound(arr) If i = UBound(arr) - 1 Then Quibble = Quibble & arr(i) & " and " & arr(i + 1) & "}" Exit For Else Quibble = Quibble & arr(i) & ", " End If Next End If End Function

WScript.StdOut.Write Quibble("") WScript.StdOut.WriteLine WScript.StdOut.Write Quibble("ABC") WScript.StdOut.WriteLine WScript.StdOut.Write Quibble("ABC,DEF") WScript.StdOut.WriteLine WScript.StdOut.Write Quibble("ABC,DEF,G,H") WScript.StdOut.WriteLine</lang>

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

XPL0

<lang XPL0>include c:\cxpl\codes;

proc Quibble(N, S); int N, S; int I; [ChOut(0, ^{); for I:= 0 to N-1 do

   [Text(0, S(I));
   if I<N-2 then Text(0, ", ");
   if I=N-2 then Text(0, " and ");
   ];

ChOut(0, ^}); ];

int I; for I:= 0 to 4 do

   if I#3 then [Quibble(I, ["ABC", "DEF", "G", "H"]);  CrLf(0)]

</lang>

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

zkl

This is a cheese ball solution that replies on no commas in the inputs <lang zkl>fcn quib(list){ text:=("{"+list.toString(*)[2,-1]+"}").replace("\"","");

  if(list.len()<2) text;
  else{
     z:=(text=text.replace(",",", ")).rfind(","); 
     String(text[0,z]," and ",text[z+2,*])
  }

}</lang> List.toString("*") converts List(1,2,3) to "L(1,2,3)" with all elements; without the *, long lists are shortened to L(1,2,3,...)

Output:
quib(List)     //-->"{}"
quib(L("ABC")) //-->"{ABC}"
quib(L("ABC", "DEF")) //-->"{ABC and DEF}"
quib(L("ABC", "DEF", "G", "H")) //-->"{ABC, DEF, G and H}"