Search code examples
common-lispclisp

Right Justified Text Lisp


I am trying to have my lisp code output something that is right-justified. I looked up the documentation for format (doc link) and was trying the following

(format t "~10:<*~>")

The problem is that running this code gives me this:

     *         *
     *

instead of giving me something like this:

   **
    *

Below is the whole function, though I don't think that it is the one causing any issues:

(defun triangle-print-neg (rows numStar)
   (cond
      ((= rows 0) (return-from triangle-print-neg nil)) ;If rows = 0, force exit
      ((< numStar (abs rows)) (progn
           (format t "~10:<*~>")
           (triangle-print-neg rows (+ numStar 1)) ;Call function again but with numStar + 1
      )); numStar < abs(rows) print("*")
      (t (progn
             (format t "~%")
             (triangle-print-neg (+ rows 1) 0)
       )) ; Else call triangle print neg with row+1, 0
   )
)

Thank you


Solution

  • Given your underlying problem, which is to print a triangle, presumably as part of an exercise in learning Lisp, and that you are (I am guessing) not very familiar with Common Lisp, I would suggest not trying to understand the details of the language of format strings, which is hairy and entirely unrelated to Lisp except insofar as it happens to be embedded in CL, and instead simply write a function to do what you want using simple printing operations.

    The two simple printing operations you need are:

    • princ which prints something without any decoration like newlines or quotes;
    • terpri which prints a newline.

    Given these two operations you can write a procedure princ-n-padded which prints n copies of a character, padded to a given width, followed by a newline. You can use the dirty trick of having the width be negative to indicate padding on the right.

    Here is such a procedure:

    (defun print-n-padded (c n width &optional (to *standard-output*))
      "Print a line with N copies of C in width |WIDTH|.
    If WIDTH >= 0 then print left justified, if it is < 0 print right justified.
    The optional TO argument is the stream to which to print."
      (if (>= width 0)
          (dotimes (i width)
            (princ (if (< i n) c #\Space) to))
         (dotimes (i (- width))
           (princ (if (< i (- (- width) n)) #\Space c) to)))
      (terpri to))
    

    And now

    > (dotimes (i 10)
        (print-n-padded #\* (+ i 1) -10))
             *
            **
           ***
          ****
         *****
        ******
       *******
      ********
     *********
    **********
    nil
    

    As you can see this is straightforward code, with no hairy and hard to understand format strings. In real life you might, eventually want to start using format in non-trivial ways, but very often it is simply easier to write code which does what you want, where what you want is something relatively simple, rather than write code which is full of line-noise strings.


    If you are required to do this recursively then you can do that too:

    (defun print-n-padded (c n width &optional (to *standard-output*))
      "Print a line with N copies of C in width |WIDTH|.
    If WIDTH >= 0 then print left justified, if it is < 0 print right justified.
    The optional TO argument is the stream to which to print."
      (if (>= width 0)
          (princ-ab c #\Space width (- width n) to)
        (princ-ab #\Space c (- width) n to))
      (terpri to))
    
    (defun princ-ab (a b n m to)
      "print (with PRINC) a total of N copies of A or B, printing N-M of A
    followed by M of B, to TO.  Do not print a newline"
      (cond  ((> n 0)
              (princ (if (<= n m) b a) to)
              (princ-ab a b (- n 1) m to))
             ((= n 0)
              nil)
             ((< n 0)
              (error "negative-creep"))))