Now works even for edge cases not in the example cases.
Full program. Prompts (STDERR) for input (STDIN) of list of strings. Outputs to STDOUT.
Assumes ⎕IO (Index Origin) to be 0, which is default on many systems.
0(('^0' '$',d)⎕R('' 'd',f)⍕2↓t)r(r←' 0d' ' 0?'⎕R'/-' '/'⊢d⎕R f,∘'d'⍕1↓t)('£',⍕⊃t)(∊'£'(⍕⊃t)'.','-'('s',⍨⍕1⊃t)[×1⊃t]'.','-d' '-'⎕R'-' ''⊢'d',⍨(d,⊂'^0')⎕R(f,'-')⍕2⊃t)[5⌊2⊥×t←v⊤+/((v←0 20 12)⊥(¯3+6×'£'=⊃)↑∘⍎('-' '\D',⍨f←,¨'nwr')⎕R('0 ',⍨1↓¨d←('\.',⍕)¨25 5 75))¨⎕]
Try it online! (or equivalent Unicode edition sporting proper fractions)
Explanation
This program has five parts; one that handles input, conversion, summation, and selection of output format, and one for each of the four possible output formats which are:
- less than a shilling:
¼d–11¾d - a whole number of shilling, which is the same format as
- between a shilling and a pound:
1/-–19/11¾ - whole pounds:
£1, £2,… £999 - everything else:
£1.-.¼d–£999.19s.11¾d
The overall structure of the program is:
0( less )r(r←' between )( whole )( everything )[ selection ]
This uses selection to index from a list of all possible formats. The initial 0 is a placeholder for output format 0 which does not exist. r← assigns format 4. to the variable r, which is then used as format 2.
input, conversion, summation, and selection of output format
5⌊2⊥×t←v⊤+/((v←0 20 12)⊥(¯3+6×'£'=⊃)↑∘⍎('-' '\D',⍨f←,¨'nwr')⎕R('0' ' ',⍨1↓¨d←('\.',⍕)¨25 5 75))¨⎕
⎕ prompt for (evaluated) user input (list of strings)
(…)¨ on each string, apply the following tacit function:
25 5 75 the numeric list [25,5,75]
(…)¨ on each number, apply the following function:
⍕ the string representation
'\.', preceded by an escaped (for PCRE) period
d← store that in d (for decimals; ["\.25","\.5","\.75"])
1↓¨ drop one character from each; [".25",".5",".75"]
'0 ',⍨ append the two characters; [".25",".5",".75","0"," "]
(…)⎕R PCRE Replace the following strings with the above:
,¨'nwr' ravel (make into own vector) each of these characters; ["n","w","r"]
f← store in f (for fractions)
'-' '\D',⍨ append these two strings; ["n","w","r","-","\D"]
⍎ execute as APL statement (this gives a numeric list)
∘ then
(…)↑ take this many elements (padding with zeros as needed):
⊃ the first of the argument (one of the strings)
'£'= Boolean (0 or 1) whether it is a pound symbol
6× multiply by six; gives 6 if string had pound(s), 0 if not
¯3+ add negative three; gives 3 if string had pound(s), −3 if not
Taking a negative number of elements takes from the right and pads on the left if needed. Taking a positive number of elements takes from the left and pads on the right if needed. Thus we now have pounds, shillings, and pence in fixed positions in a length-3 list for each input string.
(…)⊥ convert to regular number (pence) from the following mixed-base:
0 20 12 12 pence per shilling, 20 shilling per pound, no amount of pounds convert up
v← store in v (for values)
+/ sum (lit. plus reduction)
v⊤ convert to mixed-base v
t← store in t (for total)
× signum of those values
2⊥ convert to regular number from base-2 (binary)
5⌊ find the minimum of five and that.
Gives 1 for pence-only, 2 for shilling only, 3 for shilling and pence, 4 for pounds only, and 5 for pounds and change.
everything else
∊'£'(⍕⊃t)'.','-'('s',⍨⍕1⊃t)[×1⊃t]'.','-d' '-'⎕R'-' ''⊢'d',⍨(d,⊂'^0')⎕R(f,'-')⍕2⊃t
2⊃t the third (pence) value of t
⍕ string representation of that
⎕R(…) PCRE Replace the following strings:
f,'-' the list f followed by a dash; ["n","w","r","-"]
(…) with:
⊂'^0' this string (regex: leading zero)
d, appended to d; ["\.25","\.5","\.75","^0"]
'd',⍨ append a "d" to that
'-d' '-'⎕R'-' '' remove stray "d"s and dashes (caused by less than a whole pence)
…'.', prepend the following and a period to that:
…] use
1⊃t the second element of t (the shilling)
× the signum of that (i.e. whether we have any shilling or not)
'-'…[ to index from a dash and the following:
1⊃t the second element of t (the shilling)
⍕ the string representation of that
's',⍨ followed by an "s"
'£'(…)'.', prepend a pound sign, and the below, and a period to that:
⊃t the first element of t (the pounds)
⍕ the string representation of that
∊ ϵnlist (flatten)
whole pounds
'£',⍕⊃t
⊃t the first element of t (the pounds)
⍕ the string representation of that
'£', prepend a pound symbol
between a shilling and a pound
' 0d' ' 0?'⎕R'/-' '/'⊢d⎕R f,∘'d'⍕1↓t
1↓t drop one element from t (leaving the shillings and the pence)
⍕ the string representation (space separated) of that
,∘'d' append a "d" to that (lit. apply the append function with a curried right argument)
d⎕R f PCRE Replace d (["\.25","\.5","\.75"]) with f (["n","w","r"])
⊢ yield that (separates the following from d)
' 0d' ' 0?'⎕R'/-' '/' PCRE Replace:
no-pence with slash-dash
leading pence-zero with slash
less than a shilling
('^0' '$',d)⎕R('' 'd',f)⍕2↓t
2↓t drop the first two elements of t (leaving just pence)
⍕ the string representation of that
⎕R(…) PCRE Replace with the following:
'' 'd',f the list f preceded by these two strings; ["","d","n","w","r"]
(…) where the following occur:
'^0' '$',d the list d preceded by these two strings; ["^0","$","\.25","\.5","\.75"]
the first is a leading zero
the second marks the end of a string