user5597655
user5597655

Reputation:

Common Lisp map numbers to words

I am trying to make a translate function that takes a list of numbers `(1 2 3) and writes ("ONE" "TWO" "THREE"). Here's what I have so far:

(defun translate-number (num)
        (if (= num 0) (return "ZERO") ())
        (if (= num 1) (return "ONE") ())
        (if (= num 2) (return "TWO") ())
        (if (= num 3) (return "THREE") ())
        (if (= num 4) (return "FOUR") ())
        (if (= num 5) (return "FIVE") ())
        (if (= num 6) (return "SIX") ())
        (if (= num 7) (return "SEVEN") ())
        (if (= num 8) (return "EIGHT") ())
        (if (= num 9) (return "NINE") ())
)

(defun translate (L)
  (mapcar #'translate-number L)
)

(translate `(1 2 3))

If I try to run this, I get this error that I have not been able to figure out:

*** - RETURN-FROM: no block named NIL is currently visible

Any ideas? Thanks.

Upvotes: 0

Views: 776

Answers (3)

Rainer Joswig
Rainer Joswig

Reputation: 139261

CL-USER > (getf '(1 "ONE" 2 "TWO" 3 "THREE" 4 "FOUR" 5 "FIVE"
                  6 "SIX" 7 "SEVEN" 8 "EIGHT" 9 "NINE")
                2
                :dont-know)
"TWO"

CL-USER > (aref #("ZERO" "ONE" "TWO" "THREE" "FOUR"
                  "FIVE" "SIX" "SEVEN" "EIGHT" "NINE")
                2)
"TWO"

Upvotes: 3

Ehvince
Ehvince

Reputation: 18375

return doesn't do what it does in other languages :] You are looking for return-from translate-number, but this is not idiomatic.

First, did you know that the format function has a ~R "roman" directive ?

(format nil "~R" 1) ;; => one

If you really want uppercase, use string-upcase or the format directive ~( ... ~) with the modifiers @::

(format nil "~@:(~R~)" 1)
;; "ONE"

https://lispcookbook.github.io/cl-cookbook/strings.html#to-upper-case--

CL quick reference with format directives: http://clqr.boundp.org/

So:

(mapcar (lambda (nb) 
          (format nil "~@:(~R~)" nb))
        '(1 2 3))
("ONE" "TWO" "THREE")

For many if in a row you can use case or cond.

(defun translate-number (num)
   (case num
     (1 "ONE")
     (2 "FOO")))

No need of void () for the second form of if.

https://learnxinyminutes.com/docs/common-lisp/ ;)

Upvotes: 8

Simeon Ikudabo
Simeon Ikudabo

Reputation: 2190

You're returning within your if statements from an empty list (NIL) after a return statement. There is no block named nil, but an empty list will return nil. Instead, you could use a (return-from block-name (optiopnal-statement)) at for the else condition in each of your if blocks. Another issue is your design pattern here. Instead of using multiple if statements, with a return statement for each one, you can instead create a cond block, in place of multiple if statements. But, it would make even more sense to use a case statement within your translate-number function. If you are going to use multiple if statements consider a cond block, and where that doesn't make since (usually when you want to return a value depending on the type of argument passed to a function)) consider a case statement. The if statements become repetitive as you can see. Here is an example so that you don't have a nil return-from:

(defun translate-number(num)
   (case num
     (1 "ONE")
     (2 "TWO")
     (3 "THREE")
     (4 "FOUR")
     (5 "FIVE")
     (6 "SIX")
     (7 "SEVEN")
     (8 "EIGHT")
     (9 "NINE")))

(defun translate(&rest nums)
   (apply #'mapcar #'translate-number nums))

(translate '(1 2 3))
("ONE" "TWO" "THREE")

Upvotes: 3

Related Questions