Names to numbers: Difference between revisions
Content added Content deleted
(→{{header|Common Lisp}}: appears to do the opposite of what is required) |
|||
Line 105: | Line 105: | ||
=={{header|Common Lisp}}== |
=={{header|Common Lisp}}== |
||
A counterpart to (format t "~R" ...). |
|||
{{incorrect|Common Lisp|This appears to be a repeat of [[Number_names#Common_Lisp|Number names]] which does the opposite of what is required here.}} |
|||
<lang Lisp>(defpackage number-names |
|||
Just use the R directive of the format macro. There is no limit when it comes to the size of the integers. |
|||
(:use cl)) |
|||
<lang Lisp>(format t "~R" 123456789) |
|||
=> one hundred twenty-three million four hundred fifty-six thousand seven hundred eighty-nine |
|||
(in-package number-names) |
|||
NIL</lang> |
|||
(defparameter *ones* |
|||
'((one . 1) |
|||
(two . 2) |
|||
(three . 3) |
|||
(four . 4) |
|||
(five . 5) |
|||
(six . 6) |
|||
(seven . 7) |
|||
(eight . 8) |
|||
(nine . 9))) |
|||
(defparameter *teens* |
|||
'((ten . 10) |
|||
(eleven . 11) |
|||
(twelve . 12) |
|||
(thirteen . 13) |
|||
(fourteen . 14) |
|||
(fifteen . 15) |
|||
(sixteen . 16) |
|||
(seventeen . 17) |
|||
(eighteen . 18) |
|||
(nineteen . 19))) |
|||
(defparameter *tens* |
|||
'((twenty . 20) |
|||
(thirty . 30) |
|||
(fourty . 40) |
|||
(fifty . 50) |
|||
(sixty . 60) |
|||
(seventy . 70) |
|||
(eighty . 80) |
|||
(ninty . 90))) |
|||
(defparameter *hundred* |
|||
'((hundred . 100))) |
|||
(defparameter *illions* |
|||
'((quintillion . 1000000000000000000) |
|||
(quadrillion . 1000000000000000) |
|||
(trillion . 1000000000000) |
|||
(billion . 1000000000) |
|||
(million . 1000000) |
|||
(thousand . 1000))) |
|||
(defparameter *delims* '(#\Space #\Tab #\Newline #\-)) |
|||
;; Turn a single delimited word into an atom. |
|||
(defun tokenize-word (word) |
|||
(let ((stream (make-string-output-stream))) |
|||
(loop do |
|||
(let ((char (pop word))) |
|||
(cond ((null char) (return)) |
|||
((member char *delims*) (return)) |
|||
(t (write-char char stream))))) |
|||
(let ((out (get-output-stream-string stream))) |
|||
(values (intern (string-upcase out) 'number-names) |
|||
word)))) |
|||
;; Tokenize the input string. |
|||
(defun tokenize (word) |
|||
(let ((word (coerce word 'list)) |
|||
(tokens (list))) |
|||
(loop do |
|||
(let ((char (pop word))) |
|||
(cond ((null char) (return)) |
|||
((member char *delims*) nil) |
|||
(t (multiple-value-bind (token rest-word) |
|||
(tokenize-word (push char word)) |
|||
(setf word rest-word) |
|||
(push token tokens)))))) |
|||
(reverse tokens))) |
|||
;; Define a state machine to parse a subsection of a number |
|||
;; that precedes an -illion. |
|||
(defmacro defstate (name end-transitions-p &rest transitions) |
|||
(let ((token (gensym "TOKEN")) |
|||
(number (gensym "NUMBER")) |
|||
(illions (gensym "ILLIONS")) |
|||
(illion (gensym "ILLION"))) |
|||
`(defun ,name (,token ,number ,illions) |
|||
,(append '(cond) |
|||
(loop for trans in transitions collect |
|||
(destructuring-bind (place to-state op) trans |
|||
`((assoc ,token ,place) |
|||
(values ',to-state |
|||
(,op ,number (cdr (assoc ,token ,place))))))) |
|||
(when end-transitions-p |
|||
`(((assoc ,token ,illions) |
|||
(throw 'done |
|||
(let ((,illion (assoc ,token ,illions))) |
|||
(list (* ,number (cdr ,illion)) (car ,illion))))) |
|||
((null ,token) (throw 'done (list ,number nil))))) |
|||
`((t (error "Unexpected token ~a" ,token))))))) |
|||
(defstate state-a nil |
|||
(*ones* state-b +) |
|||
(*tens* state-d +) |
|||
(*teens* state-e +)) |
|||
(defstate state-b t |
|||
(*hundred* state-c *)) |
|||
(defstate state-c t |
|||
(*ones* state-e +) |
|||
(*tens* state-d +) |
|||
(*teens* state-e +)) |
|||
(defstate state-d t |
|||
(*ones* state-e +)) |
|||
(defstate state-e t) |
|||
(defun consume-illions (illion illions) |
|||
(cond ((null illions) nil) |
|||
((eq illion (caar illions)) (cdr illions)) |
|||
(t (consume-illions illion (cdr illions))))) |
|||
;; Parse a number up to the next -illion. |
|||
;; Errors on numbers that (format t "~R" ..) |
|||
;; would not generate, like "one thousand one million". |
|||
(defun parse-sub-number (tokens illions) |
|||
(let ((number 0) |
|||
(state 'state-a)) |
|||
(destructuring-bind (number illion) |
|||
(catch 'done |
|||
(loop do |
|||
(let ((token (pop tokens))) |
|||
(multiple-value-bind (next-state next-number) |
|||
(funcall state token number illions) |
|||
(setf state next-state) |
|||
(setf number next-number))))) |
|||
(values number |
|||
(if illion |
|||
(consume-illions illion illions) |
|||
illions) |
|||
tokens)))) |
|||
;; Parse the list of tokenized number parts. |
|||
(defun parse-number (tokens) |
|||
(let ((illions *illions*) |
|||
(total 0) |
|||
(negative-p (eq (car tokens) 'negative))) |
|||
(when negative-p (pop tokens)) |
|||
(if (eq (car tokens) 'zero) |
|||
(if (null (cdr tokens)) |
|||
0 |
|||
(error "Unexpected token ~a" (cadr tokens))) |
|||
(loop do |
|||
(multiple-value-bind (number new-illions rest-tokens) |
|||
(parse-sub-number tokens illions) |
|||
(setf illions new-illions) |
|||
(incf total number) |
|||
(setf tokens rest-tokens) |
|||
(unless tokens (return (* (if negative-p -1 1) total)))))))) |
|||
(defun parse (word) |
|||
(parse-number (tokenize word))) |
|||
(defun test () |
|||
(let ((test-numbers |
|||
'(+0 |
|||
-3 |
|||
+5 |
|||
-7 |
|||
+11 |
|||
-13 |
|||
+17 |
|||
-19 |
|||
+23 |
|||
-29 |
|||
201021002001 |
|||
-20102100201 |
|||
2010210020 |
|||
-201021002 |
|||
20102100 |
|||
-2010210 |
|||
201021 |
|||
-20103 |
|||
2010 |
|||
-201 |
|||
20 |
|||
-2 |
|||
0))) |
|||
(princ "number => (format t \"~R\" number) => (parse (format t \"~R\" number))") |
|||
(terpri) |
|||
(mapc (lambda (number) |
|||
(let ((word (format nil "~R" number))) |
|||
(format t "~a => ~a => ~a~%" number word (parse word)))) |
|||
test-numbers)) |
|||
(values))</lang> |
|||
Running the test procedure: |
|||
<lang none>CL-USER> (number-names::test) |
|||
number => (format t "~R" number) => (parse (format t "~R" number)) |
|||
0 => zero => 0 |
|||
-3 => negative three => -3 |
|||
5 => five => 5 |
|||
-7 => negative seven => -7 |
|||
11 => eleven => 11 |
|||
-13 => negative thirteen => -13 |
|||
17 => seventeen => 17 |
|||
-19 => negative nineteen => -19 |
|||
23 => twenty-three => 23 |
|||
-29 => negative twenty-nine => -29 |
|||
201021002001 => two hundred one billion twenty-one million two thousand one => 201021002001 |
|||
-20102100201 => negative twenty billion one hundred two million one hundred thousand two hundred one => -20102100201 |
|||
2010210020 => two billion ten million two hundred ten thousand twenty => 2010210020 |
|||
-201021002 => negative two hundred one million twenty-one thousand two => -201021002 |
|||
20102100 => twenty million one hundred two thousand one hundred => 20102100 |
|||
-2010210 => negative two million ten thousand two hundred ten => -2010210 |
|||
201021 => two hundred one thousand twenty-one => 201021 |
|||
-20103 => negative twenty thousand one hundred three => -20103 |
|||
2010 => two thousand ten => 2010 |
|||
-201 => negative two hundred one => -201 |
|||
20 => twenty => 20 |
|||
-2 => negative two => -2 |
|||
0 => zero => 0 |
|||
; No value</lang> |
|||
=={{header|J}}== |
=={{header|J}}== |