Node:graph-body-print, Next:recursive-graph-body-print, Previous:Columns of a graph, Up:Readying a Graph
graph-body-print FunctionAfter 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.
graph-symbol, graph-blank,
column-of-graph, which are in
and graph-body-print.
(graph-body-print '(1 2 3 4 6 4 3 5 7 6 5 2 3))
*scratch* buffer and place the cursor where you
want the graph to start.
eval-expression).
graph-body-print expression into the minibuffer
with C-y (yank).
graph-body-print expression.
Emacs will print a graph like this:
*
* **
* ****
*** ****
********* *
************
*************