Aniruddha
Aniruddha

Reputation: 39

How to stop getting NIL from if statement in LISP

Sorry to ask this question, but I was learning optional parameters in LISP Hence I created a function called "calculate-bill" which will take two parameters :

So as you can guess if the discount is passed to the function it will cut its value from the amount else just print the amount.

Code :

(defun calculate-bill (amount &optional discount)
  (if (null discount)
    (format t "Total bill is ~d." amount))
  (if discount
    (format t "Total bill is ~d." (- amount discount))))

(write (calculate-bill 200 50))
(terpri)
(write (calculate-bill 200))

Here I had added null condition in if blocks.

I want my expected output to be :

Total bill is 150.
Total bill is 200.

But the current output is :

Total bill is 150.NIL
Total bill is 200.NIL

How can I avoid that "NIL" ?

Upvotes: 1

Views: 348

Answers (4)

Leo
Leo

Reputation: 1934

The function calculate-bill prints out the message "Total amount is...", but this is just a side effect. The value returned by the function is, when there is no explicit return <value>, the value of the last executed function, which in this case is format, and the value of format is always nil when the output stream is T. So what the write function does is to print the value returned by calculate-bill which is nil. If you want calculate-bill to return the string then change the output stream of format to nil:

(format nil "Total bill is ~d." amount)

This creates a string which is not printed until you pass it to an output function like e.g. write.

As an extra suggestion, the function if can take three parameters: the test, the code to execute when the test is t, and the code to execute when the test is nil. So, in your case, it is far better to write:

(if (null discount)
    (format t "Total bill is ~d." amount)
    (format t "Total bill is ~d." (- amount discount)))

Upvotes: 2

Gwang-Jin Kim
Gwang-Jin Kim

Reputation: 9865

Choose default for discount as 0 then you save the null testing.

Further, I would format to nil to just return the strings.

(defun calculate-bill (amount &optional (discount 0))
  (format nil "Total bill is ~d." (- amount discount)))

write prints and returns. Only printing you achieve by (format t "~a" x).

(progn 
  (format t "~a" (calculate-bill 200 50)) 
  (terpri) 
  (format t "~a" (calculate-bill 200)))

Alternatively you can include "~%" into format string instead using (terpri).

(progn 
  (format t "~a~%" (calculate-bill 200 50)) 
  (format t "~a~%" (calculate-bill 200)))

I would optionally let display the result but return the string.

(defun calculate-bill (amount &key (discount 0) (verbosep nil))
  (let* ((result (- amount discount))
        (res (format nil "Total bill is ~d." result)))
    (if verbosep
      (format t "~a~%" res))
    res))

Then set :verbosep t if you want the output visible in the terminal.

(calculate-bill 200 :discount 50 :verbosep t)
(calculate-bill 200 :verbosep t)

Upvotes: 1

coredump
coredump

Reputation: 38809

Optional parameter admit a default value, you could for example have a default discount of zero.

(defun calculate-bill (amount &optional (discount 0))
  (check-type discount real)
  (- amount discount))

Upvotes: 1

Rainer Joswig
Rainer Joswig

Reputation: 139261

You are doing double output. You call FORMAT and WRITE.

Now you need to find out whether you really want to do double output and, if not, which function to keep and which not.

Upvotes: 2

Related Questions