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