Numberword in D
Vector 19:4, page 40
Transcribed from the printed issue; not yet reviewed. Read it in the PDF of the issue, page 40
This entry for the NUMBERWORDS Zark Limbering Up challenge demonstrates a way of using Dyalog Direct Definition Functions (D-Fns) and compares this with a traditional APL solution.
∇NumberWords←{
[1] ⍝ Spell a scalar integer in words.
[2] ⍝ Optional left argument of 1 to include 'and'
[3] ⎕IO←1 ⋄ ⎕ML←1
[4] 0=⊃⍵:'zero' ⍝ Return if zero
[5] ⍺←0 ⍝ Default left argument
[6] and←⍺/'and ' ⍝ 'and' text, if required
[7] htu←{ ⍝ Fn to spell numbers of less than 1000
[8] u←'one ' 'two ' 'three ' 'four ' 'five ' 'six '
'seven ' 'eight ' 'nine ' 'ten ' 'eleven '
'twelve ' 'thirteen ' 'fourteen ' 'fifteen '
'sixteen ' 'seventeen ' 'eighteen ' 'nineteen '
[9] t←'ten ' 'twenty ' 'thirty ' 'forty ' 'fifty '
'sixty ' 'seventy ' 'eighty ' 'ninety '
[10] ⍝ Hundreds to words
[11] r←{⍵≥100:((⌊⍵÷100)⊃u),'hundred ' ⋄ ''}⍵
[12] ⍝ Tens and units
[13] n←100|⍵
[14] n>19:r,and,((⌊n÷10)⊃t),{⍵>0:⍵⊃u ⋄ ''}10|n
[15] n>0:r,and,n⊃u ⍝ Teens
[16] r} ⍝ Exact hundred
[17] mag←{ ⍝ Fn to spell numbers according to magnitude
[18] rtxt tval←⍵ ⍝ Rt Arg. Result, text and value
[19] mtxt mval←⍺ ⍝ Lt Arg. Magnitude, text and value
[20] mval>tval:⍵ ⍝ Magnitude greater so return rt arg
[21] ⍝ Return result text and new test value
[22] (rtxt,(htu⌊tval÷mval),mtxt)(mval|tval)}
[23] ⍝ Use Reduction / to apply <mag> for each magnitude.
[24] ⍝ Remove leading 'and'. Drop trailing space.
[25] ¯1↓{((⍴and)×'a'≡⊃⍵)↓⍵}⊃⊃mag/'' 'thousand ' 'million '
'billion ' 'trillion ' ''{⍺ ⍵}¨(10*0 3 6 9 12),⊃⍵
[26] }
∇
All the action occurs on line [25] where Reduction (/) is used to loop through each order of magnitude, starting with trillions and ending with units. The {⍺ ⍵}¨ on that line is a D-Fns idiom which converts the two vectors into a vector of pairs of items.
The function mag is first applied between the rightmost two elements of the vector of pairs, effectively
The ‘and’ option can be used to write English style cheques rather than the American checks given in the Zark challenge:
The following traditional (non-nested) APL solution has exactly the same structure as the D-Fns solution, though without the ‘and’ option:
∇ R←NUMBERWORDS N;MVAL;MTXT;UNITS;TENS;X;I;⎕IO
[1] ⎕IO←1
[2] ⍝ Exit if zero
[3] →(N≠0)/L1
[4] R←'zero'
[5] →0
[6] ⍝ Set up names
[7] L1:UNITS←19 10⍴190↑'one two three four
five six seven eight nine ten
eleven twelve thirteen fourteen fifteen sixteen
seventeen eighteen nineteen'
[8] TENS←9 8⍴72↑'ten twenty thirty forty fifty
sixty seventy eighty ninety'
[9] MTXT←5 9⍴45↑'trillion billion million thousand'
[10] MVAL←10*12 9 6 3 0
[11] ⍝ Loop for each magnitude
[12] R←'' ⍝ Set up result
[13] I←1 ⍝ Loop counter
[14] ⍝ Skip if value less than magnitude
[15] L2:→(N<MVAL[I])/L6
[16] X←⌊N÷MVAL[I] ⍝ Number of mag
[17] →(X<100)/L3 ⍝ Hundreds?
[18] R←R,UNITS[⌊X÷100;],' hundred '
[19] L3:X←100|X ⍝ Tens and units
[20] →(X<20)/L4 ⍝ Teens?
[21] ⍝ 20 or more, so tends and units
[22] R←R,TENS[⌊X÷10;],UNITS[10|X;]
[23] →L5
[24] ⍝ 19 or less. Check not zero
[25] L4:→(X=0)/L5
[26] R←R,UNITS[X;] ⍝ Teens
[27] ⍝ Append magnitude text
[28] L5:R←R,MTXT[I;]
[29] ⍝ Remove high order numbers processed
[30] N←MVAL[I]|N
[31] ⍝ Carry on with loop
[32] L6:→((⍴MVAL)≥I←I+1)/L2
[33] ⍝ Remove redundant spaces
[34] R←¯1↓(X∨1,¯1↓X←R≠' ')/R
∇
Lines [15] to [30] of the traditional function are the equivalent of the mag D-Fn, with lines [17] to [26] matching the htu D-Fn called by mag.
Which way is best? I can’t make up my mind. D-Fns can be too ‘clever’ in which case they are difficult to maintain. Traditional APL tends to have lots of branching which can be equally difficult to maintain.