RosettaCodeData/Task/Four-is-magic/Racket/four-is-magic.rkt
2023-07-01 13:44:08 -04:00

55 lines
2 KiB
Racket

#lang racket
(require rackunit)
(define smalls
(map symbol->string
'(zero one two three four five six seven eight nine ten eleven twelve
thirteen fourteen fifteen sixteen seventeen eighteen nineteen)))
(define tens
(map symbol->string
'(zero ten twenty thirty forty fifty sixty seventy eighty ninety)))
(define larges
(map symbol->string
'(thousand million billion trillion quadrillion quintillion sextillion
septillion octillion nonillion decillion undecillion duodecillion
tredecillion quattuordecillion quindecillion sexdecillion
septendecillion octodecillion novemdecillion vigintillion)))
(define (number->words n)
(define (step div suffix separator [subformat number->words])
(define-values [q r] (quotient/remainder n div))
(define S (if suffix (~a (subformat q) " " suffix) (subformat q)))
(if (zero? r) S (~a S separator (number->words r))))
(cond [(< n 0) (~a "negative " (number->words (- n)))]
[(< n 20) (list-ref smalls n)]
[(< n 100) (step 10 #f "-" (curry list-ref tens))]
[(< n 1000) (step 100 "hundred" " ")]
[else (let loop ([N 1000000] [D 1000] [unit larges])
(cond [(null? unit)
(error 'number->words "number too big: ~e" n)]
[(< n N) (step D (car unit) " ")]
[else (loop (* 1000 N) (* 1000 D) (cdr unit))]))]))
(define (first-cap s)
(~a (string-upcase (substring s 0 1)) (substring s 1)))
(define (magic word [acc null])
(if (equal? word "four")
(string-join (reverse (cons "four is magic." acc)) ", \n")
(let* ([word-len (string-length word)]
[words (number->words word-len)])
(magic words
(cons (string-append word " is " words) acc)))))
(define (number-magic n)
(first-cap (magic (number->words n))))
(for ([n (append (range 11)
'(-10 23 172 20140 100 130 999999 876000000
874143425855745733896030))])
(displayln n)
(displayln (number-magic n))
(newline))