Search code examples
lispcommon-lispread-eval-print-loopclisp

Functions inside a loop behaves differently


So I have a loop to just repeat the little text game I have made about dota, but when the function 'play' is called within a loop it doesn't return the result of my cond function, it just takes an input and then moves on to the next loop.

;;;;learn the invoker combo's
(defparameter *invoker-combo* '((cold-snap  (3 0 0) 'QQQ);all of the possible invoker combo's
                                (ghost-walk (2 1 0) 'QQW)
                                (Ice-Wall (2 0 1) 'QQE)
                                (EMP (0 3 0) 'WWW)
                                (Tornado (1 2 0) 'QWW)
                                (Alacrity (0 2 1) 'WWE)
                                (Sun-Strike (0 0 3) 'EEE)
                                (Forge-Spirit (1 0 2) 'QEE)
                                (Chaos-Meteor (0 1 2) 'WEE)
                                (Deafening-Blast (1 1 1) 'QWE)))
(defun rand-combo (invoker-combo);returns a random combo
    (nth (random (length invoker-combo))invoker-combo))

(defun count-letters (input);converts the keyboard strokes into numbers to be compared as it doesn't matter what order they are in, just that there is the correct quantity of them e.g QQE could also be written QEQ.
    (append
        (list (count #\Q input)
              (count #\W input)
              (count #\E input))))

(defun try-for-combo (rand-combo);takes i-p and compares it with the value for the random combo
    (print(car rand-combo))
    (let* ((i-p (string-upcase(read-line)))
            (try (count-letters i-p)))
            (cond ((equal try (cadr rand-combo))'Good-job)
                  ((equal i-p "END")(list 'Thanks 'for 'playing))
                  (t (list i-p 'was 'wrong 'correct 'is (caddr(assoc (car rand-combo)*invoker-combo*)))))))

(defun play ()
    (try-for-combo (rand-combo *invoker-combo*)))

(defun loop-play (x)
    (loop for i from 0 to x
        :do (play)))

If I call the function 'play' I get the following o/p:

FORGE-SPIRIT asdf
("ASDF" WAS WRONG CORRECT IS 'QEE)

or

ALACRITY wwe
GOOD-JOB

But if I call the function 'loop-play' I get the following o/p:

Break 3 [7]> (loop-play 2)    
SUN-STRIKE eee    
ALACRITY wwe
TORNADO qww
NIL

Can someone explain to me why this is happening? EDIT: feel free to change the title, I didn't really know what to put.


Solution

  • Your try-for-combo function doesn't actually output anything. Rather, it returns values.

    In the REPL, if you evaluate a form, like (+ 1 2), it will always print the evaluation of that form at the end (in this case, 3). However, consider instead (+ 1 (print 2)). The print function actually outputs the argument to standard output, then returns the value itself. So this will show (on the repl)

    2
    3
    

    The 2 is outputted first, because (print 2) itself prints 2. Then, the form (+ 1 (print 2)) is evaluates to the same things as (+ 1 2), or 3.

    In your case, your try-for-combo function should look like:

    (defun try-for-combo (rand-combo)
      (print (car rand-combo))
      (let* ((i-p (string-upcase(read-line)))
             (try (count-letters i-p)))
        (print
         (cond
           ((equal try (cadr rand-combo)) 'Good-job)
           ((equal i-p "END") (list 'Thanks 'for 'playing))
           (t (list i-p 'was 'wrong 'correct 'is (caddr(assoc (car rand-combo) *invoker-combo*))))))
        nil))
    

    This will print the result of that cond form, and return 'nil'.