Comma quibbling

From Rosetta Code
Jump to: navigation, search
Task
Comma quibbling
You are encouraged to solve this task according to the task description, using any language you may know.

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

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

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

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

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

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

Contents

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


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

[edit] 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)
}
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

[edit] C

#include <stdio.h>
#include <string.h>
#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;
}
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF and G}
{ABC, DEF, G and H}

[edit] C#

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

[edit] C++

#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;
}
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF and G}
{ABC, DEF, G and H}

[edit] 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)
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

[edit] COBOL

Works with: OpenCOBOL version 2.0
       >>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.
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF and G}
{ABC, DEF, G and H}

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

[edit] Common Lisp

 
(defun quibble (&rest args)
(format t "{~{~a~#[~; and ~:;, ~]~}}" args))
 
(quibble)
(quibble "ABC")
(quibble "ABC" "DEF")
(quibble "ABC" "DEF" "G" "H")
 
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

[edit] 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;
}
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

[edit] Alternative Version

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

[edit] Déjà Vu

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

[edit] Erlang

 
-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]), " ").
 
Output:
36> comma_quibbling:task().
["{}","{ABC}","{ABC and DEF}","{ABC, DEF, G and H}"]

[edit] F#

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

Output from testing (in F# Interactive 3.0, Open Source version):

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

[edit] 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.

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"}))
}
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

[edit] Groovy

def commaQuibbling = { it.size() < 2 ? "{${it.join(', ')}}" : "{${it[0..-2].join(', ')} and ${it[-1]}}" }

Testing:

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

[edit] 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" ])
 
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}
{One, two, three and four}
{Me, myself and I}
{Jack and Jill}
{Loner}

[edit] Icon and Unicon

The following works in both languages:

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

Sample run:

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

[edit] J

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

Testing:

   Tests=: (<'');(<'ABC');('ABC';'DEF');<('ABC';'DEF';'G';'H')
quibble every Tests
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

Alternative implementation:

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

(same results)

[edit] 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"}));
}
}
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

[edit] 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));
}
);
Output:
{}
{ABC}
{ABC and DEF}
{ABC,DEF,G and H}
Works with: jq version 1.4

[edit] jq

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

Example:

( [], ["ABC"],  ["ABC", "DEF"],  ["ABC", "DEF", "G", "H"]) | quibble
Output:
jq -n -r -f Comma_quibbling.jq
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}
 

[edit] Julia

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

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

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

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

[edit] 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('{}')
}
 
}

Output:

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

[edit]

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
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

[edit] 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:

Check it on the required inputs:

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

[edit] Mathematica

quibble[words___] :=
ToString@{StringJoin@@
Replace[Riffle[{words}, ", "],
{most__, ", ", last_} -> {most, " and ", last}]}
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}

[edit] 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+"}")
)
)
 

Output:

 
separate words:#()
"{}"
separate words:#("ABC")
"{ABC}"
separate words:#("ABC","DEF")
"{ABC and DEF}"
separate words:#("ABC","DEF","G","H")
"{ABC, DEF, G and H}"
 

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

[edit] Nimrod

proc commaQuibble(s): string =
result = ""
for i, c in s:
if i > 0: result.add (if i < s.high: ", " else: " and ")
result.add c
result = "{" & result & "}"
 
var s = @[@[], @["ABC"], @["ABC", "DEF"], @["ABC", "DEF", "G", "H"]]
for i in s:
echo commaQuibble(i)

[edit] PARI/GP

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"])

Output:

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

[edit] Perl

Translation of: Perl 6
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)];
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

Perl 5.01 version and other approach:

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)];
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

[edit] Perl 6

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>];
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

[edit] 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;
}
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

[edit] PicoLisp

(for L '([] ["ABC"] ["ABC", "DEF"] ["ABC", "DEF", "G", "H"])
(let H (head -1 L)
(prinl
"{"
(glue ", " H)
(and H " and ")
(last L)
"}" ) ) )

Output:

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


[edit] PL/I

*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;
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}      

[edit] Prolog

Works with: SWI-Prolog version 7.1
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]))
).
Output:
?- test.
[] => {}
[ABC] => {ABC}
[ABC,DEF] => {ABC and DEF}
[ABC,DEF,G,H] => {ABC, DEF, G and H}
true.

[edit] Python

[edit] 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.

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

[edit] 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:

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

[edit] Python: Functional

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

[edit] 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)))
Output:
()	->	{}
("ABC")	->	{ABC}
("ABC" "DEF")	->	{ABC and DEF}
("ABC" "DEF" "G" "H")	->	{ABC, DEF, G and H}

[edit] REBOL

[edit] Straightforward implementation

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

[edit] Alternative (more efficient) version with oxford comma switch

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]
 
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}
Now with Oxford comma
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G, and H}

[edit] REXX

[edit] version 1:

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'}'
Output:
{}
{ABC}
{ABC and DEF}
{ABC,DEF,G and H}

[edit] version 2:

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'}'
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}    

[edit] version 3:

Translation of: NetRexx
/* 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'}'
 
Output:
                            [] : {}
                       ["ABC"] : {ABC}
                ["ABC", 'DEF'] : {ABC and DEF}
              [ABC, DEF, G, H] : {ABC, DEF, G and H}

[edit] Ruby

Translation of: Perl 6
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
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}


[edit] Run BASIC

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

[edit] Rust

// rust 0.9-pre
 
fn quibble(seq: &[&str]) -> ~str {
match seq {
[] => ~"{}",
[ref word] => "{" + *word + "}",
[..words, ref word] => "{" + words.connect(", ") + " and " + *word + "}",
}
}
 
fn main() {
println(quibble([]));
println(quibble(["ABC"]));
println(quibble(["ABC", "DEF"]));
println(quibble(["ABC", "DEF", "G", "H"]));
}
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

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

[edit] 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;
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

[edit] Sidef

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);
}
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

[edit] Standard ML

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

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

[edit] UNIX Shell

Translation of: AWK
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
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

[edit] 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)]
 
Output:
{}
{ABC}
{ABC and DEF}
{ABC, DEF, G and H}

[edit] zkl

This is a cheese ball solution that replies on no commas in the inputs

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,*])
}
}

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}"
Personal tools
Namespaces

Variants
Actions
Community
Explore
Misc
Toolbox