Search code examples
recursionreturnlispcommon-lisp

LISP Recursive Triangle


My recursive call seems to not be working, I am trying to make a triangle given an integer, and with the help of you guys I was finally able to make the string print on the same line, the correct amount of times for 1 line. At the end of printing the line, I have a recursive call that calls on triangle to make another line, one character shorter. This call seems to never be reached for some reason. Please find the code below, thank you all in advance for any and all help, it is all greatly appreciated!

Note: On a side note, is there any way to stop a function in Lisp similar to a return statement? I would like for the recursion to stop at k = 1, and not continue to k = 0.

(defun newTriangle (k) 
    (cond ((<= k 0) (princ '(Nope))) 
          ((or (= k 1) (= k -1)) (princ 'a)) 
          ((> k 0) (make-string k :initial-element #\a)) 
          (newTriangle (- k 1))))
        
(print (newTriangle 3))

sample output triangle(3)

aaa 
aa 
a 

sample output triangle(-3)

aaa 
 aa 
  a

Solution

  • First, you need to decide exactly what it is that newTriangle is doing. The call (print (newTriangle 3)) suggests that newTriangle should return a string, which is then printed by the call to print. But, the OP definition of newTriangle is both printing output, and returning a single line of the triangle as a string.

    The recursive call to newTriangle is never reached because all possible cases for the value of k are exhausted before this line is reached. Since k can only be less than zero, equal to zero, or greater than zero, and since all of these cases are tested for before reaching the recursive call, it is never reached. Also note that the OP code has the syntax wrong for the final part of the cond statement. The first expression in a cond branch is a test, and the convention is to use t here for a branch that will always be evaluated if reached. But, this many cases are not needed here.

    Assuming the newTriangle function should not return a string, but should print a triangle as a side-effect, what should it do? If the input number is greater than 0, it should print a line with a number of characters equal to the input number and then call itself with the input reduced by one; otherwise it should do nothing:

    (defun print-triangle (k)
      (when (> k 0)
        (princ (make-string k :initial-element #\a))
        (terpri)
        (print-triangle (- k 1))))
    

    This definition is named print-triangle to emphasize that it prints a triangle as a side-effect, and because kebab-case is idiomatic in Lisps, and camelCase is not. Note that each time that print-triangle is called with input greater than zero, a string of the correct length is printed, and then a newline is printed (with the obscurely-named terpri, which just writes a newline to the current output stream), before calling print-triangle again with k reduced by 1.

    Sample REPL interaction:

    CL-USER> (print-triangle 3)
    aaa
    aa
    a
    NIL
    

    If the goal is instead to return a string, one approach would be to call a helper function that keeps the result in a parameter:

    (defun new-triangle (k)
      (build-triangle k ""))
    
    (defun build-triangle (k result)
      (if (> k 0)
          (build-triangle (- k 1)
                          (concatenate 'string
                                       result
                                       (make-string k :initial-element #\a)
                                       (string #\newline)))
          result))
    

    Here, new-triangle takes an integer argument, and calls build-triangle, passing both the integer argument and an empty string in the result position. The build-triangle function operates much the same as print-triangle before, but instead of printing the lines, they are concatenated with result, along with a string containing a newline. When build-triangle is finished, the result string is returned to new-triangle. Note that simply calling new-triangle from the REPL will print the resulting string as data (i.e., with quotation marks); calling print on the result of new-triangle will both print the string as data, and return the string. To see the string printed without quotation marks, format can be used; or you could use princ which will print the string without quotation marks, and return the string itself:

    CL-USER> (new-triangle 3)
    "aaa
    aa
    a
    "
    
    CL-USER> (print (new-triangle 3))
    
    "aaa
    aa
    a
    " 
    "aaa
    aa
    a
    "
    
    CL-USER> (format t "~A" (new-triangle 3))
    aaa
    aa
    a
    NIL
    
    CL-USER> (princ (new-triangle 3))
    aaa
    aa
    a
    "aaa
    aa
    a
    "