Comma quibbling

From Rosetta Code
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.


Task

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.

11l

<lang 11l>F quibble(words)

  R S words.len
     0
        ‘{}’
     1
        ‘{’words[0]‘}’
     E
        ‘{’words[0.<(len)-1].join(‘, ’)‘ and ’words.last‘}’

print(quibble([‘’] * 0)) print(quibble([‘ABC’])) print(quibble([‘ABC’, ‘DEF’])) print(quibble([‘ABC’, ‘DEF’, ‘G’, ‘H’]))</lang>

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

360 Assembly

<lang 360asm>* Comma quibbling 13/03/2017 COMMAQUI CSECT

        USING  COMMAQUI,R13       base register
        B      72(R15)            skip savearea
        DC     17F'0'             savearea
        STM    R14,R12,12(R13)    save previous context
        ST     R13,4(R15)         link backward
        ST     R15,8(R13)         link forward
        LR     R13,R15            set addressability
        LA     R6,1               i=1
      DO WHILE=(C,R6,LE,=A(N))    do i=1 to hbound(t)
        LR     R1,R6                i
        SLA    R1,5                 *32
        LA     R2,T-32              @t(0)
        AR     R1,R2                @t(i)
        MVC    S1,0(R1)             s1=t(i)
        MVC    S2,=CL32'{'          s2='{'
        LA     R8,S2+1              s2ins=1
        MVC    I2,=F'0'             i2=0
        LA     R7,1                 j=1
      DO WHILE=(C,R7,LE,=A(L'T))    do j=1 to length(t)
        LA     R1,S1                  @s1
        BCTR   R1,0                   @s1-1
        AR     R1,R7                  @s1-1+j
        MVC    CJ,0(R1)               cj=mid(s1,j,1)
        CLI    CJ,C' '                if cj=' '
        BE     EXITJ                  then goto exitj
      IF CLI,CJ,EQ,C',' THEN          if cj="," then
        MVC    0(2,R8),=C', '           s2=s2||", "
        LA     R8,2(R8)                 s2ins=s2ins+2
        LR     R0,R8                    s2ins
        LA     R1,S2+1                  @s2+1
        SR     R0,R1                    len(s2)-1
        ST     R0,I2                    i2=len(s2)-1
      ELSE     ,                      else
        MVC    0(1,R8),CJ               s2=s2||cj
        LA     R8,1(R8)                 s2ins=s2ins+1
      ENDIF    ,                      endif
        LA     R7,1(R7)               j++
      ENDDO    ,                    enddo j

EXITJ MVI 0(R8),C'}' s2=s2||"}"

        LA     R8,1(R8)             s2ins=s2ins+1
        L      R0,I2                i2
      IF LTR,R0,NZ,R0 THEN          if i2<>0 then
        MVC    S2B,S2                 s2b=mid(s2,1,i2-1)
        LA     R1,S2B-1               @s2b-1
        A      R1,I2                  +i2
        MVC    0(5,R1),=C' and '      s2b||" and "
        LA     R1,5(R1)               +5 
        LA     R2,S2+1                @s2+1
        A      R2,I2                  +i2
        LR     R3,R8                  s2ins
        LA     R0,S2+1                @s2+1
        SR     R3,R0                  s2ins-(@s2+1)
        S      R3,I2                  -i2
        BCTR   R3,0                   -1
        EX     R3,XMVC                s2b||=mid(s2,i2+2)
        MVC    S2,S2B     s2=mid(s2,1,i2-1)||" and "||mid(s2,i2+2)
      ENDIF    ,                    endif
        XPRNT  S2,L'S2              print s2
        LA     R6,1(R6)             i++
      ENDDO    ,                  enddo i
        L      R13,4(0,R13)       restore previous savearea pointer
        LM     R14,R12,12(R13)    restore previous context
        XR     R15,R15            rc=0
        BR     R14                exit

XMVC MVC 0(0,R1),0(R2) mvc @r1,@r2 N EQU (TEND-T)/L'T items of t T DC CL32' ',CL32'ABC',CL32'ABC,DEF',CL32'ABC,DEF,G,H' TEND DS 0C I2 DS F S1 DS CL(L'T) S2 DS CL(L'T) S2B DS CL(L'T) CJ DS CL1

        YREGS
        END    COMMAQUI</lang>
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

8080 Assembly

<lang 8080asm> org 100h jmp demo ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Given a list of strings in HL, and a pointer in DE, write ;; the resulting string starting at DE. quibble: mvi a,'{' ; Write the first {, stax d inx d ; And increment the pointer push h ; Keep start of list call strseqlen ; Get length of list pop h ; Restore start of list xra a ; Is the list empty? ora b jz quibend ; If empty list, we're done. quibcopy: call strcpy ; Copy current string into output inx h ; Advance input pointer to next string dcr b ; Decrement counter jz quibend ; If zero, that was the last string push h ; Push input pointer mov a,b ; Is the counter 1 now? cpi 1 lxi h,quibcomma ; Add a comma and space, jnz quibsep ; unless the counter was 1, lxi h,quiband ; then use " and " quibsep: call strcpy ; Copy the separator into the output pop h ; Restore the input pointer jmp quibcopy ; Do the next string in the list quibend: mvi a,'}' ; Write the final '}' stax d inx d mvi a,'$' ; And write a string terminator stax d ret quibcomma: db ', $' quiband: db ' and $' ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Copy the string under HL to DE until the terminator $. ;; The terminator is not copied; HL and DE are left one byte ;; beyond the last byte copied. strcpy: mov a,m ; Get byte from input cpi '$' ; Are we at the end? rz ; Then stop. stax d ; Otherwise, store byte at output inx h ; Increment the pointers inx d jmp strcpy ; Copy next byte. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Return in B the amount of strings in the string list in HL strseqlen: mvi a,'$' ; String end mvi b,0 ; String counter count: cmp m ; Empty string? rz ; Then we're done inr b ; Otherwise, we have a string strsrch: cmp m ; Find the end of the string inx h jnz strsrch jmp count ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Demo code: run 'quibble' on the examples demo: mvi c,4 ; Four examples lxi h,examples ; Pointer to first example example: push b ; Push example count lxi d,buffer ; Into the buffer, call quibble ; write the output of comma-quibbling inx h ; Point to next example push h ; Save pointer to next example lxi d,buffer ; Write the output to the console mvi c,9 call 5 lxi d,newline ; Write a newline to the console mvi c,9 call 5 pop h ; Restore example pointer pop b ; Restore example counter dcr c ; If not zero, jnz example ; do the next example. ret newline: db 10,13,'$' examples: db '$' db 'ABC$$' db 'ABC$DEF$$' db 'ABC$DEF$G$H$$' buffer:</lang>

Output:
A>quib
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

Action!

<lang Action!>DEFINE PTR="CARD"

PROC Append(CHAR ARRAY text,suffix)

 BYTE POINTER srcPtr,dstPtr
 BYTE len
 len=suffix(0)
 IF text(0)+len>255 THEN
   len=255-text(0)
 FI
 IF len THEN
   srcPtr=suffix+1
   dstPtr=text+text(0)+1
   MoveBlock(dstPtr,srcPtr,len)
   text(0)==+suffix(0)
 FI

RETURN

PROC Quibble(PTR ARRAY items INT count CHAR ARRAY result)

 INT i
 result(0)=0
 Append(result,"(")
 FOR i=0 TO count-1
 DO
   Append(result,items(i))
   IF i=count-2 THEN
     Append(result," and ")
   ELSEIF i<count-2 THEN
     Append(result,", ")
   FI
 OD
 Append(result,")")

RETURN

PROC Test(PTR ARRAY items BYTE count)

 CHAR ARRAY result(256)
 Quibble(items,count,result)
 PrintE(result)

RETURN

PROC Main()

 PTR ARRAY items(5)
 Test(items,0)
 items(0)="ABC"
 Test(items,1)
 items(1)="DEF"
 Test(items,2)
 items(2)="G"
 Test(items,3)
 items(3)="H"
 Test(items,4)

RETURN</lang>

Output:

Screenshot from Atari 8-bit computer

()
(ABC)
(ABC and DEF)
(ABC, DEF and G)
(ABC, DEF, G and H)

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

   % returns a list of 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                                        %
   string(256) procedure toList ( string(256) value  words ) ;
   begin
       string(256) list;
       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( "}" );
       list
   end toList ;


   % procedure to test the toList procedure                                 %
   procedure testToList ( string(256) value words ) ;
   begin
       string(256) list;
       list := toList( words );
       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}

AppleScript

Translation of: JavaScript

<lang AppleScript>-- quibble :: [String] -> String on quibble(xs)

   if length of xs > 1 then
       set applyCommas to ¬
           compose([curry(my intercalate)'s |λ|(", "), my |reverse|, my tail])
       
       intercalate(" and ", ap({applyCommas, my head}, {|reverse|(xs)}))
   else
       concat(xs)
   end if

end quibble

-- TEST ----------------------------------------------------------------------- on run

   script braces
       on |λ|(x)
           "{" & x & "}"
       end |λ|
   end script
   
   unlines(map(compose({braces, quibble}), ¬
       append({{}, {"ABC"}, {"ABC", "DEF"}, {"ABC", "DEF", "G", "H"}}, ¬
           map(|words|, ¬
               {"One two three four", "Me myself I", "Jack Jill", "Loner"}))))

end run


-- GENERIC FUNCTIONS ----------------------------------------------------------

-- A list of functions applied to a list of arguments -- (<*> | ap) :: [(a -> b)] -> [a] -> [b] on ap(fs, xs)

   set {intFs, intXs} to {length of fs, length of xs}
   set lst to {}
   repeat with i from 1 to intFs
       tell mReturn(item i of fs)
           repeat with j from 1 to intXs
               set end of lst to |λ|(contents of (item j of xs))
           end repeat
       end tell
   end repeat
   return lst

end ap

-- (++) :: [a] -> [a] -> [a] on append(xs, ys)

   xs & ys

end append

-- compose :: [(a -> a)] -> (a -> a) on compose(fs)

   script
       on |λ|(x)
           script
               on |λ|(a, f)
                   mReturn(f)'s |λ|(a)
               end |λ|
           end script
           
           foldr(result, x, fs)
       end |λ|
   end script

end compose

-- concat :: a -> [a] | [String] -> String on concat(xs)

   script append
       on |λ|(a, b)
           a & b
       end |λ|
   end script
   
   if length of xs > 0 and class of (item 1 of xs) is string then
       set unit to ""
   else
       set unit to {}
   end if
   foldl(append, unit, xs)

end concat

-- curry :: (Script|Handler) -> Script on curry(f)

   script
       on |λ|(a)
           script
               on |λ|(b)
                   |λ|(a, b) of mReturn(f)
               end |λ|
           end script
       end |λ|
   end script

end curry

-- foldl :: (a -> b -> a) -> a -> [b] -> a on foldl(f, startValue, xs)

   tell mReturn(f)
       set v to startValue
       set lng to length of xs
       repeat with i from 1 to lng
           set v to |λ|(v, item i of xs, i, xs)
       end repeat
       return v
   end tell

end foldl

-- foldr :: (a -> b -> a) -> a -> [b] -> a on foldr(f, startValue, xs)

   tell mReturn(f)
       set v to startValue
       set lng to length of xs
       repeat with i from lng to 1 by -1
           set v to |λ|(v, item i of xs, i, xs)
       end repeat
       return v
   end tell

end foldr

-- head :: [a] -> a on head(xs)

   if length of xs > 0 then
       item 1 of xs
   else
       missing value
   end if

end head

-- intercalate :: Text -> [Text] -> Text on intercalate(strText, lstText)

   set {dlm, my text item delimiters} to {my text item delimiters, strText}
   set strJoined to lstText as text
   set my text item delimiters to dlm
   return strJoined

end intercalate

-- map :: (a -> b) -> [a] -> [b] on map(f, xs)

   tell mReturn(f)
       set lng to length of xs
       set lst to {}
       repeat with i from 1 to lng
           set end of lst to |λ|(item i of xs, i, xs)
       end repeat
       return lst
   end tell

end map

-- Lift 2nd class handler function into 1st class script wrapper -- mReturn :: Handler -> Script on mReturn(f)

   if class of f is script then
       f
   else
       script
           property |λ| : f
       end script
   end if

end mReturn

-- |reverse| :: [a] -> [a] on |reverse|(xs)

   if class of xs is text then
       (reverse of characters of xs) as text
   else
       reverse of xs
   end if

end |reverse|

-- tail :: [a] -> [a] on tail(xs)

   if length of xs > 1 then
       items 2 thru -1 of xs
   else
       {}
   end if

end tail

-- unlines :: [String] -> String on unlines(xs)

   intercalate(linefeed, xs)

end unlines

-- words :: String -> [String] on |words|(s)

   words of s

end |words|</lang>

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

Arturo

<lang rebol>quibble: function [seq][ if? 0=size seq -> "{}" else [ if? 1=size seq -> "{" ++ seq\0 ++ "}" else -> "{" ++ (join.with:", " slice seq 0 (size seq)-1) ++ " and " ++ (last seq) ++ "}" ] ]

sentences: [ [] ["ABC"] ["ABC" "DEF"] ["ABC" "DEF" "G" "H"] ]

loop sentences 'sentence [ print quibble sentence ]</lang>

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

Astro

<lang python>fun quibble(s):

   let result = s.join(' and ').replace(|| and ||, ", ", length(s) - 1)
   return "{ $result }"

let s = [

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

]

for i in s:

   print(quibble i)</lang>

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

BCPL

<lang BCPL>get "libhdr"

// Add a character to the end of a string let addch(s, ch) be $( s%0 := s%0 + 1

   s%(s%0) := ch

$) // Add s2 to the end of s1 and adds(s1, s2) be

   for i = 1 to s2%0 do
       addch(s1, s2%i)
       

// Comma quibbling on strs, which should be a 0-terminated // vector of string pointers. let quibble(strs, buf) = valof $( buf%0 := 0

   addch(buf, '{')
   until !strs = 0 do 
   $(  addch(buf, '"')
       adds(buf, !strs)
       addch(buf, '"')
       unless strs!1 = 0
           test strs!2 = 0
               then adds(buf, " and ")
               else adds(buf, ", ")
       strs := strs + 1
   $)
   addch(buf, '}')
   resultis buf

$)

let start() be $( let words = vec 4

   let buf = vec 63
   
   words!0 := 0
   writef("%S*N", quibble(words, buf))
   
   words!0 := "ABC" ; words!1 := 0
   writef("%S*N", quibble(words, buf))
   
   words!1 := "DEF" ; words!2 := 0
   writef("%S*N", quibble(words, buf))
   
   words!2 := "G" ; words!3 := "H" ; words!4 := 0
   writef("%S*N", quibble(words, buf))

$)</lang>

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

Bracmat

<lang bracmat>( :?L1 & ABC:?L2 & ABC DEF:?L3 & ABC DEF G H:?L4 & L1 L2 L3 L4:?names & ( quibble

 =   w
   .     !arg:%?w (% %:?arg)
       & !w ", " quibble$!arg
     | !arg:%?w %?arg&!w " and " quibble$!arg
     | !arg
 )

& (concat=.str$("{" quibble$!arg "}")) & whl

 ' (!names:%?name ?names&out$(!name concat$!!name))

);</lang>

Output:
L1 {}
L2 {ABC}
L3 {ABC and DEF}
L4 {ABC, DEF, G and H}

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) {

           return
               String.Format("{{{0}}}",
                   String.Join("",
                       input.Reverse().Zip(
                           new [] { "", " and " }.Concat(Enumerable.Repeat(", ", int.MaxValue)),
                           (x, y) => x + y).Reverse()));

}


       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}

Cowgol

<lang cowgol>include "cowgol.coh";

sub quibble(words: uint8,

           length: intptr, 
           buf: [uint8]):
          (out: [uint8]) is
   sub append(s: [uint8]) is
       while [s] != 0 loop
           [buf] := [s];
           buf := @next buf;
           s := @next s;
       end loop;
   end sub;
   
   out := buf;
   
   append("{");
   while length > 0 loop
       append([words]);
       words := @next words;
       case length is
           when 1: break;
           when 2: append(" and ");
           when else: append(", ");
       end case;
       length := length - 1;
   end loop;
   append("}");
   
   [buf] := 0;

end sub;

var w1: [uint8][] := {}; var w2: [uint8][] := {"ABC"}; var w3: [uint8][] := {"ABC","DEF"}; var w4: [uint8][] := {"ABC","DEF","G","H"};

print(quibble(&w1[0], @sizeof w1, LOMEM)); print_nl(); print(quibble(&w2[0], @sizeof w2, LOMEM)); print_nl(); print(quibble(&w3[0], @sizeof w3, LOMEM)); print_nl(); print(quibble(&w4[0], @sizeof w4, LOMEM)); print_nl(); </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}

Delphi

See Pascal.

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

EchoLisp

<lang scheme> (lib 'match)

(define (quibble words)

   (match words
        [ null "{}"]
        [ (a)  (format "{ %a }" a)]
        [ (a b) (format "{ %a and %a }" a b)]
        [( a ... b c) (format "{ %a %a and %a }" (for/string ([w a]) (string-append w ", "))  b c)]
        [else 'bad-input]))

output
(for ([t '(() ("ABC") ("ABC" "DEF") ("ABC" "DEF" "G" "H"))])
   (writeln t '----> (quibble t)))

null ----> "{}" ("ABC") ----> "{ ABC }" ("ABC" "DEF") ----> "{ ABC and DEF }" ("ABC" "DEF" "G" "H") ----> "{ ABC, DEF, G and H }" </lang>

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#

One Way

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

or Another

The Function

<lang fsharp> let quibble quibbler quibblee = Seq.zip quibblee quibbler //Sorry, just too good a line to miss, back in my Latin classes </lang>

The Task

<lang fsharp> let fN n = quibble (List.mapi(fun n _->match n with 0->"" |1-> " and " |_->", ") n |> List.rev) n printf "{"; fN ["ABC"; "DEF"; "G"; "H"] |> Seq.iter(fun(n,g)->printf "%s%s" n g); printfn"}" printf "{"; fN ["ABC"; "DEF"; "G"] |> Seq.iter(fun(n,g)->printf "%s%s" n g); printfn"}" printf "{"; fN ["ABC"; "DEF"] |> Seq.iter(fun(n,g)->printf "%s%s" n g); printfn"}" printf "{"; fN ["ABC"] |> Seq.iter(fun(n,g)->printf "%s%s" n g); printfn"}" printf "{"; fN [] |> Seq.iter(fun(n,g)->printf "%s%s" n g); printfn"}" </lang>

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

Factor

This example uses the inverse vocabulary, which builds on the concept of invertible quotations as the basis for pattern matching. It is discussed at length in this approachable paper. <lang factor>USING: inverse qw sequences ;

(quibble) ( seq -- seq' )
   {
       { [ { } ] [ "" ] }
       { [ 1array ] [ ] }
       { [ 2array ] [ " and " glue ] }
       [ unclip swap (quibble) ", " glue ]
   } switch ;
quibble ( seq -- str ) (quibble) "{%s}" sprintf ;

{ } qw{ ABC } qw{ ABC DEF } qw{ ABC DEF G H } [ quibble print ] 4 napply</lang>

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

Forth

The efficient and beautiful way to solve the task is to keep a sliding triplet of consequent words. First we unconditionally read the first two words; if there are none, "read" gives us an empty word. Then we read the input stream, typing third to last word together with a comma. When the loop ends, we have two (possible empty) words on the stack, and the only thing left to do is to output it accordingly. <lang>

read bl parse ;
not-empty? ( c-addr u -- c-addr u true | false ) ?dup-if true else drop false then ;
third-to-last 2rot ;
second-to-last 2swap ;
quibble

." {" read read begin read not-empty? while third-to-last type ." , " repeat second-to-last not-empty? if type then not-empty? if ." and " type then ." }" cr ;

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

Fortran

The usual problem of "How long is a piece of string?" is answered in the usual way with a declaration that is "surely long enough", at least for anticipated problems. Thus, variable TEXT is declared as 666 characters long. The input statement reads up to that number of characters, or the length of the record if shorter, and supplies trailing spaces to pad the recipient variable to its full length. There is unfortunately no read feature that will create a recipient storage area that matches the size of the record being read. There is such a facility in pl/i, except that the recipient variable still has a pre-specified upper bound to its size.

Subroutine QUIBBLE doesn't have to worry about this because it works with TEXT as a parameter, whatever its size (various integer limits apply) however, it too has the same problem because it locates the start and end positions of each word, and, how many words are going to be found? So once again, the arrays are made "surely large enough" for the expected class of problem. The first stage is to locate the words separated by any amount of "white space", which, thanks to the inability to rely on the evaluation of compound boolean expressions (of the form IF (in bounds & Array indexing)) in the "shortcut" manner, employs a battery of IF-statements. Fortran does not offer a data type "list of ..." so there is no prospect of placing the words into such an entity then inserting commas and "and" elements into the list to taste. Instead, the list of words is represented by a sequence of values in ordinary arrays.

The source style is Fortran 77, thus the use of COMMON to pass some I/O unit numbers. The plan initially was to engage in trickery with the variable FORMAT features, of the form <expression>(blah blah) to signify some number of repetitions of (blah blah), which number might be zero, but alas, although <0>X works, it proved not to work for grouped items in place of a format code. So the <..> extension had to be abandoned, and plainer F77 results.<lang Fortran> SUBROUTINE QUIBBLE(TEXT,OXFORDIAN) !Punctuates a list with commas and stuff.

      CHARACTER*(*) TEXT	!The text, delimited by spaces.
      LOGICAL OXFORDIAN	!Just so.
      INTEGER IST(6),LST(6)	!Start and stop positions.
      INTEGER N,L,I		!Counters.
      INTEGER L1,L2		!Fingers for the scan.
      INTEGER MSG		!Output unit.
      COMMON /IODEV/MSG	!Share.

Chop the text into words.

       N = 0		!No words found.
       L = LEN(TEXT)	!Multiple trailing spaces - no worries.
       L2 = 0		!Syncopation: where the previous chomp ended.
  10   L1 = L2		!Thus, where a fresh scan should follow.
  11   L1 = L1 + 1		!Advance one.
       IF (L1.GT.L) GO TO 20		!Finished yet?
       IF (TEXT(L1:L1).LE." ") GO TO 11	!No. Skip leading spaces.
       L2 = L1			!Righto, L1 is the first non-blank.
  12   L2 = L2 + 1		!Scan through the non-blanks.
       IF (L2.GT.L) GO TO 13	!Is it safe to look?
       IF (TEXT(L2:L2).GT." ") GO TO 12	!Yes. Speed through non-blanks.
  13   N = N + 1			!Righto, a word is found in TEXT(L1:L2 - 1)
       IST(N) = L1		!So, recall its first character.
       LST(N) = L2 - 1		!And its last.
       IF (L2.LT.L) GO TO 10	!Perhaps more text follows.

Comma time...

  20   WRITE (MSG,21) "{"	!Start the output.
  21   FORMAT (A,$)		!The $, obviously, specifies that the line is not finished.
       DO I = 1,N		!Step through the texts, there possibly being none.
         IF (I.GT.1) THEN		!If there has been a predecessor, supply separators.
           IF (I.LT.N) THEN			!Up to the last two, it's easy.
             WRITE (MSG,21) ", "			!Always just a comma.
           ELSE IF (OXFORDIAN) THEN		!But after the penultimate item, what?
             WRITE (MSG,21) ", and "			!Supply the comma omitted above: a double-power separator.
           ELSE				!One fewer comma, with possible ambiguity arising.
             WRITE (MSG,21) " and "			!A single separator.
           END IF				!So much for the style.
         END IF			!Enough with the separation.
         WRITE (MSG,21) TEXT(IST(I):LST(I))	!The text at last!
       END DO			!On to the next text.
       WRITE (MSG,"('}')")	!End the line, marking the end of the text.
     END		!That was fun.
     PROGRAM ENCOMMA	!Punctuate a list with commas.
     CHARACTER*(666) TEXT	!Holds the text. Easily long enough.
     INTEGER KBD,MSG,INF	!Now for some messing.
     COMMON /IODEV/MSG,KBD	!Pass the word.
     KBD = 5	!Standard input.
     MSG = 6	!Standard output.
     INF = 10	!Suitable for a disc file.
     OPEN (INF,FILE="List.txt",ACTION = "READ")	!Attach one.
  10 WRITE (MSG,11) "To insert commas into lists..."	!Announce.
  11 FORMAT (A)			!Just the text.
  12 READ (INF,11,END = 20) TEXT	!Grab the text, with trailing spaces to fill out TEXT.
     CALL QUIBBLE(TEXT,.FALSE.)	!One way to quibble.
     GO TO 12				!Try for another.
  20 REWIND (INF)			!Back to the start of the file.
     WRITE (MSG,11)			!Set off a bit.
     WRITE (MSG,11) "Oxford style..."	!Announce the proper style.
  21 READ (INF,11,END = 30) TEXT	!Grab the text.
     CALL QUIBBLE(TEXT,.TRUE.)		!The other way to quibble.
     GO TO 21				!Have another try.

Closedown

  30 END	!All files are closed by exiting.</lang>

Output:

To insert commas into lists...
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

Oxford style...
{}
{ABC}
{ABC, and DEF}
{ABC, DEF, G, and H}

FreeBASIC

<lang freebasic> ' FB 1.05.0 Win64

Sub Split(s As String, sep As String, result() As String)

 Dim As Integer i, j, count = 0
 Dim temp As String
 Dim As Integer position(Len(s) + 1)
 position(0) = 0
 For i = 0 To Len(s) - 1
   For j = 0 To Len(sep) - 1
     If s[i] = sep[j] Then 
       count += 1
       position(count) = i + 1 
     End If
   Next j
 Next i
 position(count + 1) = Len(s) + 1
 Redim result(count)
 For i = 1 To count + 1    
   result(i - 1) = Mid(s, position(i - 1) + 1, position(i) - position(i - 1) - 1)
 Next

End Sub

Function CommaQuibble(s As String) As String

 Dim i As Integer
 Dim As String result
 Dim As String words() 
 s = Trim(s, Any "[]""") 
 ' Now remove internal quotes
 Split s, """", words()
 s = ""
 For i = 0 To UBound(words)
    s &= words(i)
 Next
 ' Now split 's' using the comma as separator
 Erase words  
 Split s, ",", words()
 ' And re-assemble the string in the desired format
 result = "{"
 For i = 0 To UBound(words) 
   If i = 0 Then
     result &= words(i)
   ElseIf i = UBound(words) Then 
     result &= " and " & words(i)       
   Else
     result &= ", " + words(i)
   EndIf   
 Next
 Return result & "}"

End Function

' As 3 of the strings contain embedded quotes these need to be doubled in FB Print CommaQuibble("[]") Print CommaQuibble("[""ABC""]") Print CommaQuibble("[""ABC"",""DEF""]") Print CommaQuibble("[""ABC"",""DEF"",""G"",""H""]") Print Print "Press any key to quit the program" Sleep </lang>

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

Gambas

Click this link to run this code <lang gambas>Public Sub Main() Dim sInput As String[] = ["", "ABC", "ABC DEF", "ABC DEF G H"] Dim sTemp As String

For Each sTemp In sInput

 Print sTemp & " = ";
 sTemp = Replace(sTemp, " ", ",")
 If RInStr(sTemp, ",") > 0 Then 
   sTemp = Mid(sTemp, 1, RInStr(sTemp, ",") - 1) & " and " & Mid(sTemp, RInStr(sTemp, ",") + 1)
 End If
 sTemp = "{" & sTemp & "}"
 Print sTemp

Next

End</lang> Output:

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

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}

Or, defining just two cases, and drawing more on standard libraries than on hand-crafted pattern-matching and recursion: <lang Haskell>import Data.List (intercalate)

quibble :: [String] -> String quibble ws

 | length ws > 1 =
   intercalate
     " and "
     ([intercalate ", " . reverse . tail, head] <*> [reverse ws])
 | otherwise = concat ws

main :: IO () main =

 mapM_ (putStrLn . (`intercalate` ["{", "}"]) . quibble) $
 [[], ["ABC"], ["ABC", "DEF"], ["ABC", "DEF", "G", "H"]] ++
 (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

ES5

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

ES6

Translation of: Haskell

Composing from a set of generic functions: <lang JavaScript>(() => {

   'use strict';
   // ----------------- COMMA QUIBBLING -----------------
   // quibble :: [String] -> String
   const quibble = xs =>
       1 < xs.length ? (
           intercalate(' and ')(
               ap([
                   compose(
                       intercalate(', '), 
                       reverse, 
                       tail
                   ),
                   head
               ])([reverse(xs)])
           )
       ) : concat(xs);


   // ---------------------- TEST -----------------------
   const main = () =>
       unlines(
           map(compose(x => '{' + x + '}', quibble))(
               append([
                   [],
                   ["ABC"],
                   ["ABC", "DEF"],
                   ["ABC", "DEF", "G", "H"]
               ])(
                   map(words)([
                       "One two three four",
                       "Me myself I",
                       "Jack Jill",
                       "Loner"
                   ])
               )
           ));


   // ---------------- GENERIC FUNCTIONS ----------------
   // ap (<*>) :: [(a -> b)] -> [a] -> [b]
   const ap = fs =>
       // The sequential application of each of a list
       // of functions to each of a list of values.
       // apList([x => 2 * x, x => 20 + x])([1, 2, 3])
       //     -> [2, 4, 6, 21, 22, 23]
       xs => fs.flatMap(f => xs.map(f));


   // append (++) :: [a] -> [a] -> [a]
   const append = xs =>
       // A list defined by the
       // concatenation of two others.
       ys => xs.concat(ys);


   // compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
   const compose = (...fs) =>
       // A function defined by the right-to-left
       // composition of all the functions in fs.
       fs.reduce(
           (f, g) => x => f(g(x)),
           x => x
       );


   // concat :: a -> [a]
   // concat :: [String] -> String
   const concat = xs => (
       ys => 0 < ys.length ? (
           ys.every(Array.isArray) ? (
               []
           ) : 
       ).concat(...ys) : ys
   )(xs);


   // head :: [a] -> a
   const head = xs => (
       ys => ys.length ? (
           ys[0]
       ) : undefined
   )(list(xs));


   // intercalate :: String -> [String] -> String
   const intercalate = s =>
       // The concatenation of xs
       // interspersed with copies of s.
       xs => xs.join(s);


   // list :: StringOrArrayLike b => b -> [a]
   const list = xs =>
       // xs itself, if it is an Array,
       // or an Array derived from xs.
       Array.isArray(xs) ? (
           xs
       ) : Array.from(xs || []);


   // map :: (a -> b) -> [a] -> [b]
   const map = f =>
       // The list obtained by applying f
       // to each element of xs.
       // (The image of xs under f).
       xs => [...xs].map(f);


   // reverse :: [a] -> [a]
   const reverse = xs =>
       'string' !== typeof xs ? (
           xs.slice(0).reverse()
       ) : xs.split().reverse().join();


   // tail :: [a] -> [a]
   const tail = xs =>
       // A new list consisting of all
       // items of xs except the first.
       xs.slice(1);


   // unlines :: [String] -> String
   const unlines = xs =>
       // A single string formed by the intercalation
       // of a list of strings with the newline character.
       xs.join('\n');


   // words :: String -> [String]
   const words = s =>
       // List of space-delimited sub-strings.
       s.split(/\s+/);
   // MAIN ---
   return main();

})();</lang>

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

Alternative implementation: <lang JavaScript> function quibble(words) {

 var words2 = words.join()
 var words3 = [...words2].reverse().join();
 var res = words3.replace(",", " dna ");
 var words4 = [...res].reverse().join();
 return '{'+words4+'}';

} </lang>

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

Works with: Julia version 0.6

<lang Julia>function quibble(arr::Array)

   if isempty(arr) rst = "" else rst = "$(arr[end])" end
   if length(arr) > 1 rst = join(arr[1:end-1], ", ") * " and " * rst end
   return "{" * rst * "}"

end

@show quibble([]) @show quibble(["ABC"]) @show quibble(["ABC", "DEF"]) @show quibble(["ABC", "DEF", "G", "H"])</lang>

Output:
quibble([]) = "{}"
quibble(["ABC"]) = "{ABC}"
quibble(["ABC", "DEF"]) = "{ABC and DEF}"
quibble(["ABC", "DEF", "G", "H"]) = "{ABC, DEF, G and H}"

Kotlin

<lang scala>// version 1.0.6

fun commaQuibble(s: String): String {

   val t = s.trim('[', ']').replace(" ", "").replace("\"", "") 
   val words = t.split(',')
   val sb = StringBuilder("{")
   for (i in 0 until words.size) {
       sb.append(when (i) {
           0                -> ""
           words.lastIndex  -> " and "
           else             -> ", "    
       })
       sb.append(words[i])
   }
   return sb.append("}").toString()

}

fun main(args: Array<String>) {

   val inputs = arrayOf(
       """[]""",
       """["ABC"]""",
       """["ABC", "DEF"]""",
       """["ABC", "DEF", "G", "H"]"""
   )
   for (input in inputs) println("${input.padEnd(24)}  ->  ${commaQuibble(input)}")

}</lang>

Output:
[]                        ->  {}
["ABC"]                   ->  {ABC}
["ABC", "DEF"]            ->  {ABC and DEF}
["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}

Lua

<lang Lua>function quibble (strTab)

   local outString, join = "{"
   for strNum = 1, #strTab do
       if strNum == #strTab then
           join = ""
       elseif strNum == #strTab - 1 then
           join = " and " 
       else
           join = ", "
       end
       outString = outString .. strTab[strNum] .. join
   end
   return outString .. '}'

end

local testCases = {

   {},
   {"ABC"},
   {"ABC", "DEF"},
   {"ABC", "DEF", "G", "H"}

} for _, input in pairs(testCases) do print(quibble(input)) end</lang>

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

M2000 Interpreter

Using string as argument

<lang M2000 Interpreter> Module Checkit {

     function f$ {
           what$=mid$(trim$(letter$),2)
           what$=Left$(what$, len(what$)-1)
           flush ' erase any argument from stack
           Data param$(what$)
           m=stack.size
           document resp$="{" 
            if m>2 then {
                 shift m-1, 2    ' get last two as first two
                 push letter$+" and "+letter$
                 m--   ' one less
                 shiftback m   ' move to last position
           }
           while not empty {
                  resp$=letter$+if$(not empty->", ", "")
            }
           =resp$+"}"   
      
     }
     \\ we use ? for Print
     ? f$({[]})
     ? f$({["ABC"]})
     ? f$({["ABC", "DEF"]})
     ? f$({["ABC","DEF", "G", "H"]})

} Checkit </lang>

Using String functions only

<lang M2000 Interpreter> Module Checkit {

     function f$ {
           what$=filter$(trim$(letter$), chr$(34)) 
           what$=Mid$(what$, 2, len(what$)-2)
           count=Len(what$)-Len(filter$(what$,","))
           if count>2 then m=rinstr(what$, ", ")  :  insert m, 2 what$=" and " 
           ="{"+what$+"}"
     }
     ? f$({[]})
     ? f$({["ABC"]})
     ? f$({["ABC", "DEF"]})
     ? f$({["ABC","DEF", "G", "H"]})

} Checkit </lang>

Using array as argument

<lang M2000 Interpreter> Module Checkit {

     function f$(ar) {
           flush
           Data ! ar
           m=stack.size
           document resp$="{" 
            if m>2 then {
                 shift m-1, 2    ' get last two as first two
                 push letter$+" and "+letter$
                 m--   ' one less
                 shiftback m   ' move to last position
           }
           while not empty {
                  resp$=letter$+if$(not empty->", ", "")
            }
           =resp$+"}"   
     }
     ? f$((,))
     ? f$(("ABC",))
     ? f$(("ABC", "DEF"))
     ? f$(("ABC","DEF", "G", "H"))

} Checkit </lang>

Output:
{}
{ABC}
{ABC, 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 / Wolfram Language

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

MATLAB / Octave

<lang Matlab> function r = comma_quibbling(varargin) if isempty(varargin) r = ; elseif length(varargin)==1; r = varargin{1}; else r = [varargin{end-1},' and ', varargin{end}]; for k=length(varargin)-2:-1:1, r = [varargin{k}, ', ', r]; end end end; </lang>


Output:
octave:73> comma_quibbling('')
ans = 
octave:74> comma_quibbling('ABC')
ans = ABC
octave:75> comma_quibbling('ABC','DEF')
ans = ABC and DEF
octave:76> comma_quibbling('ABC','DEF','G')
ans = ABC, DEF and G
octave:77> comma_quibbling('ABC','DEF','G','H')
ans = 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: openArray[string]): 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>

Oberon-2

Works with: oo2c

<lang oberon2> MODULE CommaQuibbling; IMPORT

 NPCT:Args,
 Strings,
 Out;
 

VAR

 str: ARRAY 256 OF CHAR;
 PROCEDURE Do(VAR s: ARRAY OF CHAR);
 VAR
   aux: ARRAY 128 OF CHAR;
   i,params: LONGINT;
 BEGIN
   params := Args.Number() - 1;
   CASE params OF
      0: 
       COPY("{}",s)
     |1:
       Args.At(1,aux);
       Strings.Append("{",s);
       Strings.Append(aux,s);
       Strings.Append("}",s); 
     ELSE
       Strings.Append("{",s);
       FOR i := 1 TO params - 1 DO
         Args.At(i,aux);
         Strings.Append(aux,s);
         IF i # params - 1 THEN 
           Strings.Append(", ",s)
         ELSE 
           Strings.Append(" and ", s) 
         END
       END;
       Args.At(params,aux);
       Strings.Append(aux,s);
       Strings.Append("}",s)
   END;
   
 END Do;
 

BEGIN

 Do(str);
 Out.String(":> ");Out.String(str);Out.Ln

END CommaQuibbling. </lang>

Output:
$ bin/CommaQuibbling 
:> {}
$ bin/CommaQuibbling ABC
:> {ABC}
$ bin/CommaQuibbling ABC DEF
:> {ABC and DEF}
$ bin/CommaQuibbling ABC DEF G
:> {ABC, DEF and G}
$ bin/CommaQuibbling ABC DEF G H
:> {ABC, DEF, G and H}

Objeck

<lang objeck>class Quibbler {

 function : Quibble(words : String[]) ~ String {
   text := "{";
   
   each(i : words) {
     text += words[i];
     if(i < words->Size() - 2) {
       text += ", ";
     }
     else if(i = words->Size() - 2) {
       text += " and ";
     };
   };
   text += "}";
   return text;
 }

 function : Main(args : String[]) ~ Nil {
   words := String->New[0];
   Quibble(words)->PrintLine();
   words := ["ABC"];
   Quibble(words)->PrintLine();
   words := ["ABC", "DEF"];
   Quibble(words)->PrintLine();
   words := ["ABC", "DEF", "G", "H"];
   Quibble(words)->PrintLine();
 }

}</lang>

Output:

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

OCaml

<lang ocaml>open Printf

let quibble list =

 let rec aux = function
   | a :: b :: c :: d :: rest -> a ^ ", " ^ aux (b :: c :: d :: rest)
   | [a; b; c] -> sprintf "%s, %s and %s}" a b c
   | [a; b] -> sprintf "%s and %s}" a b
   | [a] -> sprintf "%s}" a
   | [] -> "}" in
 "{" ^ aux list

let test () =

 [[];
  ["ABC"];
  ["ABC"; "DEF"];
  ["ABC"; "DEF"; "G"; "H"]]
 |> List.iter (fun list -> print_endline (quibble list))</lang>
Works with: Core version v0.9.116.03+91

<lang ocaml>open Core

let quibble = function

 | [| |] -> "{}"
 | [| a |] -> sprintf "{%s}" a
 | array ->
   let last, rest = Array.last array, Array.slice array 0 (-1) in
   sprintf "{%s and %s}" (String.concat_array ~sep:", " rest) last

let test () =

 [[||];
  [|"ABC"|];
  [|"ABC"; "DEF"|];
  [|"ABC"; "DEF"; "G"; "H"|]]
 |> List.iter ~f:(fun list -> print_endline (quibble list))</lang>
Output:
# test ();;
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

Oforth

<lang Oforth>: quibbing(l) -- string | 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) .
[{}, {ABC}, {ABC and DEF}, {ABC, DEF, G and H}]

Ol

<lang scheme> (define (quibble . args)

  (display "{")
  (let loop ((args args))
     (unless (null? args) (begin
        (display (car args))
        (cond
           ((= 1 (length args)) #t)
           ((= 2 (length args))
              (display " and "))
           (else
              (display ", ")))
        (loop (cdr args)))))
  (print "}"))
testing =>

(quibble) (quibble "ABC") (quibble "ABC" "DEF") (quibble "ABC" "DEF" "G" "H") </lang>

Output:
{}
{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}"

Pascal

<Lang Pascal> program CommaQuibbling;

uses

 SysUtils,
 Classes,
 StrUtils;

const

 OuterBracket =['[', ']'];

type

{$IFNDEF FPC}

 SizeInt = LongInt;

{$ENDIF}


 { TCommaQuibble }
 TCommaQuibble = class(TStringList)
 private
   function GetCommaquibble: string;
   procedure SetCommaQuibble(AValue: string);
 public
   property CommaQuibble: string read GetCommaquibble write SetCommaQuibble;
 end;

{$IFNDEF FPC} // Delphi support

function WordPosition(const N: Integer; const S: string; const WordDelims:

 TSysCharSet): SizeInt;

var

 PS, P, PE: PChar;
 Count: Integer;

begin

 Result := 0;
 Count := 0;
 PS := PChar(pointer(S));
 PE := PS + Length(S);
 P := PS;
 while (P < PE) and (Count <> N) do
 begin
   while (P < PE) and (P^ in WordDelims) do
     inc(P);
   if (P < PE) then
     inc(Count);
   if (Count <> N) then
     while (P < PE) and not (P^ in WordDelims) do
       inc(P)
   else
     Result := (P - PS) + 1;
 end;

end;

function ExtractWordPos(N: Integer; const S: string; const WordDelims:

 TSysCharSet; out Pos: Integer): string;

var

 i, j, l: SizeInt;

begin

 j := 0;
 i := WordPosition(N, S, WordDelims);
 if (i > High(Integer)) then
 begin
   Result := ;
   Pos := -1;
   Exit;
 end;
 Pos := i;
 if (i <> 0) then
 begin
   j := i;
   l := Length(S);
   while (j <= l) and not (S[j] in WordDelims) do
     inc(j);
 end;
 SetLength(Result, j - i);
 if ((j - i) > 0) then
   Result := copy(S, i, j - i);

end;

function ExtractWord(N: Integer; const S: string; const WordDelims: TSysCharSet):

 string; inline;

var

 i: SizeInt;

begin

 Result := ExtractWordPos(N, S, WordDelims, i);

end; {$ENDIF}

{ TCommaQuibble }

procedure TCommaQuibble.SetCommaQuibble(AValue: string); begin

 AValue := ExtractWord(1, AValue, OuterBracket);
 commatext := AValue;

end;

function TCommaQuibble.GetCommaquibble: string; var

 x: Integer;
 Del: string;

begin

 result := ;
 Del := ', ';
 for x := 0 to Count - 1 do
 begin
   result := result + Strings[x];
   if x = Count - 2 then
     Del := ' and '
   else if x = Count - 1 then
     Del := ;
   result := result + Del;
 end;
 result := '{' + result + '}';

end;

const

 TestData: array[0..7] of string = ('[]', '["ABC"]', '["ABC", "DEF"]',
   '["ABC", "DEF", "G", "H"]', , '"ABC"', '"ABC", "DEF"', '"ABC", "DEF", "G", "H"');

var

 Quibble: TCommaQuibble;
 TestString: string;

begin

 Quibble := TCommaQuibble.Create;
 for TestString in TestData do
 begin
   Quibble.CommaQuibble := TestString;
   writeln(Quibble.CommaQuibble);
 end;

end.</Lang> Output:

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

Perl

Translation of: Raku

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

Phix

function quibble(sequence words)
    if length(words)>=2 then
        words[-2..-1] = {words[-2]&" and "&words[-1]}
    end if
    return "{"&join(words,", ")&"}"
end function
 
constant tests = {{},
                  {"ABC"},
                  {"ABC","DEF"},
                  {"ABC","DEF","G","H"}}
 
for i=1 to length(tests) do
    ?quibble(tests[i])
end for
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}      

PL/M

<lang plm>100H:

/* COPY A STRING (MINUS TERMINATOR), RETURNS LENGTH (MINUS TERMINATOR) */ COPY$STR: PROCEDURE(SRC, DST) ADDRESS;

   DECLARE (SRC, DST) ADDRESS;
   DECLARE (SCH BASED SRC, DCH BASED DST) BYTE;
   DECLARE L ADDRESS;
   L = 0;
   DO WHILE SCH <> '$';
       DCH = SCH;
       SRC = SRC + 1;
       DST = DST + 1;
       L = L + 1;
   END;
   RETURN L;

END COPY$STR;

/* QUIBBLE GIVEN ARRAY OF $-TERMINATED STRINGS, STORE RESULT IN BUFR */ QUIBBLE: PROCEDURE(WORDS, BUFR) ADDRESS;

   DECLARE (WORDS, BUFR, ADR) ADDRESS;
   DECLARE (WORD BASED WORDS, WPTR) ADDRESS;
   DECLARE (WCHAR BASED WPTR, BCHAR BASED BUFR) BYTE;
   
   /* BRACES AND LOWERCASE LETTERS ARE NOT WITHIN PL/M CHARSET */
   DECLARE LBRACE LITERALLY '123', RBRACE LITERALLY '125';
   DECLARE ANDSTR DATA (32,97,110,100,32,'$');
   ADR = BUFR;
   BCHAR = LBRACE; 
   BUFR = BUFR + 1;
   DO WHILE WORD <> 0;
       BUFR = BUFR + COPY$STR(WORD, BUFR);
       WORDS = WORDS + 2;
       IF WORD <> 0 THEN
           IF WORD(1) <> 0 THEN
               BUFR = BUFR + COPY$STR(.', $', BUFR);
           ELSE
               BUFR = BUFR + COPY$STR(.ANDSTR, BUFR);
   END;
   BCHAR = RBRACE;
   BUFR = BUFR + 1;
   BCHAR = '$';
   RETURN ADR;

END QUIBBLE;

/* --- CP/M OUTPUT AND TESTING --- */ BDOS: PROCEDURE(FUNC, ARG); /* MAKE CP/M SYSTEM CALL */

   DECLARE FUNC BYTE, ARG ADDRESS;
   GO TO 5;

END BDOS;

DECLARE BDOS$EXIT LITERALLY '0', /* EXIT TO CP/M */

       BDOS$PUTS LITERALLY '9';  /* PRINT STRING */

PUTS: PROCEDURE(S);

   DECLARE S ADDRESS;
   CALL BDOS(BDOS$PUTS, S);
   CALL BDOS(BDOS$PUTS, .(13,10,'$'));

END PUTS;

/* ARRAY WITH INITIALLY NO CONTENTS */ DECLARE ARR (5) ADDRESS INITIAL (0,0,0,0,0);

CALL PUTS(QUIBBLE(.ARR, .MEMORY)); /* NO STRINGS */ ARR(0) = .'ABC$'; CALL PUTS(QUIBBLE(.ARR, .MEMORY)); /* ABC */ ARR(1) = .'DEF$'; CALL PUTS(QUIBBLE(.ARR, .MEMORY)); /* ABC AND DEF */ ARR(2) = .'G$'; ARR(3) = .'H$'; CALL PUTS(QUIBBLE(.ARR, .MEMORY)); /* ABC, DEF, G AND H */

CALL BDOS(BDOS$EXIT, 0); EOF</lang>

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

Plain English

<lang plainenglish>To quibble four words: Add "ABC" to some string things. Add "DEF" to the string things. Add "G" to the string things. Add "H" to the string things. Quibble the string things.

To quibble one word: Add "ABC" to some string things. Quibble the string things.

To quibble some string things: Quibble the string things giving a string. Destroy the string things. Write the string on the console.

To quibble some string things giving a string: Append "{" to the string. Put the string things' count into a count. If the count is 0, append "}" to the string; exit. Get a string thing from the string things. If the count is 1, append the string thing's string then "}" to the string; exit. Loop. If a counter is past the count minus 2, append the string thing's string then " and " then the string thing's next's string then "}" to the string; exit. Append the string thing's string then ", " to the string. Put the string thing's next into the string thing. Repeat.

To quibble two words: Add "ABC" to some string things. Add "DEF" to the string things. Quibble the string things.

To quibble zero words: Quibble some string things.

To run: Start up. Quibble zero words. Quibble one word. Quibble two words. Quibble four words. Wait for the escape key. Shut down.</lang>

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

PowerShell

<lang PowerShell> function Out-Quibble {

   [OutputType([string])]
   Param
   (
       # Zero or more strings.
       [Parameter(Mandatory=$false, Position=0)]
       [AllowEmptyString()]
       [string[]]
       $Text = ""
   )
   # If not null or empty...
   if ($Text)
   {
       # Remove empty strings from the array.
       $text = "$Text".Split(" ", [StringSplitOptions]::RemoveEmptyEntries)
   }
   else
   {
       return "{}"
   }
   # Build a format string.
   $outStr = ""
   for ($i = 0; $i -lt $text.Count; $i++)
   { 
       $outStr += "{$i}, "
   }
   $outStr = $outStr.TrimEnd(", ")
   # If more than one word, insert " and" at last comma position.
   if ($text.Count -gt 1)
   {
       $cIndex = $outStr.LastIndexOf(",")
       $outStr = $outStr.Remove($cIndex,1).Insert($cIndex," and")
   }
   # Output the formatted string.
   "{" + $outStr -f $text + "}"

} </lang> <lang PowerShell> Out-Quibble Out-Quibble "ABC" Out-Quibble "ABC", "DEF" Out-Quibble "ABC", "DEF", "G", "H" </lang>

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

What it might look like when working with a file: <lang PowerShell> $file = @'

ABC ABC, DEF ABC, DEF, G, H '@ -split [Environment]::NewLine

foreach ($line in $file) {

   Out-Quibble -Text ($line -split ", ")

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

PureBasic

<lang PureBasic> EnableExplicit

Procedure.s CommaQuibble(Input$)

 Protected i, count
 Protected result$, word$
 Input$ = RemoveString(Input$, "[")
 Input$ = RemoveString(Input$, "]")
 Input$ = RemoveString(Input$, #DQUOTE$)
 count = CountString(Input$, ",") + 1
 result$ = "{"
 For i = 1 To count
   word$ = StringField(Input$, i, ",")
   If i = 1
     result$ + word$
   ElseIf Count = i
     result$ + " and " + word$      
   Else
     result$ + ", " + word$
   EndIf
 Next
 ProcedureReturn result$ + "}"

EndProcedure

If OpenConsole()

 ; As 3 of the strings contain embedded quotes these need to be escaped with '\' and the whole string preceded by '~'
 PrintN(CommaQuibble("[]"))
 PrintN(CommaQuibble(~"[\"ABC\"]"))
 PrintN(CommaQuibble(~"[\"ABC\",\"DEF\"]"))
 PrintN(CommaQuibble(~"[\"ABC\",\"DEF\",\"G\",\"H\"]"))
 PrintN("")
 PrintN("Press any key to close the console")
 Repeat: Delay(10) : Until Inkey() <> ""
 CloseConsole()

EndIf </lang>

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

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>

Quackery

<lang quackery>[ swap join join ] is glue ( [ [ [ --> [ )

[ [ dup size

   dup 0 = iff
     [ 2drop [] ] done
   dup 1 = iff
     [ drop unpack ] done
   2 = iff
     [ unpack $ ' and ' glue ] done
   behead swap recurse $ ', ' glue ]
 $ '{' swap join $ '}' join ]         is quibble ( [     --> $ )

[] quibble echo$ cr $ 'ABC' nest$ quibble echo$ cr $ 'ABC DEF' nest$ quibble echo$ cr $ 'ABC DEF G H' nest$ quibble echo$</lang>

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

R

<lang r>quib <- function(vect) {

 #The task does not consider empty strings to be words, so we remove them immediately.
 #We could also remove non-upper-case characters, but the tasks gives off the impression that the user will do that.
 vect <- vect[!nchar(vect) == 0]
 len <- length(vect)
 if(len == 0) return("{}")
 result <- switch(len,
                  paste0("{", vect, "}"),
                  paste0("{", vect[1], " and ", vect[2], "}"))
 #R's switch for numerics doesn't support default cases, so this next line cannot go in the switch.
 if(is.null(result)) paste0("{", paste0(vect[seq_len(len-1)], collapse = ", "), " and ", vect[len], "}") else result

} quib(character(0)) #R has several types of empty string, e.g. character(0), "", and c("", "", ""). quib("") quib(" ") quib(c("", "")) quib(rep("", 10)) quib("ABC") quib(c("ABC", "")) quib(c("ABC", "DEF")) quib(c("ABC", "DEF", "G", "H")) quib(c("ABC", "DEF", "G", "H", "I", "J", ""))</lang>

Output:
> quib(character(0))
[1] "{}"
> quib("")
[1] "{}"
> quib(" ")
[1] "{ }"
> quib(c("", ""))
[1] "{}"
> quib(rep("", 10))
[1] "{}"
> quib("ABC")
[1] "{ABC}"
> quib(c("ABC", ""))
[1] "{ABC}"
> quib(c("ABC", "DEF"))
[1] "{ABC and DEF}"
> quib(c("ABC", "DEF", "G", "H"))
[1] "{ABC, DEF, G and H}"
> quib(c("ABC", "DEF", "G", "H", "I", "J", ""))
[1] "{ABC, DEF, G, H, I and J}"

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}

Raku

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

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}

Ring

<lang ring>

  1. Project : Comma Quibbling

text = list(4) text[1] = "{}" text[2] = "ABC" text[3] = "ABC,DEF" text[4] = "ABC,DEF,G,H" comma(text)

func comma(text)

      listtext = []
      for n = 1 to 4
           listtext = str2list(substr(text[n], ",", nl))
           if n = 2
              see "{" + list2str(listtext) + "}" + nl
              loop
           ok
           if len(listtext) = 1
              see "{}" + nl
              loop
           ok
           str = "{"
           for m = 1 to len(listtext)-1
               if len(listtext) = 2
                  str = str + listtext[m] + " "
               else
                  str = str + listtext[m] + ", "
               ok
           next
           if len(listtext) = 2
              str = left(str, len(str)-1)
           else
              str = left(str, len(str)-2)
           ok
           if len(listtext) = 2
              str = str + " " + listtext[len(listtext)] + "}"
           else
              str = str + " and " + listtext[len(listtext)] + "}"
           ok
           see str + nl
    next

</lang> Output:

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

Ruby

Translation of: Raku

<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> fn quibble(seq: &[&str]) -> String {

   match seq.len() {
       0 => "{}".to_string(),
       1 => format!("{{{}}}", seq[0]),
       _ => {
           format!("{{{} and {}}}",
                   seq[..seq.len() - 1].join(", "),
                   seq.last().unwrap())
       }
   }

}

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}

Scheme

<lang scheme> (define (quibble . args)

 (display "{")
 (do ((rem args (cdr rem)))
   ((null? rem) (display "}\n"))
   (display (car rem))
   (cond ((= 1 (length rem)) )
         ((= 2 (length rem))
          (display " and "))
         (else
           (display ", ")))))

(quibble) (quibble "ABC") (quibble "ABC" "DEF") (quibble "ABC" "DEF" "G" "H") </lang>

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

Sed

script-file: <lang sed>s/#.*$//g y/[/{/ y/]/}/ s/"//g s/ [A-Z][A-Z]*}/ and&/g s/, and/ and/</lang> test.txt: <lang text>[] # (No input words). ["ABC"] ["ABC", "DEF"] ["ABC", "DEF", "G", "H"]</lang> sed -f script-file test.txt

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}

SenseTalk

<lang sensetalk>Quibble [] // (No input words). Quibble ["ABC"] Quibble ["ABC", "DEF"] Quibble ["ABC", "DEF", "G", "H"]

to Quibble with wordList if the number of items in wordList is ... ... 0 then put "{}" ... 1 then put "{" & item 1 of wordList & "}" ... else put "{" & (items first to penultimate of wordList) joined by ", " & " and " & the last item of wordlist & "}" end if end Quibble </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}

TXR

<lang txrlisp>(defun quib (list)

 (tree-bind (: last . lead) (reverse list)
   `{@{(nreverse lead) ", "}@(if lead " and ")@last}`))</lang>

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}

VBA

<lang vb>Option Explicit

Sub Main()

  Debug.Print Quibbling("")
  Debug.Print Quibbling("ABC")
  Debug.Print Quibbling("ABC, DEF")
  Debug.Print Quibbling("ABC, DEF, G, H")
  Debug.Print Quibbling("ABC, DEF, G, H, IJKLM, NO, PQRSTUV")

End Sub

Private Function Quibbling(MyString As String) As String Dim s As String, n As Integer

  s = "{" & MyString & "}": n = InStrRev(s, ",")
  If n > 0 Then s = Left(s, n - 1) & " and " & Right(s, Len(s) - (n + 1))
  Quibbling = s

End Function</lang>

Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}
{ABC, DEF, G, H, IJKLM, NO and PQRSTUV}

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}

Visual Basic .NET

FormatEnumerable() accepts an IEnumerable(Of String), as per Lippert's original specification. FormatArray() contains an alternative implementation for String().

<lang vbnet>Option Explicit On Option Infer On Option Strict On

Module Program

   Function FormatEnumerable(source As IEnumerable(Of String)) As String
       Dim res As New Text.StringBuilder("{")
       Using en = source.GetEnumerator()
           Dim moreThanOne As Boolean = False
           Dim nxt = If(en.MoveNext(), en.Current, String.Empty)
           Do While en.MoveNext()
               If moreThanOne Then res.Append(", ")
               moreThanOne = True
               res.Append(nxt)
               nxt = en.Current
           Loop
           Dim lastItem = If(moreThanOne, " and ", "") & nxt
           Return res.ToString() & lastItem & "}"
       End Using
   End Function
   Function FormatArray(source As String()) As String
       Select Case source.Length
           Case 0 : Return "{}"
           Case 1 : Return "{" & source(0) & "}"
           Case Else : Return "{" & String.Join(", ", source.Take(source.Length - 1)) & " and " & source(source.Length - 1) & "}"
       End Select
   End Function
   Sub Main()
       Dim cases As String()() = {Array.Empty(Of String), New String() {"ABC"}, New String() {"ABC", "DEF"}, New String() {"ABC", "DEF", "G", "H"}}
       For Each c In cases
           Console.WriteLine(FormatArray(c))
           Console.WriteLine(FormatEnumerable(c))
       Next
   End Sub

End Module </lang>

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

Wren

<lang ecmascript>var quibbling = Fn.new { |w|

   var c = w.count
   if (c == 0) return "{}"
   if (c == 1) return "{%(w[0])}"
   if (c == 2) return "{%(w[0]) and %(w[1])}"
   return "{%(w[0..-2].join(", ")) and %(w[-1])}"

}

var words = [ [], ["ABC"], ["ABC", "DEF"], ["ABC", "DEF", "G", "H"] ] for (w in words) System.print(quibbling.call(w))</lang>

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

XBS

<lang>func task(a:array){ set x:string=""; foreach(k,v as a){ set out:string=""; if ((k==(?a-2))&((?a)>1)){out=" and "}elif(k!=(?a-1)){out=", "} x+=v+out;del out; } send "{"+x+"}"; }

log(task([])); log(task(["ABC"])); log(task(["ABC","DEF"])); log(task(["ABC","DEF","GHI"]));</lang>

Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF and GHI}

XLISP

I like the Oxford comma; but specifications are specifications. So this implementation produces the required output by default. It also, however, allows an optional OXFORD-COMMA parameter: pass a true value, and you won't find yourself saying things like "I want to thank my parents, Ayn Rand and God". <lang lisp>(defun quibble (inputs &optional oxford-comma)

   (define final
       (if (and (caddr inputs) oxford-comma)
           ", and "
           " and " ) )
   (defun comma-quibble (words)
       (cond
           ((null words) "")
           ((null (cdr words)) (car words))
           (t (begin
               (string-append (car words)
                   (if (caddr words)
                       (string-append ", " (comma-quibble (cdr words)))
                       (string-append final (cadr words))) ) ) ) ) )
   (string-append "{" (comma-quibble inputs) "}") )
test cases

(print (quibble '())) ; empty list (print (quibble '("ABC"))) (print (quibble '("ABC" "DEF"))) (print (quibble '("ABC" "DEF" "G" "H")))

(newline)

test cases using the Oxford comma

(print (quibble '() t)) (print (quibble '("ABC") t)) (print (quibble '("ABC" "DEF") t)) (print (quibble '("ABC" "DEF" "G" "H") t))</lang>

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

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

ZX Spectrum Basic

<lang zxbasic>10 DATA 0 20 DATA 1,"ABC" 30 DATA 2,"ABC","DEF" 40 DATA 4,"ABC","DEF","G","H" 50 FOR n=10 TO 40 STEP 10 60 RESTORE n: GO SUB 1000 70 NEXT n 80 STOP 1000 REM quibble 1010 LET s$="" 1020 READ j 1030 IF j=0 THEN GO TO 1100 1040 FOR i=1 TO j 1050 READ a$ 1060 LET s$=s$+a$ 1070 IF (i+1)=j THEN LET s$=s$+" and ": GO TO 1090 1080 IF (i+1)<j THEN LET s$=s$+", " 1090 NEXT i 1100 PRINT "{";s$;"}" 1110 RETURN</lang>