Skip to content

Numberword in D

Vector 19:4, page 40

programming techniques

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

('trillion ' (10*12)) mag '' ⍵

The ‘and’ option can be used to write English style cheques rather than the American checks given in the Zark challenge:

      ChequeAmount 1234.56
One thousand two hundred and thirty four pounds 56 pence

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.