Comma quibbling: Difference between revisions
No edit summary |
Add ed example |
||
(310 intermediate revisions by more than 100 users not shown) | |||
Line 1: | Line 1: | ||
{{ |
{{task}} |
||
Comma quibbling is a task originally set by Eric Lippert in his [http://blogs.msdn.com/b/ericlippert/archive/2009/04/15/comma-quibbling.aspx blog]. |
Comma quibbling is a task originally set by Eric Lippert in his [http://blogs.msdn.com/b/ericlippert/archive/2009/04/15/comma-quibbling.aspx 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: |
|||
;Task: |
|||
Write a function to generate a string output which is the concatenation of input words from a list/sequence where: |
|||
# An input of no words produces the output string of just the two brace characters "{}". |
# An input of no words produces the output string of just the two brace characters "{}". |
||
# An input of just one word, e.g. ["ABC"], produces the output string of the word inside the two braces, e.g. "{ABC}". |
# An input of just one word, e.g. ["ABC"], produces the output string of the word inside the two braces, e.g. "{ABC}". |
||
Line 8: | Line 11: | ||
# 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}". |
# 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}". |
||
<br> |
|||
Test your function with the following series of inputs showing your output here on this page: |
Test your function with the following series of inputs showing your output here on this page: |
||
* [] # (No input words). |
* [] # (No input words). |
||
Line 14: | Line 18: | ||
* ["ABC", "DEF", "G", "H"] |
* ["ABC", "DEF", "G", "H"] |
||
<br> |
|||
Note: for this task assume words are non-empty strings. |
|||
Note: Assume words are non-empty strings of uppercase characters for this task. |
|||
<br><br> |
|||
=={{header|11l}}== |
|||
<syntaxhighlight 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’]))</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|360 Assembly}}== |
|||
<syntaxhighlight 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</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|8080 Assembly}}== |
|||
<syntaxhighlight 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:</syntaxhighlight> |
|||
{{out}} |
|||
<pre>A>quib |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|ABC}}== |
|||
<syntaxhighlight lang="ABC">HOW TO RETURN quibble words: |
|||
PUT "" IN result |
|||
PUT #words IN remaining |
|||
FOR word IN words: |
|||
PUT result^word IN result |
|||
PUT remaining-1 IN remaining |
|||
IF remaining = 1: PUT result^" and " IN result |
|||
IF remaining > 1: PUT result^", " IN result |
|||
RETURN "{" ^ result ^ "}" |
|||
PUT {} IN tests |
|||
INSERT {} IN tests |
|||
INSERT {[1]: "ABC"} IN tests |
|||
INSERT {[1]: "ABC"; [2]: "DEF"} IN tests |
|||
INSERT {[1]: "ABC"; [2]: "DEF"; [3]: "G"; [4]: "H"} IN tests |
|||
FOR test IN tests: |
|||
WRITE quibble test/</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|Acornsoft Lisp}}== |
|||
There's no string data type; symbols are used instead. The <code>implode</code> function is used to concatenate a list of symbols. When writing a symbol in source code, exclamation mark is an escape character that allows characters such as spaces and exclamation marks to be treated as part of the symbol's name. |
|||
<syntaxhighlight lang="lisp"> |
|||
(defun examples () |
|||
(map '(lambda (words) (printc (quibble words))) |
|||
'(() (ABC) (ABC DEF) (ABC DEF G H)))) |
|||
(defun quibble (words) |
|||
(implode (list '{ (quibbles words) '}))) |
|||
(defun quibbles (words) |
|||
(implode (conjunction words))) |
|||
(defun conjunction (words) |
|||
(cond ((null words) |
|||
'()) |
|||
((null (cdr words)) |
|||
words) |
|||
((null (cddr words)) |
|||
(list (car words) '! and! (cadr words))) |
|||
(t |
|||
(cons (car words) |
|||
(cons ',! (conjunction (cdr words))))))) |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
Calling <code>(examples)</code> will output: |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|Action!}}== |
|||
<syntaxhighlight 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</syntaxhighlight> |
|||
{{out}} |
|||
[https://gitlab.com/amarok8bit/action-rosetta-code/-/raw/master/images/Comma_quibbling.png Screenshot from Atari 8-bit computer] |
|||
<pre> |
|||
() |
|||
(ABC) |
|||
(ABC and DEF) |
|||
(ABC, DEF and G) |
|||
(ABC, DEF, G and H) |
|||
</pre> |
|||
=={{header|Ada}}== |
|||
<syntaxhighlight 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;</syntaxhighlight> |
|||
{{out}} |
|||
<pre>./comma_quibble |
|||
{} |
|||
./comma_quibble abc |
|||
{abc} |
|||
./comma_quibble abc def |
|||
{abc and def} |
|||
./comma_quibble abc def g h |
|||
{abc, def, g and h}</pre> |
|||
=={{header|ALGOL 68}}== |
|||
{{works with|ALGOL 68G|Any - tested with release 2.8.win32}} |
|||
<syntaxhighlight lang="algol68"># returns a string ( assumed to be of space-separated words ) with the words # |
|||
# separated by ", ", except for the last which is separated from the rest by # |
|||
# " and ". The list is enclosed by braces # |
|||
PROC to list = ( STRING words ) STRING: |
|||
BEGIN |
|||
# count the number of words # |
|||
INT word count := 0; |
|||
BOOL in word := FALSE; |
|||
FOR char pos FROM LWB words TO UPB words |
|||
DO |
|||
IF NOT is upper( words[ char pos ] ) |
|||
THEN |
|||
# not an upper-case letter, possibly a word has been ended # |
|||
in word := FALSE |
|||
ELSE |
|||
# not a delimitor, possibly the start of a word # |
|||
IF NOT in word |
|||
THEN |
|||
# we are starting a new word # |
|||
word count +:= 1; |
|||
in word := TRUE |
|||
FI |
|||
FI |
|||
OD; |
|||
# format the result # |
|||
STRING result := "{"; |
|||
in word := FALSE; |
|||
INT word number := 0; |
|||
FOR char pos FROM LWB words TO UPB words |
|||
DO |
|||
IF NOT is upper( words[ char pos ] ) |
|||
THEN |
|||
# not an upper-case letter, possibly a word has been ended # |
|||
in word := FALSE |
|||
ELSE |
|||
# not a delimitor, possibly the start of a word # |
|||
IF NOT in word |
|||
THEN |
|||
# we are starting a new word # |
|||
word number +:= 1; |
|||
in word := TRUE; |
|||
IF word number > 1 |
|||
THEN |
|||
# second or subsequent word - need a separator # |
|||
result +:= IF word number = word count |
|||
THEN # final word # |
|||
" and " |
|||
ELSE # non-final word # |
|||
", " |
|||
FI |
|||
FI |
|||
FI; |
|||
# add the character to the result # |
|||
result +:= words[ char pos ] |
|||
FI |
|||
OD; |
|||
result + "}" |
|||
END # to list # ; |
|||
# procedure to test the to list PROC # |
|||
PROC test to list = ( STRING words ) VOID: |
|||
print( ( ( words |
|||
+ ": " |
|||
+ to list( words ) |
|||
) |
|||
, newline |
|||
) |
|||
); |
|||
# test the to list PROC # |
|||
test to list( "" ); |
|||
test to list( "ABC" ); |
|||
test to list( "ABC DEF" ); |
|||
test to list( "ABC DEF G H" )</syntaxhighlight> |
|||
{{out}} |
|||
<pre>: {} |
|||
ABC: {ABC} |
|||
ABC DEF: {ABC and DEF} |
|||
ABC DEF G H: {ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|ALGOL W}}== |
|||
<syntaxhighlight 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.</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
: {} |
|||
ABC : {ABC} |
|||
ABC DEF : {ABC and DEF} |
|||
ABC DEF G H : {ABC, DEF, G and H}</pre> |
|||
=={{header|APL}}== |
|||
<syntaxhighlight lang="APL">quibble ← 1⌽'}{',(∊⊢,¨2↓(' and ' ''),⍨(⊂', ')⍴⍨⍴)</syntaxhighlight> |
|||
{{out}} |
|||
<pre> quibble ⍬ |
|||
{} |
|||
quibble ⊂'ABC' |
|||
{ABC} |
|||
quibble 'ABC' 'DEF' |
|||
{ABC and DEF} |
|||
quibble 'ABC' 'DEF' 'G' 'H' |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|AppleScript}}== |
|||
{{Trans|JavaScript}} |
|||
<syntaxhighlight 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|</syntaxhighlight> |
|||
{{Out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
{One, two, three and four} |
|||
{Me, myself and I} |
|||
{Jack and Jill} |
|||
{Loner}</pre> |
|||
=={{header|Arturo}}== |
|||
<syntaxhighlight lang="rebol">quibble: $[sequence :block][ |
|||
if? 0 = size sequence |
|||
-> return "{}" |
|||
if? 1 = size sequence |
|||
-> return ~"{|sequence\0|}" |
|||
last: pop 'sequence |
|||
return ~« {|join.with: ", " sequence| and |last|} |
|||
] |
|||
sentences: [ |
|||
[] |
|||
["ABC"] |
|||
["ABC" "DEF"] |
|||
["ABC" "DEF" "G" "H"] |
|||
] |
|||
loop sentences 'sentence [ |
|||
print quibble sentence |
|||
]</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|Astro}}== |
|||
<syntaxhighlight 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)</syntaxhighlight> |
|||
=={{header|AutoHotkey}}== |
|||
<syntaxhighlight 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 . "}" |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|AWK}}== |
|||
<syntaxhighlight 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) |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|Batch File}}== |
|||
<syntaxhighlight 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</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
Press any key to continue . . .</pre> |
|||
=={{header|BCPL}}== |
|||
<syntaxhighlight 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)) |
|||
$)</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{"ABC"} |
|||
{"ABC" and "DEF"} |
|||
{"ABC", "DEF", "G" and "H"}</pre> |
|||
=={{header|Bracmat}}== |
|||
<syntaxhighlight 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)) |
|||
);</syntaxhighlight> |
|||
{{out}} |
|||
<pre>L1 {} |
|||
L2 {ABC} |
|||
L3 {ABC and DEF} |
|||
L4 {ABC, DEF, G and H}</pre> |
|||
=={{header|C}}== |
|||
<syntaxhighlight lang="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; |
|||
}</syntaxhighlight> |
|||
{{Out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF and G} |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|C sharp|C#}}== |
|||
<syntaxhighlight 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 |
|||
} |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
< Press Any Key ></pre> |
|||
=={{header|C++}}== |
|||
<syntaxhighlight 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; |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF and G} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|Clojure}}== |
|||
<syntaxhighlight 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)</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|CLU}}== |
|||
<syntaxhighlight lang="clu">quibble = proc (words: array[string]) returns (string) |
|||
out: string := "{" |
|||
last: int := array[string]$high(words) |
|||
for i: int in array[string]$indexes(words) do |
|||
out := out || words[i] |
|||
if i < last-1 then |
|||
out := out || ", " |
|||
elseif i = last-1 then |
|||
out := out || " and " |
|||
end |
|||
end |
|||
return(out || "}") |
|||
end quibble |
|||
start_up = proc () |
|||
as = array[string] |
|||
aas = array[as] |
|||
po: stream := stream$primary_output() |
|||
testcases: aas := aas$ |
|||
[as$[], |
|||
as$["ABC"], |
|||
as$["ABC","DEF"], |
|||
as$["ABC","DEF","G","H"]] |
|||
for testcase: as in aas$elements(testcases) do |
|||
stream$putl(po, quibble(testcase)) |
|||
end |
|||
end start_up</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|COBOL}}== |
|||
{{works with|OpenCOBOL|2.0}} |
|||
<syntaxhighlight 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.</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF and G} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|CoffeeScript}}== |
|||
<syntaxhighlight 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" ] ] |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|Commodore BASIC}}== |
|||
The Commodore character set has no curly braces, so I substituted square brackets. The solution could no doubt be improved, but some of the elegance of other solutions is not possible simply because a FOR loop always executes at least once, even if its parameters would seem to indicate that it should not. |
|||
<syntaxhighlight lang="basic">100 DIM A$(3) |
|||
110 FOR TC=1 TO 4 |
|||
120 : READ A: IF A=0 THEN 160 |
|||
130 : FOR I=0 TO A-1 |
|||
140 : READ A$(I) |
|||
150 : NEXT I |
|||
160 : GOSUB 200 |
|||
170 : PRINT CQ$ |
|||
180 NEXT TC |
|||
190 END |
|||
200 CQ$="[" |
|||
210 IF A < 1 THEN 290 |
|||
220 CQ$ = CQ$ + A$(0) |
|||
230 IF A < 2 THEN 290 |
|||
240 IF A < 3 THEN 280 |
|||
250 FOR I=1 TO A - 2 |
|||
260 : CQ$ = CQ$ + ", " + A$(I) |
|||
270 NEXT I |
|||
280 CQ$ = CQ$ + " AND " + A$(A - 1) |
|||
290 CQ$ = CQ$ + "]" |
|||
300 RETURN |
|||
310 DATA 0 |
|||
320 DATA 1, ABC |
|||
330 DATA 2, ABC, DEF |
|||
340 DATA 4, ABC, DEF, G, H |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre>[] |
|||
[ABC] |
|||
[ABC AND DEF] |
|||
[ABC, DEF, G AND H]</pre> |
|||
=={{header|Common Lisp}}== |
|||
<syntaxhighlight lang="lisp"> |
|||
(defun quibble (&rest args) |
|||
(format t "{~{~a~#[~; and ~:;, ~]~}}" args)) |
|||
(quibble) |
|||
(quibble "ABC") |
|||
(quibble "ABC" "DEF") |
|||
(quibble "ABC" "DEF" "G" "H") |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|Cowgol}}== |
|||
<syntaxhighlight 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(); |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|D}}== |
=={{header|D}}== |
||
< |
<syntaxhighlight lang="d">import std.stdio, std.string; |
||
string quibbler(in string[] seq) pure /*nothrow*/ { |
string quibbler(in string[] seq) pure /*nothrow*/ { |
||
Line 33: | Line 1,437: | ||
["ABC", "DEF", "G", "H"]]) |
["ABC", "DEF", "G", "H"]]) |
||
test.quibbler.writeln; |
test.quibbler.writeln; |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>{} |
<pre>{} |
||
Line 40: | Line 1,444: | ||
{ABC, DEF, G and H}</pre> |
{ABC, DEF, G and H}</pre> |
||
===Alternative Version=== |
|||
=={{header|Perl 6}}== |
|||
<syntaxhighlight lang="d">import std.stdio, std.string, std.algorithm, std.conv, std.array; |
|||
<lang perl6>sub comma-quibbling(@A) { |
|||
'{' ~ ( |
|||
enum quibbler = (in string[] a) pure => |
|||
@$_ < 3 ?? .join(',') !! |
|||
"{%-(%s and %)}".format(a.length < 2 ? a : |
|||
[a[0 .. $-1].join(", "), a.back]); |
|||
) ~ '}' given @A».perl |
|||
void main() { |
|||
[[], ["ABC"], ["ABC", "DEF"], ["ABC", "DEF", "G", "H"]] |
|||
.map!quibbler.writeln; |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>["{}", "{ABC}", "{ABC and DEF}", "{ABC, DEF, G and H}"]</pre> |
|||
=={{header|dc}}== |
|||
<syntaxhighlight lang="dc">[(q)uibble: main entry point. print brackets, calling n in between if stack not |
|||
empty.]sx |
|||
[ [{]n z 0 !=n [}]pR ]sq |
|||
[(n)onempty: if more than 1 item, call m. then print top of stack.]sx |
|||
[ z 1 !=m n ]sn |
|||
[(m)ore: call f to flip stack into r register, call p to print most of it, |
|||
then pop the last item back onto the main stack so it's there to be printed |
|||
after we return]sx |
|||
[ lfx lpx Lr ]sm |
|||
[(f)lip: utility routine to reverse the stack into the r register]sx |
|||
[ Sr z 0 !=f ]sf |
|||
[(p)rint: get next item from stack in r register and print it. If there are |
|||
more than 2 items left on the register stack (which never drops below one |
|||
item), print a comma (c) and recurse. If there are exactly two items left, |
|||
print " and " (a) and return.]sx |
|||
[ Lr n 2 yr >c 2 yr =a 2 yr >p]sp |
|||
[(c)omma: utility routine to print a comma followed by a space]sx |
|||
[ [, ]n ]sc |
|||
[(a)and: utility routine to print " and "]sx |
|||
[ [ and ]n ]sa |
|||
[run tests]sx |
|||
lqx |
|||
[ABC] lqx |
|||
[ABC] [DEF] lqx |
|||
[ABC] [DEF] [G] [H] lqx</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|DCL}}== |
|||
<syntaxhighlight 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</syntaxhighlight> |
|||
{}out}} |
|||
<pre>$ @comma_quibbling |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|Delphi}}== |
|||
See [[#Pascal|Pascal]]. |
|||
=={{header|Déjà Vu}}== |
|||
<syntaxhighlight 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" ]</syntaxhighlight> |
|||
{{out}} |
|||
<pre>"{}" |
|||
"{ABC}" |
|||
"{ABC and DEF}" |
|||
"{ABC, DEF, G and H}"</pre> |
|||
=={{header|EasyLang}}== |
|||
<syntaxhighlight> |
|||
func$ tolist s$ . |
|||
s$[] = strsplit s$ " " |
|||
r$ = "{" |
|||
n = len s$[] |
|||
for i = 1 to n - 2 |
|||
r$ &= s$[i] & ", " |
|||
. |
|||
if n > 0 |
|||
if n > 1 |
|||
r$ &= s$[n - 1] & " and " |
|||
. |
|||
r$ &= s$[n] |
|||
. |
|||
r$ &= "}" |
|||
return r$ |
|||
. |
|||
print tolist "" |
|||
print tolist "ABC" |
|||
print tolist "ABC DEF" |
|||
print tolist "ABC DEF G H" |
|||
</syntaxhighlight> |
|||
=={{header|EchoLisp}}== |
|||
<syntaxhighlight 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 }" |
|||
</syntaxhighlight> |
|||
=={{header|ed}}== |
|||
Uses basic regular expressions (BREs). |
|||
<syntaxhighlight lang="sed"> |
|||
# by Artyom Bologov |
|||
H |
|||
,p |
|||
g/.*/s/ /, /g |
|||
g/[,]/s/,\([^,]*\)$/ and\1/ |
|||
g/.*/s//{&}/ |
|||
,p |
|||
Q |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre>$ ed -s comma-quibling.input < comma-quibling.ed |
|||
Newline appended |
|||
ABC |
|||
ABC DEF |
|||
ABC DEF G H |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|Eiffel}}== |
|||
<syntaxhighlight 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 |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|Elixir}}== |
|||
{{trans|Erlang}} |
|||
<syntaxhighlight 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)</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
"{}" |
|||
"{ABC}" |
|||
"{ABC and DEF}" |
|||
"{ABC, DEF, G and H}" |
|||
</pre> |
|||
=={{header|Erlang}}== |
|||
<syntaxhighlight lang="text"> |
|||
-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]), " "). |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
36> comma_quibbling:task(). |
|||
["{}","{ABC}","{ABC and DEF}","{ABC, DEF, G and H}"] |
|||
</pre> |
|||
=={{header|F_Sharp|F#}}== |
|||
===One Way=== |
|||
<syntaxhighlight 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"]</syntaxhighlight> |
|||
Output from testing (in F# Interactive 3.0, Open Source version): |
|||
<syntaxhighlight lang="text"> |
|||
> 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}"</syntaxhighlight> |
|||
===or Another=== |
|||
====The Function==== |
|||
<syntaxhighlight lang="fsharp"> |
|||
let quibble quibbler quibblee = Seq.zip quibblee quibbler //Sorry, just too good a line to miss, back in my Latin classes |
|||
</syntaxhighlight> |
|||
====The Task==== |
|||
<syntaxhighlight 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"}" |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
{ABC, DEF, G and H} |
|||
{ABC, DEF and G} |
|||
{ABC and DEF} |
|||
{ABC} |
|||
{} |
|||
</pre> |
|||
=={{header|Factor}}== |
|||
This example uses the <code>inverse</code> vocabulary, which builds on the concept of invertible quotations as the basis for pattern matching. It is discussed at length in this approachable [http://micsymposium.org/mics_2009_proceedings/mics2009_submission_72.pdf paper]. |
|||
<syntaxhighlight 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</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|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. |
|||
<syntaxhighlight lang="text"> |
|||
: 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 |
|||
</syntaxhighlight> |
|||
=={{header|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 <code>IF (''in bounds'' & ''Array indexing'')</code>) 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.<syntaxhighlight 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.</syntaxhighlight> |
|||
Output: |
|||
<pre> |
|||
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} |
|||
</pre> |
|||
=={{header|FreeBASIC}}== |
|||
<syntaxhighlight 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 |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|Frink}}== |
|||
<syntaxhighlight lang="frink">quibble[enum] := |
|||
{ |
|||
list = toArray[enum] // This makes it work on any enumerating expression |
|||
size = length[list] |
|||
if size >= 2 |
|||
return "{" + join[", ", first[list, size-1]] + " and " + last[list] + "}" |
|||
else |
|||
return "{" + join["", list] + "}" |
|||
} |
} |
||
data = [[], ["ABC"], ["ABC", "DEF"], ["ABC", "DEF", "G", "H"]] |
|||
say comma-quibbling(eval($_)) for |
|||
for line = data |
|||
< [] ["ABC"] ["ABC","DEF"] ["ABC","DEF","G","H"] >;</lang> |
|||
println[quibble[line]]</syntaxhighlight> |
|||
{{out}} |
{{out}} |
||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|FutureBasic}}== |
|||
Long solution: |
|||
<syntaxhighlight lang="futurebasic"> |
|||
include "NSLog.incl" |
|||
local fn CommaQuibber( string as CFStringRef ) as CFStringRef |
|||
CFStringRef tempStr |
|||
NSUInteger i |
|||
tempStr = fn StringByReplacingOccurrencesOfString( string, @"[", @"" ) |
|||
tempStr = fn StringByReplacingOccurrencesOfString( tempStr, @"]", @"" ) |
|||
tempStr = fn StringByReplacingOccurrencesOfString( tempStr, @" ", @"" ) |
|||
tempStr = fn StringByReplacingOccurrencesOfString( tempStr, @"\"", @"" ) |
|||
CFMutableStringRef quibStr = fn MutableStringWithCapacity(0) |
|||
CFArrayRef arr = fn StringComponentsSeparatedByString( tempStr, @"," ) |
|||
NSUInteger count = len(arr) |
|||
select switch ( count ) |
|||
case 0 : MutableStringSetString( quibStr, @"{}" ) : break |
|||
case 1 : MutableStringSetString( quibStr, fn StringWithFormat( @"{%@}", arr[0] ) ) : break |
|||
case 2 : MutableStringSetString( quibStr, fn StringWithFormat( @"{%@ and %@}", arr[0], arr[1] ) ) : break |
|||
case else |
|||
MutableStringAppendFormat( quibStr, @"{" ) |
|||
for i = 0 to count -1 |
|||
if ( i != count -1 ) |
|||
MutableStringAppendFormat( quibStr, @"%@, ", arr[i] ) |
|||
else |
|||
MutableStringAppendFormat( quibStr, @"and %@}", arr[i] ) |
|||
end if |
|||
next |
|||
end select |
|||
end fn = quibStr |
|||
NSLog( @"%@", fn CommaQuibber( @"[]" ) ) |
|||
NSLog( @"%@", fn CommaQuibber( @"[\"ABC\"]" ) ) |
|||
NSLog( @"%@", fn CommaQuibber( @"[\"ABC\", \"DEF\"]" ) ) |
|||
NSLog( @"%@", fn CommaQuibber( @"[\"ABC\", \"DEF\", \"G\", \"H\"]" ) ) |
|||
HandleEvents |
|||
</syntaxhighlight> |
|||
Short solution: |
|||
<syntaxhighlight lang="futurebasic"> |
|||
local fn CommaQuibbler( s as CFArrayRef ) as CFStringRef |
|||
CFStringRef result = NULL |
|||
select ( len(s) ) |
|||
case 0: exit fn = @"{}" |
|||
case 1: exit fn = fn StringWithFormat( @"{%@}", s[0] ) |
|||
case 2: exit fn = fn StringWithFormat( @"{%@ and %@}", s[0], s[1] ) |
|||
case else |
|||
result = fn StringWithFormat( @"{%@}", fn ArrayComponentsJoinedByString( s, @", " ) ) |
|||
CFRange lastComma = fn StringRangeOfStringWithOptions( result, @",", NSBackwardsSearch ) |
|||
result = fn StringByReplacingCharactersInRange( result, lastComma, @" and" ) |
|||
end select |
|||
end fn = result |
|||
print fn CommaQuibbler( @[] ) |
|||
print fn CommaQuibbler( @[@"ABC"] ) |
|||
print fn CommaQuibbler( @[@"ABC", @"DEF"] ) |
|||
print fn CommaQuibbler( @[@"ABC", @"DEF", @"G", @"H"] ) |
|||
HandleEvents |
|||
</syntaxhighlight> |
|||
{{output}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G, and H} |
|||
</pre> |
|||
=={{header|Gambas}}== |
|||
'''[https://gambas-playground.proko.eu/?gist=8edc63b206a1de50dd104cd12486ac03 Click this link to run this code]''' |
|||
<syntaxhighlight 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</syntaxhighlight> |
|||
Output: |
|||
<pre> |
|||
= {} |
|||
ABC = {ABC} |
|||
ABC DEF = {ABC and DEF} |
|||
ABC DEF G H = {ABC,DEF,G and H} |
|||
</pre> |
|||
=={{header|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. |
|||
<syntaxhighlight 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"})) |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|Groovy}}== |
|||
<syntaxhighlight lang="groovy">def commaQuibbling = { it.size() < 2 ? "{${it.join(', ')}}" : "{${it[0..-2].join(', ')} and ${it[-1]}}" }</syntaxhighlight> |
|||
'''Testing:''' |
|||
<syntaxhighlight 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 |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Verifying commaQuibbling([]) == {} |
|||
Verifying commaQuibbling([ABC]) == {ABC} |
|||
Verifying commaQuibbling([ABC, DEF]) == {ABC and DEF} |
|||
Verifying commaQuibbling([ABC, DEF, G, H]) == {ABC, DEF, G and H}</pre> |
|||
=={{header|Haskell}}== |
|||
<syntaxhighlight 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" ]) |
|||
</syntaxhighlight> |
|||
{{Out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
{One, two, three and four} |
|||
{Me, myself and I} |
|||
{Jack and Jill} |
|||
{Loner} |
|||
</pre> |
|||
Or, defining just two cases, and drawing more on standard libraries than on hand-crafted pattern-matching and recursion: |
|||
<syntaxhighlight lang="haskell">import Data.List (intercalate) |
|||
--------------------- COMMA QUIBBLING -------------------- |
|||
quibble :: [String] -> String |
|||
quibble ws@(_ : _ : _) = |
|||
intercalate |
|||
" and " |
|||
( [intercalate ", " . reverse . tail, head] |
|||
<*> [reverse ws] |
|||
) |
|||
quibble xs = concat xs |
|||
--------------------------- TEST ------------------------- |
|||
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" |
|||
] |
|||
)</syntaxhighlight> |
|||
{{Out}} |
|||
<pre>{} |
<pre>{} |
||
{ |
{ABC} |
||
{ |
{ABC and DEF} |
||
{ |
{ABC, DEF, G and H} |
||
{One, two, three and four} |
|||
{Me, myself and I} |
|||
{Jack and Jill} |
|||
{Loner}</pre> |
|||
=={{header|Icon}} and {{header|Unicon}}== |
|||
The following works in both languages: |
|||
<syntaxhighlight 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</syntaxhighlight> |
|||
Sample run: |
|||
<pre> |
|||
->cq |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
-> |
|||
</pre> |
|||
=={{header|J}}== |
|||
<syntaxhighlight lang="j">quibLast2=: ' and ' joinstring (2 -@<. #) {. ] |
|||
withoutLast2=: ([: # _2&}.) {. ] |
|||
quibble=: '{', '}' ,~ ', ' joinstring withoutLast2 , <@quibLast2</syntaxhighlight> |
|||
'''Testing:''' |
|||
<syntaxhighlight lang="j"> Tests=: (<<<3){(i.5)<@{."0 1;:'ABC DEF G H' |
|||
quibble every Tests |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</syntaxhighlight> |
|||
Alternative implementation: |
|||
<syntaxhighlight lang="j">commaand=: 1 ;@}.&, ] ,.~ 1 |.!.(<' and ') (<', ')"0 |
|||
quibble=: '{','}',~ commaand</syntaxhighlight> |
|||
(same results) |
|||
=={{header|Java}}== |
|||
<syntaxhighlight 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"})); |
|||
} |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|JavaScript}}== |
|||
===ES5=== |
|||
<syntaxhighlight 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)); |
|||
} |
|||
);</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC,DEF,G and H} |
|||
</pre> |
|||
===ES6=== |
|||
{{Trans|Haskell}} |
|||
Composing from a set of generic functions: |
|||
<syntaxhighlight 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(); |
|||
})();</syntaxhighlight> |
|||
{{Out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
{One, two, three and four} |
|||
{Me, myself and I} |
|||
{Jack and Jill} |
|||
{Loner}</pre> |
|||
Alternative implementation: |
|||
<syntaxhighlight 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+'}'; |
|||
} |
|||
</syntaxhighlight> |
|||
=={{header|jq}}== |
|||
{{works with|jq|1.4}} |
|||
<syntaxhighlight lang="jq">def quibble: |
|||
if length == 0 then "" |
|||
elif length == 1 then .[0] |
|||
else (.[0:length-1] | join(", ")) + " and " + .[length-1] |
|||
end |
|||
| "{" + . + "}";</syntaxhighlight> |
|||
'''Example''': |
|||
<syntaxhighlight lang="jq">( [], ["ABC"], ["ABC", "DEF"], ["ABC", "DEF", "G", "H"]) | quibble</syntaxhighlight> |
|||
{{Out}} |
|||
<syntaxhighlight lang="sh">jq -n -r -f Comma_quibbling.jq |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</syntaxhighlight> |
|||
=={{header|Julia}}== |
|||
{{works with|Julia|0.6}} |
|||
<syntaxhighlight 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"])</syntaxhighlight> |
|||
{{Out}} |
|||
<pre>quibble([]) = "{}" |
|||
quibble(["ABC"]) = "{ABC}" |
|||
quibble(["ABC", "DEF"]) = "{ABC and DEF}" |
|||
quibble(["ABC", "DEF", "G", "H"]) = "{ABC, DEF, G and H}"</pre> |
|||
=={{header|Kotlin}}== |
|||
<syntaxhighlight 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)}") |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
[] -> {} |
|||
["ABC"] -> {ABC} |
|||
["ABC", "DEF"] -> {ABC and DEF} |
|||
["ABC", "DEF", "G", "H"] -> {ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|Lang}}== |
|||
<syntaxhighlight lang="lang"> |
|||
fp.quibble = (&words) -> { |
|||
$len $= @&words |
|||
$output = \{\e |
|||
$i |
|||
repeat($[i], $len) { |
|||
$output += &words[$i] ||| ($i == -|$len?\e:($i == $len - 2?\sand\s:\,\s)) |
|||
} |
|||
$output += \}\e |
|||
return $output |
|||
} |
|||
fn.println(fp.quibble(fn.arrayOf())) |
|||
fn.println(fp.quibble(fn.arrayOf(ABC))) |
|||
fn.println(fp.quibble(fn.arrayOf(ABC, DEF))) |
|||
fn.println(fp.quibble(fn.arrayOf(ABC, DEF, G, H))) |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|Lasso}}== |
|||
<syntaxhighlight 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('{}') |
|||
} |
|||
}</syntaxhighlight> |
|||
Output: |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|Liberty BASIC}}== |
|||
<syntaxhighlight 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. |
|||
</syntaxhighlight> |
|||
{{Out}} |
|||
<pre> |
|||
'' 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}</pre> |
|||
=={{header|Logo}}== |
|||
<syntaxhighlight 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</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|Lua}}== |
|||
<syntaxhighlight 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</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|M2000 Interpreter}}== |
|||
===Using string as argument=== |
|||
<syntaxhighlight 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 |
|||
</syntaxhighlight> |
|||
===Using String functions only=== |
|||
<syntaxhighlight 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 |
|||
</syntaxhighlight> |
|||
===Using array as argument=== |
|||
<syntaxhighlight 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 |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre > |
|||
{} |
|||
{ABC} |
|||
{ABC, DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre > |
|||
=={{header|Maple}}== |
|||
<syntaxhighlight 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:</syntaxhighlight> |
|||
Check it on the required inputs: |
|||
<syntaxhighlight lang="maple">> Quibble([]); |
|||
"{}" |
|||
> Quibble( [ "ABC" ] ); |
|||
"{ABC}" |
|||
> Quibble( [ "ABC", "DEF" ] ); |
|||
"{ABC and DEF}" |
|||
> Quibble( ["ABC", "DEF", "G", "H"] ); |
|||
"{ABC, DEF, G and H}" |
|||
</syntaxhighlight> |
|||
=={{header|Mathematica}} / {{header|Wolfram Language}}== |
|||
<syntaxhighlight lang="mathematica">quibble[words___] := |
|||
ToString@{StringJoin@@ |
|||
Replace[Riffle[{words}, ", "], |
|||
{most__, ", ", last_} -> {most, " and ", last}]}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
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} |
|||
</pre> |
|||
=={{header|MATLAB}} / {{header|Octave}}== |
|||
<syntaxhighlight 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; |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
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 |
|||
</pre> |
|||
=={{header|MAXScript}}== |
|||
<syntaxhighlight 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+"}") |
|||
) |
|||
) |
|||
</syntaxhighlight> |
|||
Output: |
|||
<syntaxhighlight 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}" |
|||
</syntaxhighlight> |
|||
=={{header|Miranda}}== |
|||
<syntaxhighlight lang="miranda">main :: [sys_message] |
|||
main = [Stdout (show test ++ ": {" ++ quibble test ++ "}\n") | test <- tests] |
|||
tests :: [[[char]]] |
|||
tests = [ [], |
|||
["ABC"], |
|||
["ABC","DEF"], |
|||
["ABC","DEF","G","H"] ] |
|||
quibble :: [[char]]->[char] |
|||
quibble [] = [] |
|||
quibble [word] = word |
|||
quibble [word1,word2] = word1 ++ " and " ++ word2 |
|||
quibble (word:words) = word ++ ", " ++ quibble words</syntaxhighlight> |
|||
{{out}} |
|||
<pre>[]: {} |
|||
["ABC"]: {ABC} |
|||
["ABC","DEF"]: {ABC and DEF} |
|||
["ABC","DEF","G","H"]: {ABC, DEF, G and H}</pre> |
|||
=={{header|NetRexx}}== |
|||
<syntaxhighlight 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 |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
[] : {} |
|||
["ABC"] : {ABC} |
|||
["ABC", "DEF"] : {ABC and DEF} |
|||
["ABC", "DEF", "G", "H"] : {ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|Nim}}== |
|||
<syntaxhighlight 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)</syntaxhighlight> |
|||
=={{header|Oberon-2}}== |
|||
{{works with|oo2c}} |
|||
<syntaxhighlight 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. |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
$ 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} |
|||
</pre> |
|||
=={{header|Objeck}}== |
|||
<syntaxhighlight 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(); |
|||
} |
|||
}</syntaxhighlight> |
|||
Output: |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|OCaml}}== |
|||
<syntaxhighlight 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))</syntaxhighlight> |
|||
{{works with|Core|v0.9.116.03+91}} |
|||
<syntaxhighlight 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))</syntaxhighlight> |
|||
{{out}} |
|||
<pre># test ();; |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|Oforth}}== |
|||
<syntaxhighlight 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 ; </syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
[ [], ["ABC"], ["ABC", "DEF"], ["ABC", "DEF", "G", "H"] ] map(#quibbing) . |
|||
[{}, {ABC}, {ABC and DEF}, {ABC, DEF, G and H}] |
|||
</pre> |
|||
=={{header|Ol}}== |
|||
<syntaxhighlight 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") |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|PARI/GP}}== |
|||
<syntaxhighlight 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"])</syntaxhighlight> |
|||
Output: |
|||
<pre>%1 = "{}" |
|||
%2 = "{ABC}" |
|||
%3 = "{ABC and DEF}" |
|||
%4 = "{ABC, DEF, G and H}"</pre> |
|||
=={{header|Pascal}}== |
|||
<syntaxhighlight 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.</syntaxhighlight> |
|||
Output: |
|||
<Pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</Pre> |
|||
=={{header|Perl}}== |
|||
{{trans|Raku}} |
|||
<syntaxhighlight lang="perl">sub comma_quibbling :prototype(@) { |
|||
return "{$_}" for |
|||
@_ < 2 ? "@_" : |
|||
join(', ', @_[0..@_-2]) . ' and ' . $_[-1]; |
|||
} |
|||
print comma_quibbling(@$_), "\n" for |
|||
[], [qw(ABC)], [qw(ABC DEF)], [qw(ABC DEF G H)];</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</pre> |
|||
'''Perl 5.01 version and other approach:''' |
|||
<syntaxhighlight 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)];</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|Phix}}== |
|||
<!--<syntaxhighlight lang="phix">(phixonline)--> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">quibble</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">words</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">words</span><span style="color: #0000FF;">)>=</span><span style="color: #000000;">2</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #000000;">words</span><span style="color: #0000FF;">[-</span><span style="color: #000000;">2</span><span style="color: #0000FF;">..-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">words</span><span style="color: #0000FF;">[-</span><span style="color: #000000;">2</span><span style="color: #0000FF;">]&</span><span style="color: #008000;">" and "</span><span style="color: #0000FF;">&</span><span style="color: #000000;">words</span><span style="color: #0000FF;">[-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]}</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #008000;">"{"</span><span style="color: #0000FF;">&</span><span style="color: #7060A8;">join</span><span style="color: #0000FF;">(</span><span style="color: #000000;">words</span><span style="color: #0000FF;">,</span><span style="color: #008000;">", "</span><span style="color: #0000FF;">)&</span><span style="color: #008000;">"}"</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #008080;">constant</span> <span style="color: #000000;">tests</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{{},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ABC"</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ABC"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"DEF"</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ABC"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"DEF"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"G"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"H"</span><span style="color: #0000FF;">}}</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">tests</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #0000FF;">?</span><span style="color: #000000;">quibble</span><span style="color: #0000FF;">(</span><span style="color: #000000;">tests</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">])</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<!--</syntaxhighlight>--> |
|||
{{out}} |
|||
<pre> |
|||
"{}" |
|||
"{ABC}" |
|||
"{ABC and DEF}" |
|||
"{ABC, DEF, G and H}" |
|||
</pre> |
|||
=={{header|PHP}}== |
|||
<syntaxhighlight lang="php"><?php |
|||
function quibble($arr) { |
|||
switch (count($arr)) { |
|||
case 0: |
|||
return '{}'; |
|||
case 1: |
|||
return "{{$arr[0]}}"; |
|||
default: |
|||
$left = implode(', ', array_slice($arr, 0, -1)); |
|||
$right = array_slice($arr, -1)[0]; |
|||
return "{{$left} and {$right}}"; |
|||
} |
|||
} |
|||
$tests = [ |
|||
[], |
|||
["ABC"], |
|||
["ABC", "DEF"], |
|||
["ABC", "DEF", "G", "H"] |
|||
]; |
|||
foreach ($tests as $test) { |
|||
echo quibble($test) . PHP_EOL; |
|||
} |
|||
?></syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|PicoLisp}}== |
|||
<syntaxhighlight lang="picolisp">(for L '([] ["ABC"] ["ABC", "DEF"] ["ABC", "DEF", "G", "H"]) |
|||
(let H (head -1 L) |
|||
(prinl |
|||
"{" |
|||
(glue ", " H) |
|||
(and H " and ") |
|||
(last L) |
|||
"}" ) ) )</syntaxhighlight> |
|||
Output: |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|PL/I}}== |
|||
<syntaxhighlight 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;</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|PL/M}}== |
|||
<syntaxhighlight 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</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|Plain English}}== |
|||
<syntaxhighlight 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.</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|PowerShell}}== |
|||
<syntaxhighlight 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 + "}" |
|||
} |
|||
</syntaxhighlight> |
|||
<syntaxhighlight lang="powershell"> |
|||
Out-Quibble |
|||
Out-Quibble "ABC" |
|||
Out-Quibble "ABC", "DEF" |
|||
Out-Quibble "ABC", "DEF", "G", "H" |
|||
</syntaxhighlight> |
|||
{{Out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
What it might look like when working with a file: |
|||
<syntaxhighlight lang="powershell"> |
|||
$file = @' |
|||
ABC |
|||
ABC, DEF |
|||
ABC, DEF, G, H |
|||
'@ -split [Environment]::NewLine |
|||
foreach ($line in $file) |
|||
{ |
|||
Out-Quibble -Text ($line -split ", ") |
|||
} |
|||
</syntaxhighlight> |
|||
{{Out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|Prolog}}== |
|||
{{works with|SWI-Prolog|7.1}} |
|||
<syntaxhighlight 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])) |
|||
).</syntaxhighlight> |
|||
{{Out}} |
|||
<syntaxhighlight lang="prolog">?- test. |
|||
[] => {} |
|||
[ABC] => {ABC} |
|||
[ABC,DEF] => {ABC and DEF} |
|||
[ABC,DEF,G,H] => {ABC, DEF, G and H} |
|||
true.</syntaxhighlight> |
|||
=={{header|PureBasic}}== |
|||
<syntaxhighlight 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 |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|Python}}== |
=={{header|Python}}== |
||
===Python: Replace() whilst reversed=== |
|||
<lang python>>>> def strcat(sequence): |
|||
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. |
|||
<syntaxhighlight lang="python">>>> def strcat(sequence): |
|||
return '{%s}' % ', '.join(sequence)[::-1].replace(',', 'dna ', 1)[::-1] |
|||
return '{%s}' % ', '.join(sequence)[::-1].replace(',', 'dna ', 1)[::-1] |
|||
>>> for seq in ([], ["ABC"], ["ABC", "DEF"], ["ABC", "DEF", "G", "H"]): |
>>> for seq in ([], ["ABC"], ["ABC", "DEF"], ["ABC", "DEF", "G", "H"]): |
||
print('Input: %-24r -> Output: %r' % (seq, strcat(seq))) |
|||
Line 71: | Line 3,867: | ||
Input: ['ABC', 'DEF'] -> Output: '{ABC and DEF}' |
Input: ['ABC', 'DEF'] -> Output: '{ABC and DEF}' |
||
Input: ['ABC', 'DEF', 'G', 'H'] -> Output: '{ABC, DEF, G and H}' |
Input: ['ABC', 'DEF', 'G', 'H'] -> Output: '{ABC, DEF, G and H}' |
||
>>> </ |
>>> </syntaxhighlight> |
||
===Python: Counted replacement=== |
|||
(Possible){{trans|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: |
|||
<syntaxhighlight 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)))</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Input: [] -> Output: '{}' |
|||
Input: ['ABC'] -> Output: '{ABC}' |
|||
Input: ['ABC', 'DEF'] -> Output: '{ABC and DEF}' |
|||
Input: ['ABC', 'DEF', 'G', 'H'] -> Output: '{ABC, DEF, G and H}'</pre> |
|||
===Python: Functional=== |
|||
<syntaxhighlight 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}' |
|||
>>> </syntaxhighlight> |
|||
=={{header|Quackery}}== |
|||
<syntaxhighlight 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$</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|R}}== |
|||
<syntaxhighlight lang="rsplus">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) |
|||
allButLastWord <- if(len >= 2) paste0(vect[seq_len(len - 1)], collapse = ", ") else "" |
|||
paste0("{", if(nchar(allButLastWord) == 0) vect else paste0(allButLastWord, " and ", vect[len]), "}") |
|||
} |
|||
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", ""))</syntaxhighlight> |
|||
{{out}} |
|||
<pre>> 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}"</pre> |
|||
=={{header|Racket}}== |
|||
<syntaxhighlight 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)))</syntaxhighlight> |
|||
{{out}} |
|||
<pre>() -> {} |
|||
("ABC") -> {ABC} |
|||
("ABC" "DEF") -> {ABC and DEF} |
|||
("ABC" "DEF" "G" "H") -> {ABC, DEF, G and H}</pre> |
|||
=={{header|Raku}}== |
|||
(formerly Perl 6) |
|||
<syntaxhighlight lang="raku" line>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>];</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|REBOL}}== |
|||
===Straightforward implementation=== |
|||
<syntaxhighlight 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] |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</pre> |
|||
===Alternative (more efficient) version with oxford comma switch=== |
|||
<syntaxhighlight 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] |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
Now with Oxford comma |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G, and H}</pre> |
|||
=={{header|Refal}}== |
|||
<syntaxhighlight lang="refal">$ENTRY Go { |
|||
= <Prout <Quibble>> |
|||
<Prout <Quibble ('ABC')>> |
|||
<Prout <Quibble ('ABC') ('DEF')>> |
|||
<Prout <Quibble ('ABC') ('DEF') ('G') ('H')>>; |
|||
}; |
|||
Quibble { |
|||
e.X = '{' <Quibble1 e.X> '}'; |
|||
}; |
|||
Quibble1 { |
|||
= ; |
|||
(e.Word) = e.Word; |
|||
(e.Word1) (e.Word2) = e.Word1 ' and ' e.Word2; |
|||
(e.Word) e.Words = e.Word ', ' <Quibble1 e.Words>; |
|||
};</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|REXX}}== |
|||
===version 1:=== |
|||
<syntaxhighlight 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'}'</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC,DEF,G and H} |
|||
</pre> |
|||
===version 2:=== |
|||
<syntaxhighlight 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'}'</syntaxhighlight> |
|||
{{output}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
===version 3:=== |
|||
{{trans|NetRexx}} |
|||
<syntaxhighlight 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'}'</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
[] : {} |
|||
["ABC"] : {ABC} |
|||
["ABC", 'DEF'] : {ABC and DEF} |
|||
[ABC, DEF, G, H] : {ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|Ring}}== |
|||
<syntaxhighlight lang="ring"> |
|||
# 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 |
|||
</syntaxhighlight> |
|||
Output: |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|RPL}}== |
|||
Whilst it is always important to ensure overall code clarity for maintenance by using explicit variable names and structured control flows, the spirit of reverse Polish notation programming is to optimize some key part of the algorithm in order to make it as compact - and possibly as fast - as possible, whatever the induced loss of readability. Here, two <code>IFTE</code> low-level instructions are nested to deliver the appropriate liaison substring between two words or before the first word. |
|||
{{works with|Halcyon Calc|4.2.7}} |
|||
≪ DUP SIZE → words n |
|||
≪ "{" 1 |
|||
'''WHILE''' DUP n ≤ '''REPEAT''' |
|||
DUP 1 ≠ OVER n ≠ ", " " and " '''IFTE''' "" '''IFTE''' |
|||
ROT SWAP + |
|||
words 3 PICK GET + |
|||
SWAP 1 + |
|||
'''END''' |
|||
DROP "}" + |
|||
≫ ≫ 'CMAQBL' STO |
|||
{{in}} |
|||
<pre> |
|||
{ } CMAQBL |
|||
{ "ABC" } CMAQBL |
|||
{ "ABC", "DEF" } CMAQBL |
|||
{ "ABC", "DEF", "G", "H" } CMAQBL |
|||
</pre> |
|||
{{out}} |
|||
<pre> |
|||
4: "{}" |
|||
3: "{ABC}" |
|||
2: "{ABC and DEF}" |
|||
1: "{ABC, DEF, G and H}" |
|||
</pre> |
|||
=={{header|Ruby}}== |
|||
{{trans|Raku}} |
|||
<syntaxhighlight 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</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|Run BASIC}}== |
|||
<syntaxhighlight 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</syntaxhighlight> |
|||
{{out}} |
|||
<pre>[] ==> {} |
|||
["ABC"] ==> {"ABC"} |
|||
["ABC", "DEF"] ==> {"ABC" and "DEF"} |
|||
["ABC", "DEF", "G", "H"] ==> {"ABC", "DEF", "G" and "H"}</pre> |
|||
=={{header|Rust}}== |
|||
<syntaxhighlight 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"])); |
|||
} |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|Scala}}== |
|||
<syntaxhighlight 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") ) ) |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|Scheme}}== |
|||
<syntaxhighlight 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") |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|Sed}}== |
|||
script-file: |
|||
<syntaxhighlight lang="sed">s/#.*$//g |
|||
y/[/{/ |
|||
y/]/}/ |
|||
s/"//g |
|||
s/ [A-Z][A-Z]*}/ and&/g |
|||
s/, and/ and/</syntaxhighlight> |
|||
test.txt: |
|||
<syntaxhighlight lang="text">[] # (No input words). |
|||
["ABC"] |
|||
["ABC", "DEF"] |
|||
["ABC", "DEF", "G", "H"]</syntaxhighlight> |
|||
sed -f script-file test.txt |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|Seed7}}== |
|||
<syntaxhighlight 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;</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|SenseTalk}}== |
|||
<syntaxhighlight 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 |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|SETL}}== |
|||
<syntaxhighlight lang="setl">program comma_quibbling; |
|||
tests := [ |
|||
[], |
|||
["ABC"], |
|||
["ABC","DEF"], |
|||
["ABC","DEF","G","H"] |
|||
]; |
|||
loop for t in tests do |
|||
print(t, "=", quibble(t)); |
|||
end loop; |
|||
proc quibble(words); |
|||
ret := '{'; |
|||
loop while words /= [] do |
|||
word fromb words; |
|||
ret +:= word; |
|||
case of |
|||
(#words = 1): |
|||
ret +:= " and "; |
|||
(#words > 1): |
|||
ret +:= ", "; |
|||
end case; |
|||
end loop; |
|||
return ret + '}'; |
|||
end proc; |
|||
end program;</syntaxhighlight> |
|||
{{out}} |
|||
<pre>[] = {} |
|||
[ABC] = {ABC} |
|||
[ABC DEF] = {ABC and DEF} |
|||
[ABC DEF G H] = {ABC, DEF, G and H}</pre> |
|||
=={{header|Sidef}}== |
|||
<syntaxhighlight lang="ruby">func comma_quibbling(words) { |
|||
'{' + ([words.first(-1).join(', ')]-[''] + [words.last] -> join(' and ')) + '}' |
|||
} |
|||
[<>, <ABC>, <ABC DEF>, <ABC DEF G H>].each { |w| |
|||
say comma_quibbling(w) |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|Standard ML}}== |
|||
<syntaxhighlight 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}" |
|||
</syntaxhighlight> |
|||
=={{header|Swift}}== |
|||
<syntaxhighlight 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) |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|Tcl}}== |
|||
<syntaxhighlight 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] |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|TXR}}== |
|||
<syntaxhighlight lang="txrlisp">(defun quib (list) |
|||
(tree-bind (: last . lead) (reverse list) |
|||
`{@{(nreverse lead) ", "}@(if lead " and ")@last}`))</syntaxhighlight> |
|||
=={{header|Uiua}}== |
|||
{{works with|Uiua|0.10.0-dev.1}} |
|||
<syntaxhighlight lang="Uiua"> |
|||
Tests ← {{} |
|||
{"ABC"} |
|||
{"ABC" "DEF"} |
|||
{"ABC" "DEF" "G" "H"}} |
|||
Jam ← ◇⊂◇⊂:□ |
|||
Quibble ← |1 ⟨""◌|°□⊢|Jam" and "°⊟|Quibble⊂⊃(□Jam", "°⊟↙2)(↘2)⟩↧3⧻. |
|||
Wrap ← ⊂⊂"{" : "}" Quibble |
|||
⍚(&pWrap) Tests |
|||
</syntaxhighlight> |
|||
=={{header|UNIX Shell}}== |
|||
{{works with|Bourne Again SHell}} |
|||
{{works with|Korn Shell}} |
|||
{{works with|Z Shell}} |
|||
<syntaxhighlight lang="bash"> |
|||
quibble() { |
|||
printf '{' |
|||
while (( $# > 2 )); do |
|||
printf '%s, ' "$1" |
|||
shift |
|||
done |
|||
if (( $# )); then |
|||
printf '%s' "$1" |
|||
shift |
|||
fi |
|||
if (( $# )); then |
|||
printf ' and %s' "$1" |
|||
fi |
|||
printf '%s\n' '}' |
|||
}</syntaxhighlight> |
|||
With a slight modification, it will work in any POSIX shell, or even older Bourne-compatible shells as long as they have functions and <b>printf</b>: |
|||
<syntaxhighlight lang="sh"> |
|||
quibble() { |
|||
printf '{' |
|||
while [ $# -gt 2 ]; do |
|||
printf '%s, ' "$1" |
|||
shift |
|||
done |
|||
if [ $# -gt 0 ]; then |
|||
printf '%s' "$1" |
|||
shift |
|||
fi |
|||
if [ $# -gt 0 ]; then |
|||
printf ' and %s' "$1" |
|||
fi |
|||
printf '%s\n' '}' |
|||
}</syntaxhighlight> |
|||
Going the other way, Zsh-specific code can be more compact: |
|||
{{works with|Z Shell}} |
|||
<syntaxhighlight lang="zsh">quibble() { |
|||
printf '{' |
|||
if (( $# > 1 )) printf '%s and ' ${(j:, :)@[1,-2]} |
|||
printf '%s}\n' $@[-1] |
|||
}</syntaxhighlight> |
|||
The test code is the same either way: |
|||
<syntaxhighlight lang="sh"> |
|||
quibble |
|||
quibble ABC |
|||
quibble ABC DEF |
|||
quibble ABC DEF G H</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|VBA}}== |
|||
<syntaxhighlight 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</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
{ABC, DEF, G, H, IJKLM, NO and PQRSTUV}</pre> |
|||
=={{header|VBScript}}== |
|||
<syntaxhighlight 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</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|Visual Basic .NET}}== |
|||
FormatEnumerable() accepts an IEnumerable(Of String), as per Lippert's original specification. FormatArray() contains an alternative implementation for String(). |
|||
<syntaxhighlight 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 |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{} |
|||
{} |
|||
{ABC} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
{ABC, DEF, G and H}</pre> |
|||
=={{header|V (Vlang)}}== |
|||
{{trans|go}} |
|||
<syntaxhighlight lang="v (vlang)">fn q(s []string) string { |
|||
match s.len { |
|||
0 { |
|||
return '{}' |
|||
} |
|||
1 { |
|||
return '{${s[0]}}' |
|||
} |
|||
2 { |
|||
return '{${s[0]} and ${s[1]}}' |
|||
} |
|||
else{ |
|||
return '{${s[0..s.len-1].join(', ')} and ${s[s.len-1]}}' |
|||
} |
|||
} |
|||
} |
|||
fn main(){ |
|||
println(q([])) |
|||
println(q(['ABC'])) |
|||
println(q(['ABC','DEF'])) |
|||
println(q(['ABC','DEF','G','H'])) |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|Wren}}== |
|||
<syntaxhighlight lang="wren">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))</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|XBS}}== |
|||
<syntaxhighlight lang="text">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"]));</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF and GHI} |
|||
</pre> |
|||
=={{header|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 <tt>OXFORD-COMMA</tt> parameter: pass a true value, and you won't find yourself saying things like "I want to thank my parents, Ayn Rand and God". |
|||
<syntaxhighlight 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))</syntaxhighlight> |
|||
{{out}} |
|||
<pre>"{}" |
|||
"{ABC}" |
|||
"{ABC and DEF}" |
|||
"{ABC, DEF, G and H}" |
|||
"{}" |
|||
"{ABC}" |
|||
"{ABC and DEF}" |
|||
"{ABC, DEF, G, and H}"</pre> |
|||
=={{header|XPL0}}== |
|||
<syntaxhighlight 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)] |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
{} |
|||
{ABC} |
|||
{ABC and DEF} |
|||
{ABC, DEF, G and H} |
|||
</pre> |
|||
=={{header|zkl}}== |
|||
This is a cheese ball solution that replies on no commas in the inputs |
|||
<syntaxhighlight 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,*]) |
|||
} |
|||
}</syntaxhighlight> |
|||
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,...) |
|||
{{out}} |
|||
<pre> |
|||
quib(List) //-->"{}" |
|||
quib(L("ABC")) //-->"{ABC}" |
|||
quib(L("ABC", "DEF")) //-->"{ABC and DEF}" |
|||
quib(L("ABC", "DEF", "G", "H")) //-->"{ABC, DEF, G and H}" |
|||
</pre> |
|||
=={{header|ZX Spectrum Basic}}== |
|||
<syntaxhighlight 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</syntaxhighlight> |
Revision as of 20:00, 2 July 2024
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:
- An input of no words produces the output string of just the two brace characters "{}".
- An input of just one word, e.g. ["ABC"], produces the output string of the word inside the two braces, e.g. "{ABC}".
- 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}".
- 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
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’]))
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
360 Assembly
* 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
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
8080 Assembly
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:
- Output:
A>quib {} {ABC} {ABC and DEF} {ABC, DEF, G and H}
ABC
HOW TO RETURN quibble words:
PUT "" IN result
PUT #words IN remaining
FOR word IN words:
PUT result^word IN result
PUT remaining-1 IN remaining
IF remaining = 1: PUT result^" and " IN result
IF remaining > 1: PUT result^", " IN result
RETURN "{" ^ result ^ "}"
PUT {} IN tests
INSERT {} IN tests
INSERT {[1]: "ABC"} IN tests
INSERT {[1]: "ABC"; [2]: "DEF"} IN tests
INSERT {[1]: "ABC"; [2]: "DEF"; [3]: "G"; [4]: "H"} IN tests
FOR test IN tests:
WRITE quibble test/
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
Acornsoft Lisp
There's no string data type; symbols are used instead. The implode
function is used to concatenate a list of symbols. When writing a symbol in source code, exclamation mark is an escape character that allows characters such as spaces and exclamation marks to be treated as part of the symbol's name.
(defun examples ()
(map '(lambda (words) (printc (quibble words)))
'(() (ABC) (ABC DEF) (ABC DEF G H))))
(defun quibble (words)
(implode (list '{ (quibbles words) '})))
(defun quibbles (words)
(implode (conjunction words)))
(defun conjunction (words)
(cond ((null words)
'())
((null (cdr words))
words)
((null (cddr words))
(list (car words) '! and! (cadr words)))
(t
(cons (car words)
(cons ',! (conjunction (cdr words)))))))
- Output:
Calling (examples)
will output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
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
- Output:
Screenshot from Atari 8-bit computer
() (ABC) (ABC and DEF) (ABC, DEF and G) (ABC, DEF, G and H)
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}
ALGOL 68
# returns a string ( assumed to be of space-separated words ) with the words #
# separated by ", ", except for the last which is separated from the rest by #
# " and ". The list is enclosed by braces #
PROC to list = ( STRING words ) STRING:
BEGIN
# count the number of words #
INT word count := 0;
BOOL in word := FALSE;
FOR char pos FROM LWB words TO UPB words
DO
IF NOT is upper( words[ char pos ] )
THEN
# not an upper-case letter, possibly a word has been ended #
in word := FALSE
ELSE
# not a delimitor, possibly the start of a word #
IF NOT in word
THEN
# we are starting a new word #
word count +:= 1;
in word := TRUE
FI
FI
OD;
# format the result #
STRING result := "{";
in word := FALSE;
INT word number := 0;
FOR char pos FROM LWB words TO UPB words
DO
IF NOT is upper( words[ char pos ] )
THEN
# not an upper-case letter, possibly a word has been ended #
in word := FALSE
ELSE
# not a delimitor, possibly the start of a word #
IF NOT in word
THEN
# we are starting a new word #
word number +:= 1;
in word := TRUE;
IF word number > 1
THEN
# second or subsequent word - need a separator #
result +:= IF word number = word count
THEN # final word #
" and "
ELSE # non-final word #
", "
FI
FI
FI;
# add the character to the result #
result +:= words[ char pos ]
FI
OD;
result + "}"
END # to list # ;
# procedure to test the to list PROC #
PROC test to list = ( STRING words ) VOID:
print( ( ( words
+ ": "
+ to list( words )
)
, newline
)
);
# test the to list PROC #
test to list( "" );
test to list( "ABC" );
test to list( "ABC DEF" );
test to list( "ABC DEF G H" )
- Output:
: {} ABC: {ABC} ABC DEF: {ABC and DEF} ABC DEF G H: {ABC, DEF, G and H}
ALGOL W
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.
- Output:
: {} ABC : {ABC} ABC DEF : {ABC and DEF} ABC DEF G H : {ABC, DEF, G and H}
APL
quibble ← 1⌽'}{',(∊⊢,¨2↓(' and ' ''),⍨(⊂', ')⍴⍨⍴)
- Output:
quibble ⍬ {} quibble ⊂'ABC' {ABC} quibble 'ABC' 'DEF' {ABC and DEF} quibble 'ABC' 'DEF' 'G' 'H' {ABC, DEF, G and H}
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|
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H} {One, two, three and four} {Me, myself and I} {Jack and Jill} {Loner}
Arturo
quibble: $[sequence :block][
if? 0 = size sequence
-> return "{}"
if? 1 = size sequence
-> return ~"{|sequence\0|}"
last: pop 'sequence
return ~« {|join.with: ", " sequence| and |last|}
]
sentences: [
[]
["ABC"]
["ABC" "DEF"]
["ABC" "DEF" "G" "H"]
]
loop sentences 'sentence [
print quibble sentence
]
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
Astro
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)
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}
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}
Batch File
@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
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H} Press any key to continue . . .
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))
$)
- Output:
{} {"ABC"} {"ABC" and "DEF"} {"ABC", "DEF", "G" and "H"}
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))
);
- Output:
L1 {} L2 {ABC} L3 {ABC and DEF} L4 {ABC, DEF, G and H}
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}
C#
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
}
}
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H} < Press Any Key >
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}
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}
CLU
quibble = proc (words: array[string]) returns (string)
out: string := "{"
last: int := array[string]$high(words)
for i: int in array[string]$indexes(words) do
out := out || words[i]
if i < last-1 then
out := out || ", "
elseif i = last-1 then
out := out || " and "
end
end
return(out || "}")
end quibble
start_up = proc ()
as = array[string]
aas = array[as]
po: stream := stream$primary_output()
testcases: aas := aas$
[as$[],
as$["ABC"],
as$["ABC","DEF"],
as$["ABC","DEF","G","H"]]
for testcase: as in aas$elements(testcases) do
stream$putl(po, quibble(testcase))
end
end start_up
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
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.
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF and G} {ABC, DEF, G and H}
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}
Commodore BASIC
The Commodore character set has no curly braces, so I substituted square brackets. The solution could no doubt be improved, but some of the elegance of other solutions is not possible simply because a FOR loop always executes at least once, even if its parameters would seem to indicate that it should not.
100 DIM A$(3)
110 FOR TC=1 TO 4
120 : READ A: IF A=0 THEN 160
130 : FOR I=0 TO A-1
140 : READ A$(I)
150 : NEXT I
160 : GOSUB 200
170 : PRINT CQ$
180 NEXT TC
190 END
200 CQ$="["
210 IF A < 1 THEN 290
220 CQ$ = CQ$ + A$(0)
230 IF A < 2 THEN 290
240 IF A < 3 THEN 280
250 FOR I=1 TO A - 2
260 : CQ$ = CQ$ + ", " + A$(I)
270 NEXT I
280 CQ$ = CQ$ + " AND " + A$(A - 1)
290 CQ$ = CQ$ + "]"
300 RETURN
310 DATA 0
320 DATA 1, ABC
330 DATA 2, ABC, DEF
340 DATA 4, ABC, DEF, G, H
- Output:
[] [ABC] [ABC AND DEF] [ABC, DEF, G AND H]
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}
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();
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
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}
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}"]
dc
[(q)uibble: main entry point. print brackets, calling n in between if stack not
empty.]sx
[ [{]n z 0 !=n [}]pR ]sq
[(n)onempty: if more than 1 item, call m. then print top of stack.]sx
[ z 1 !=m n ]sn
[(m)ore: call f to flip stack into r register, call p to print most of it,
then pop the last item back onto the main stack so it's there to be printed
after we return]sx
[ lfx lpx Lr ]sm
[(f)lip: utility routine to reverse the stack into the r register]sx
[ Sr z 0 !=f ]sf
[(p)rint: get next item from stack in r register and print it. If there are
more than 2 items left on the register stack (which never drops below one
item), print a comma (c) and recurse. If there are exactly two items left,
print " and " (a) and return.]sx
[ Lr n 2 yr >c 2 yr =a 2 yr >p]sp
[(c)omma: utility routine to print a comma followed by a space]sx
[ [, ]n ]sc
[(a)and: utility routine to print " and "]sx
[ [ and ]n ]sa
[run tests]sx
lqx
[ABC] lqx
[ABC] [DEF] lqx
[ABC] [DEF] [G] [H] lqx
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
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
{}out}}
$ @comma_quibbling {} {ABC} {ABC and DEF} {ABC, DEF, G and H}
Delphi
See Pascal.
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}"
EasyLang
func$ tolist s$ .
s$[] = strsplit s$ " "
r$ = "{"
n = len s$[]
for i = 1 to n - 2
r$ &= s$[i] & ", "
.
if n > 0
if n > 1
r$ &= s$[n - 1] & " and "
.
r$ &= s$[n]
.
r$ &= "}"
return r$
.
print tolist ""
print tolist "ABC"
print tolist "ABC DEF"
print tolist "ABC DEF G H"
EchoLisp
(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 }"
ed
Uses basic regular expressions (BREs).
# by Artyom Bologov
H
,p
g/.*/s/ /, /g
g/[,]/s/,\([^,]*\)$/ and\1/
g/.*/s//{&}/
,p
Q
- Output:
$ ed -s comma-quibling.input < comma-quibling.ed Newline appended ABC ABC DEF ABC DEF G H {} {ABC} {ABC and DEF} {ABC, DEF, G and H}
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
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
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)
- Output:
"{}" "{ABC}" "{ABC and DEF}" "{ABC, DEF, G and H}"
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}"]
F#
One Way
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}"
or Another
The Function
let quibble quibbler quibblee = Seq.zip quibblee quibbler //Sorry, just too good a line to miss, back in my Latin classes
The Task
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"}"
- 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.
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
- 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.
: 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
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.
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.
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
' 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
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
Frink
quibble[enum] :=
{
list = toArray[enum] // This makes it work on any enumerating expression
size = length[list]
if size >= 2
return "{" + join[", ", first[list, size-1]] + " and " + last[list] + "}"
else
return "{" + join["", list] + "}"
}
data = [[], ["ABC"], ["ABC", "DEF"], ["ABC", "DEF", "G", "H"]]
for line = data
println[quibble[line]]
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
FutureBasic
Long solution:
include "NSLog.incl"
local fn CommaQuibber( string as CFStringRef ) as CFStringRef
CFStringRef tempStr
NSUInteger i
tempStr = fn StringByReplacingOccurrencesOfString( string, @"[", @"" )
tempStr = fn StringByReplacingOccurrencesOfString( tempStr, @"]", @"" )
tempStr = fn StringByReplacingOccurrencesOfString( tempStr, @" ", @"" )
tempStr = fn StringByReplacingOccurrencesOfString( tempStr, @"\"", @"" )
CFMutableStringRef quibStr = fn MutableStringWithCapacity(0)
CFArrayRef arr = fn StringComponentsSeparatedByString( tempStr, @"," )
NSUInteger count = len(arr)
select switch ( count )
case 0 : MutableStringSetString( quibStr, @"{}" ) : break
case 1 : MutableStringSetString( quibStr, fn StringWithFormat( @"{%@}", arr[0] ) ) : break
case 2 : MutableStringSetString( quibStr, fn StringWithFormat( @"{%@ and %@}", arr[0], arr[1] ) ) : break
case else
MutableStringAppendFormat( quibStr, @"{" )
for i = 0 to count -1
if ( i != count -1 )
MutableStringAppendFormat( quibStr, @"%@, ", arr[i] )
else
MutableStringAppendFormat( quibStr, @"and %@}", arr[i] )
end if
next
end select
end fn = quibStr
NSLog( @"%@", fn CommaQuibber( @"[]" ) )
NSLog( @"%@", fn CommaQuibber( @"[\"ABC\"]" ) )
NSLog( @"%@", fn CommaQuibber( @"[\"ABC\", \"DEF\"]" ) )
NSLog( @"%@", fn CommaQuibber( @"[\"ABC\", \"DEF\", \"G\", \"H\"]" ) )
HandleEvents
Short solution:
local fn CommaQuibbler( s as CFArrayRef ) as CFStringRef
CFStringRef result = NULL
select ( len(s) )
case 0: exit fn = @"{}"
case 1: exit fn = fn StringWithFormat( @"{%@}", s[0] )
case 2: exit fn = fn StringWithFormat( @"{%@ and %@}", s[0], s[1] )
case else
result = fn StringWithFormat( @"{%@}", fn ArrayComponentsJoinedByString( s, @", " ) )
CFRange lastComma = fn StringRangeOfStringWithOptions( result, @",", NSBackwardsSearch )
result = fn StringByReplacingCharactersInRange( result, lastComma, @" and" )
end select
end fn = result
print fn CommaQuibbler( @[] )
print fn CommaQuibbler( @[@"ABC"] )
print fn CommaQuibbler( @[@"ABC", @"DEF"] )
print fn CommaQuibbler( @[@"ABC", @"DEF", @"G", @"H"] )
HandleEvents
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G, and H}
Gambas
Click this link to run this code
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
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.
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}
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}
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}
Or, defining just two cases, and drawing more on standard libraries than on hand-crafted pattern-matching and recursion:
import Data.List (intercalate)
--------------------- COMMA QUIBBLING --------------------
quibble :: [String] -> String
quibble ws@(_ : _ : _) =
intercalate
" and "
( [intercalate ", " . reverse . tail, head]
<*> [reverse ws]
)
quibble xs = concat xs
--------------------------- TEST -------------------------
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"
]
)
- 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:
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} ->
J
quibLast2=: ' and ' joinstring (2 -@<. #) {. ]
withoutLast2=: ([: # _2&}.) {. ]
quibble=: '{', '}' ,~ ', ' joinstring withoutLast2 , <@quibLast2
Testing:
Tests=: (<<<3){(i.5)<@{."0 1;:'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)
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}
JavaScript
ES5
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}
ES6
Composing from a set of generic functions:
(() => {
'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();
})();
- 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:
function quibble(words) {
var words2 = words.join()
var words3 = [...words2].reverse().join('');
var res = words3.replace(",", " dna ");
var words4 = [...res].reverse().join('');
return '{'+words4+'}';
}
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}
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"])
- Output:
quibble([]) = "{}" quibble(["ABC"]) = "{ABC}" quibble(["ABC", "DEF"]) = "{ABC and DEF}" quibble(["ABC", "DEF", "G", "H"]) = "{ABC, DEF, G and H}"
Kotlin
// 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)}")
}
- Output:
[] -> {} ["ABC"] -> {ABC} ["ABC", "DEF"] -> {ABC and DEF} ["ABC", "DEF", "G", "H"] -> {ABC, DEF, G and H}
Lang
fp.quibble = (&words) -> {
$len $= @&words
$output = \{\e
$i
repeat($[i], $len) {
$output += &words[$i] ||| ($i == -|$len?\e:($i == $len - 2?\sand\s:\,\s))
}
$output += \}\e
return $output
}
fn.println(fp.quibble(fn.arrayOf()))
fn.println(fp.quibble(fn.arrayOf(ABC)))
fn.println(fp.quibble(fn.arrayOf(ABC, DEF)))
fn.println(fp.quibble(fn.arrayOf(ABC, DEF, G, H)))
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
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}
Liberty BASIC
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.
- 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}
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
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
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
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
M2000 Interpreter
Using string as argument
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
Using String functions only
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
Using array as argument
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
- Output:
{} {ABC} {ABC, DEF} {ABC, DEF, G and H}
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}"
Mathematica / Wolfram Language
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}
MATLAB / Octave
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;
- 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
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}"
Miranda
main :: [sys_message]
main = [Stdout (show test ++ ": {" ++ quibble test ++ "}\n") | test <- tests]
tests :: [[[char]]]
tests = [ [],
["ABC"],
["ABC","DEF"],
["ABC","DEF","G","H"] ]
quibble :: [[char]]->[char]
quibble [] = []
quibble [word] = word
quibble [word1,word2] = word1 ++ " and " ++ word2
quibble (word:words) = word ++ ", " ++ quibble words
- Output:
[]: {} ["ABC"]: {ABC} ["ABC","DEF"]: {ABC and DEF} ["ABC","DEF","G","H"]: {ABC, DEF, G and H}
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}
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)
Oberon-2
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.
- 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
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();
}
}
Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
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))
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))
- Output:
# test ();; {} {ABC} {ABC and DEF} {ABC, DEF, G and H}
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 ;
- Output:
[ [], ["ABC"], ["ABC", "DEF"], ["ABC", "DEF", "G", "H"] ] map(#quibbing) . [{}, {ABC}, {ABC and DEF}, {ABC, DEF, G and H}]
Ol
(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")
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
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}"
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.
Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H} {} {ABC} {ABC and DEF} {ABC, DEF, G and H}
Perl
sub comma_quibbling :prototype(@) {
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}
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
<?php
function quibble($arr) {
switch (count($arr)) {
case 0:
return '{}';
case 1:
return "{{$arr[0]}}";
default:
$left = implode(', ', array_slice($arr, 0, -1));
$right = array_slice($arr, -1)[0];
return "{{$left} and {$right}}";
}
}
$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}
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}
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}
PL/M
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
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
Plain English
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.
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
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 + "}"
}
Out-Quibble
Out-Quibble "ABC"
Out-Quibble "ABC", "DEF"
Out-Quibble "ABC", "DEF", "G", "H"
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
What it might look like when working with a file:
$file = @'
ABC
ABC, DEF
ABC, DEF, G, H
'@ -split [Environment]::NewLine
foreach ($line in $file)
{
Out-Quibble -Text ($line -split ", ")
}
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
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]))
).
- Output:
?- test.
[] => {}
[ABC] => {ABC}
[ABC,DEF] => {ABC and DEF}
[ABC,DEF,G,H] => {ABC, DEF, G and H}
true.
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
- 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.
>>> 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}'
>>>
Python: Counted replacement
(Possible)
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}'
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}'
>>>
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$
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
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)
allButLastWord <- if(len >= 2) paste0(vect[seq_len(len - 1)], collapse = ", ") else ""
paste0("{", if(nchar(allButLastWord) == 0) vect else paste0(allButLastWord, " and ", vect[len]), "}")
}
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", ""))
- 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
(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}
Raku
(formerly 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}
REBOL
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}
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}
Refal
$ENTRY Go {
= <Prout <Quibble>>
<Prout <Quibble ('ABC')>>
<Prout <Quibble ('ABC') ('DEF')>>
<Prout <Quibble ('ABC') ('DEF') ('G') ('H')>>;
};
Quibble {
e.X = '{' <Quibble1 e.X> '}';
};
Quibble1 {
= ;
(e.Word) = e.Word;
(e.Word1) (e.Word2) = e.Word1 ' and ' e.Word2;
(e.Word) e.Words = e.Word ', ' <Quibble1 e.Words>;
};
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
REXX
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}
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}
version 3:
/* 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}
Ring
# 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
Output:
{} {ABC} {ABC DEF} {ABC, DEF, G and H}
RPL
Whilst it is always important to ensure overall code clarity for maintenance by using explicit variable names and structured control flows, the spirit of reverse Polish notation programming is to optimize some key part of the algorithm in order to make it as compact - and possibly as fast - as possible, whatever the induced loss of readability. Here, two IFTE
low-level instructions are nested to deliver the appropriate liaison substring between two words or before the first word.
≪ DUP SIZE → words n ≪ "{" 1 WHILE DUP n ≤ REPEAT DUP 1 ≠ OVER n ≠ ", " " and " IFTE "" IFTE ROT SWAP + words 3 PICK GET + SWAP 1 + END DROP "}" + ≫ ≫ 'CMAQBL' STO
- Input:
{ } CMAQBL { "ABC" } CMAQBL { "ABC", "DEF" } CMAQBL { "ABC", "DEF", "G", "H" } CMAQBL
- Output:
4: "{}" 3: "{ABC}" 2: "{ABC and DEF}" 1: "{ABC, DEF, G and H}"
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
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
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"}
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"]));
}
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
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}
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")
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
Sed
script-file:
s/#.*$//g
y/[/{/
y/]/}/
s/"//g
s/ [A-Z][A-Z]*}/ and&/g
s/, and/ and/
test.txt:
[] # (No input words).
["ABC"]
["ABC", "DEF"]
["ABC", "DEF", "G", "H"]
sed -f script-file test.txt
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
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}
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
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
SETL
program comma_quibbling;
tests := [
[],
["ABC"],
["ABC","DEF"],
["ABC","DEF","G","H"]
];
loop for t in tests do
print(t, "=", quibble(t));
end loop;
proc quibble(words);
ret := '{';
loop while words /= [] do
word fromb words;
ret +:= word;
case of
(#words = 1):
ret +:= " and ";
(#words > 1):
ret +:= ", ";
end case;
end loop;
return ret + '}';
end proc;
end program;
- Output:
[] = {} [ABC] = {ABC} [ABC DEF] = {ABC and DEF} [ABC DEF G H] = {ABC, DEF, G and H}
Sidef
func comma_quibbling(words) {
'{' + ([words.first(-1).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}
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}"
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)
}
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
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}
TXR
(defun quib (list)
(tree-bind (: last . lead) (reverse list)
`{@{(nreverse lead) ", "}@(if lead " and ")@last}`))
Uiua
Tests ← {{}
{"ABC"}
{"ABC" "DEF"}
{"ABC" "DEF" "G" "H"}}
Jam ← ◇⊂◇⊂:□
Quibble ← |1 ⟨""◌|°□⊢|Jam" and "°⊟|Quibble⊂⊃(□Jam", "°⊟↙2)(↘2)⟩↧3⧻.
Wrap ← ⊂⊂"{" : "}" Quibble
⍚(&pWrap) Tests
UNIX Shell
quibble() {
printf '{'
while (( $# > 2 )); do
printf '%s, ' "$1"
shift
done
if (( $# )); then
printf '%s' "$1"
shift
fi
if (( $# )); then
printf ' and %s' "$1"
fi
printf '%s\n' '}'
}
With a slight modification, it will work in any POSIX shell, or even older Bourne-compatible shells as long as they have functions and printf:
quibble() {
printf '{'
while [ $# -gt 2 ]; do
printf '%s, ' "$1"
shift
done
if [ $# -gt 0 ]; then
printf '%s' "$1"
shift
fi
if [ $# -gt 0 ]; then
printf ' and %s' "$1"
fi
printf '%s\n' '}'
}
Going the other way, Zsh-specific code can be more compact:
quibble() {
printf '{'
if (( $# > 1 )) printf '%s and ' ${(j:, :)@[1,-2]}
printf '%s}\n' $@[-1]
}
The test code is the same either way:
quibble
quibble ABC
quibble ABC DEF
quibble ABC DEF G H
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
VBA
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
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H} {ABC, DEF, G, H, IJKLM, NO and PQRSTUV}
VBScript
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
- 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().
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
- Output:
{} {} {ABC} {ABC} {ABC and DEF} {ABC and DEF} {ABC, DEF, G and H} {ABC, DEF, G and H}
V (Vlang)
fn q(s []string) string {
match s.len {
0 {
return '{}'
}
1 {
return '{${s[0]}}'
}
2 {
return '{${s[0]} and ${s[1]}}'
}
else{
return '{${s[0..s.len-1].join(', ')} and ${s[s.len-1]}}'
}
}
}
fn main(){
println(q([]))
println(q(['ABC']))
println(q(['ABC','DEF']))
println(q(['ABC','DEF','G','H']))
}
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
Wren
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))
- Output:
{} {ABC} {ABC and DEF} {ABC, DEF, G and H}
XBS
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"]));
- 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".
(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))
- Output:
"{}" "{ABC}" "{ABC and DEF}" "{ABC, DEF, G and H}" "{}" "{ABC}" "{ABC and DEF}" "{ABC, DEF, G, and H}"
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}
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}"
ZX Spectrum Basic
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
- Programming Tasks
- Solutions by Programming Task
- 11l
- 360 Assembly
- 8080 Assembly
- ABC
- Acornsoft Lisp
- Action!
- Ada
- ALGOL 68
- ALGOL W
- APL
- AppleScript
- Arturo
- Astro
- AutoHotkey
- AWK
- Batch File
- BCPL
- Bracmat
- C
- C sharp
- C++
- Clojure
- CLU
- COBOL
- CoffeeScript
- Commodore BASIC
- Common Lisp
- Cowgol
- D
- Dc
- DCL
- Delphi
- Déjà Vu
- EasyLang
- EchoLisp
- Ed
- Eiffel
- Elixir
- Erlang
- F Sharp
- Factor
- Forth
- Fortran
- FreeBASIC
- Frink
- FutureBasic
- Gambas
- Go
- Groovy
- Haskell
- Icon
- Unicon
- J
- Java
- JavaScript
- Jq
- Julia
- Kotlin
- Lang
- Lasso
- Liberty BASIC
- Logo
- Lua
- M2000 Interpreter
- Maple
- Mathematica
- Wolfram Language
- MATLAB
- Octave
- MAXScript
- Miranda
- NetRexx
- Nim
- Oberon-2
- Objeck
- OCaml
- Oforth
- Ol
- PARI/GP
- Pascal
- Perl
- Phix
- PHP
- PicoLisp
- PL/I
- PL/M
- Plain English
- PowerShell
- Prolog
- PureBasic
- Python
- Quackery
- R
- Racket
- Raku
- REBOL
- Refal
- REXX
- Ring
- RPL
- Ruby
- Run BASIC
- Rust
- Scala
- Scheme
- Sed
- Seed7
- SenseTalk
- SETL
- Sidef
- Standard ML
- Swift
- Tcl
- TXR
- Uiua
- UNIX Shell
- VBA
- VBScript
- Visual Basic .NET
- V (Vlang)
- Wren
- XBS
- XLISP
- XPL0
- Zkl
- ZX Spectrum Basic
- Pages with too many expensive parser function calls