Go to the previous, next section.

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@dots{}"
  (let ((height  @dots{}
         @dots{}))

    (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 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-ESC ( 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:

                    *    
                *   **   
                *  ****  
               *** ****  
              ********* *
             ************
            *************

Go to the previous, next section.