eintr: graph-body-print

 
 15.1 The ‘graph-body-print’ Function
 ====================================
 
 After our preparation in the preceding section, the ‘graph-body-print’
 function is straightforward.  The function will print column after
 column of asterisks and blanks, using the elements of a numbers’ list to
 specify the number of asterisks in each column.  This is a repetitive
 act, which means we can use a decrementing ‘while’ loop or recursive
 function for the job.  In this section, we will write the definition
 using a ‘while’ loop.
 
    The ‘column-of-graph’ function requires the height of the graph as an
 argument, so we should determine and record that as a local variable.
 
    This leads us to the following template for the ‘while’ loop version
 of this function:
 
      (defun graph-body-print (numbers-list)
        "DOCUMENTATION..."
        (let ((height  ...
               ...))
 
          (while numbers-list
            INSERT-COLUMNS-AND-REPOSITION-POINT
            (setq numbers-list (cdr numbers-list)))))
 
 We need to fill in the slots of the template.
 
    Clearly, we can use the ‘(apply 'max numbers-list)’ expression to
 determine the height of the graph.
 
    The ‘while’ loop will cycle through the ‘numbers-list’ one element at
 a time.  As it is shortened by the ‘(setq numbers-list (cdr
 numbers-list))’ expression, the CAR of each instance of the list is the
 value of the argument for ‘column-of-graph’.
 
    At each cycle of the ‘while’ loop, the ‘insert-rectangle’ function
 inserts the list returned by ‘column-of-graph’.  Since the
 ‘insert-rectangle’ function moves point to the lower right of the
 inserted rectangle, we need to save the location of point at the time
 the rectangle is inserted, move back to that position after the
 rectangle is inserted, and then move horizontally to the next place from
 which ‘insert-rectangle’ is called.
 
    If the inserted columns are one character wide, as they will be if
 single blanks and asterisks are used, the repositioning command is
 simply ‘(forward-char 1)’; however, the width of a column may be greater
 than one.  This means that the repositioning command should be written
 ‘(forward-char symbol-width)’.  The ‘symbol-width’ itself is the length
 of a ‘graph-blank’ and can be found using the expression ‘(length
 graph-blank)’.  The best place to bind the ‘symbol-width’ variable to
 the value of the width of graph column is in the varlist of the ‘let’
 expression.
 
    These considerations lead to the following function definition:
 
      (defun graph-body-print (numbers-list)
        "Print a bar graph of the NUMBERS-LIST.
      The numbers-list consists of the Y-axis values."
 
        (let ((height (apply 'max numbers-list))
              (symbol-width (length graph-blank))
              from-position)
 
          (while numbers-list
            (setq from-position (point))
            (insert-rectangle
             (column-of-graph height (car numbers-list)))
            (goto-char from-position)
            (forward-char symbol-width)
            ;; Draw graph column by column.
            (sit-for 0)
            (setq numbers-list (cdr numbers-list)))
          ;; Place point for X axis labels.
          (forward-line height)
          (insert "\n")
      ))
 
 The one unexpected expression in this function is the ‘(sit-for 0)’
 expression in the ‘while’ loop.  This expression makes the graph
 printing operation more interesting to watch than it would be otherwise.
 The expression causes Emacs to “sit” or do nothing for a zero length of
 time and then redraw the screen.  Placed here, it causes Emacs to redraw
 the screen column by column.  Without it, Emacs would not redraw the
 screen until the function exits.
 
    We can test ‘graph-body-print’ with a short list of numbers.
 
   1. Install ‘graph-symbol’, ‘graph-blank’, ‘column-of-graph’, which are
      in SeeColumns of a graph, and ‘graph-body-print’.
 
   2. Copy the following expression:
 
           (graph-body-print '(1 2 3 4 6 4 3 5 7 6 5 2 3))
 
   3. Switch to the ‘*scratch*’ buffer and place the cursor where you
      want the graph to start.
 
   4. Type ‘M-:’ (‘eval-expression’).
 
   5. Yank the ‘graph-body-print’ expression into the minibuffer with
      ‘C-y’ (‘yank)’.
 
   6. Press <RET> to evaluate the ‘graph-body-print’ expression.
 
    Emacs will print a graph like this:
 
                          *
                      *   **
                      *  ****
                     *** ****
                    ********* *
                   ************
                  *************