1

Hi I am new in Common Lisp and there aren't any tutorials I can find that relates to my current problem, I have decent knowledge in Java and I tried converting simple programs from Java to Common LISP as an exercise and one particular thing I can't do is a while loop, how do I do it? it always results in UNDEFINED-FUNCTION

TL;DR

how do I do a while loop in common LISP with certain conditions like in Java like this:

while(UserIn > 0)
{
    LastD = UserIn % 10;
    Sum = Sum + LastD;
    Product = Product * LastD; 
    UserIn = UserIn / 10;
}


if (Sum == Product) 
{
    System.out.println("\nGiven number is a Spy number");
}
else 
{
    System.out.println("\nGiven number is not a Spy number");
}

my attempt in common LISP is as follows

  (while (> userIn 0)
        (setq LastD(mod 10 UserIn))
        (setq Product(* LastD Product))
        (setq Sum(+ LastD Sum))
        (setq UserIn(/ UserIn 10)))
    
    (terpri)
    
    (if (= a b)
    (format t "is a spy number")
    (format t "is not a spy number"))
    )
    (Spynumber)

and it keeps on saying: debugger invoked on a UNDEFINED-FUNCTION, thank you!

2

5 Answers 5

5

Common Lisp does not have while form, but it has a much more powerful loop macro which has the while keyword you want:

(loop while ... do ...)

See also How to do a while loop in LISP

Sign up to request clarification or add additional context in comments.

1 Comment

I read in some CL book that "loop" is not recommended for use because even ANSI standard is very ambiguous and incomplete on "loop" macro
4

As someone else has said, you can use loop to do that, and that would be the idiomatic approach.

But Lisp is the programmable programming language: if you want while, you can have while:

(defmacro while (test &body decls/tags/forms)
  `(do () ((not ,test) (values))
     ,@decls/tags/forms))

And now

> (let ((i 0))
    (while (< i 10)
      (print i)
      (incf i)))

0 
1 
2 
3 
4 
5 
6 
7 
8 
9 

> 

Comments

2

Your example program in Java assigns variables that are not declared in the current scope. You have the same problem in Lisp, where you call setq on variables that are not declared. You need to declare variables in Lisp otherwise the behavior is unspecified.

One way to declare variables is to have a let block:

(let ((product 1)
      (sum 0))
   ...

   (setq sum (+ sum d)) ;; <-- here SETQ is well-defined

   ...)
      

Also, you compute both quotient and the remainder of a division: in Common Lisp functions can return more than one values, and in particular, truncate divides and returns the remainder as a secondary value:

(multiple-value-bind (q r) (truncate u 10)

   ...)

Recursive approach

It is possible to write loops as recursive procedures, and your example is one of the cases where I find the recursive approach easier to understand. Let's define spy-compute a function of three arguments: a number, the current sum, and the current product of the remainders:

(defun spy-compute (u s p)
  ...)

The base case corresponds to (= u 0), and in this case the function returns both the sum and the product, as two values:

(defun spy-compute (u s p)
  (if (= u 0)
      (values s p)
      ...))

The generat case of recursion consists in dividing u by 10, and calling spy-number recursively with modified sum and products:

(defun spy-compute (u s p)
  (if (= u 0)
      (values s p)
      (multiple-value-bind (q r) (truncate u 10)
        (spy-compute q (+ s r) (* p r)))))

This functions needs to be called with the sum initialized to 0 and the product initialized to 1. You can provide a simpler interface to the caller:

(defun spy (n)
  (spy-compute n 0 1))

(I fixed a bug here, I had 0 and 1 reversed)

And if you want to check if the number is a spy number, you can define this functions (the p suffix is for "predicate", the naming convention for functions that returns a boolean value):

(defun spyp (n)
  (multiple-value-bind (s p) (spy n)
    (= s p)))
Example

Having the three functions defined as above, let's trace them and check if 1124 is a spy number (spoiler alert, it is):

* (trace spy-compute spy spyp)
* (spyp 1124)

Here is the execution trace, I added comments manually:

  ;; root call to SPYP with 1124
  0: (SO::SPYP 1124)
    ;; it calls SPY
    1: (SO::SPY 1124)
      ;; ... which calls SPY-COMPUTE with sum 0 and product 1
      2: (SO::SPY-COMPUTE 1124 0 1)
        ;; DIVIDE by TEN, REMAINDER is 4
        ;; RECURSE With SUM = SUM + 4 and PRODUCT = PRODUCT * 4
        3: (SO::SPY-COMPUTE 112 4 4)
          ;; DIVIDE by TEN: 112 = 11 * 10 + 2, adjust counters
          4: (SO::SPY-COMPUTE 11 6 8)
            ;; ETC.
            5: (SO::SPY-COMPUTE 1 7 8)
              ;; BASE CASE OF RECURSION, RETURN BOTH COUNTERS
              6: (SO::SPY-COMPUTE 0 8 8)
              ;; NO CHANGE IS MADE TO THE RESULT, IT BUBBLES UP
              6: SPY-COMPUTE returned 8 8
            5: SPY-COMPUTE returned 8 8
          4: SPY-COMPUTE returned 8 8
        3: SPY-COMPUTE returned 8 8
      2: SPY-COMPUTE returned 8 8
    1: SPY returned 8 8
  ;; CHECK if (= P S), which is T here
  0: SPYP returned T

Iterate

Your example can also be written using a loop. In addition other the standard ways of looping, you can also use the iterate package, which contrary to LOOP allows to mix the test clauses (while) with iteration clauses (for):

(ql:quickload :iterate) ;; see https://www.quicklisp.org/beta/

(use-package :iterate)

(defun iter-spy (n)
  (iter
    (for u :initially n :then q)
    (while (> u 0))
    (for (values q r) = (truncate u 10))
    (sum r :into s)
    (multiply r :into p)
    (finally (return
               (values s p)))))

2 Comments

does this work with userinput as well? I may have not included the whole code because it might be too long and I really don't know how to do it here in stack overflow, I tried the 1st approach and the recursive one, I still don't get it. I'm sorry I'm a newbie at programming especially in common LISP
@NikkoNeri I don't understand your question about "userinput"? I changed the answer to show a step-by-step execution of the recursive approach if that helps (I also fixed a bug :/)
0

With do you can loop and assign variables in parallel, the syntax is:

(do ((<var1> <var1-initial-value> <var1-step>)
     (<var2> <var2-initial-value> <var2-step>)
     ...)
    ((<exit-condition>)
     (<final-statement1>)
     (<final-statement2>)
     ...)
  (<action1-during-loop>)
  (<action2-during-loop>)
  ...)

So with your code, more or less:

(let* ((UserIn (read))
       (UI UserIn))
  (do* ((LastD (rem UserIn 10) (rem UserIn 10))
        (Sum 0 (+ Sum LastD))
        (Product 1 (* Product LastD))
        (UserIn UserIn (truncate UserIn 10)))
       ((<= UserIn 0)
        (format t "~&LastD: ~f, Sum: ~f, Product: ~f, UserIn: ~f"
                LastD Sum Product UserIn)
        (if (= Sum Product)
            (format t "~&~A is Spy number" UI)
            (format t "~&~A is Not Spy number" UI)))
    (format t "~&LastD: ~f, Sum: ~f, Product: ~f, UserIn: ~f" 
            LastD Sum Product UserIn)))

> LastD: 4.0, Sum: 0.0, Product: 1.0, UserIn: 1124.0
> LastD: 4.0, Sum: 4.0, Product: 4.0, UserIn: 112.0
> LastD: 2.0, Sum: 6.0, Product: 8.0, UserIn: 11.0
> LastD: 1.0, Sum: 7.0, Product: 8.0, UserIn: 1.0
> LastD: 1.0, Sum: 8.0, Product: 8.0, UserIn: 0.0
> 1124 is Spy number
> LastD: 2.0, Sum: 0.0, Product: 1.0, UserIn: 12.0
> LastD: 2.0, Sum: 2.0, Product: 2.0, UserIn: 1.0
> LastD: 1.0, Sum: 3.0, Product: 2.0, UserIn: 0.0
> 12 is Not Spy number

For some code snippets you can visit http://rosettacode.org/wiki/Rosetta_Code.

Comments

0
(defmacro while (condition &rest body)
  `(loop while ,condition
         do (progn
              ,@body)))

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.