1This is emacs-lisp-intro.info, produced by makeinfo version 4.0b from 2emacs-lisp-intro.texi. 3 4INFO-DIR-SECTION Emacs 5START-INFO-DIR-ENTRY 6* Emacs Lisp Intro: (eintr). 7 A simple introduction to Emacs Lisp programming. 8END-INFO-DIR-ENTRY 9 10 This is an introduction to `Programming in Emacs Lisp', for people 11who are not programmers. 12 13 Edition 2.04, 2001 Dec 17 14 15 Copyright (C) 1990, '91, '92, '93, '94, '95, '97, 2001 Free Software 16Foundation, Inc. 17 18 Permission is granted to copy, distribute and/or modify this document 19under the terms of the GNU Free Documentation License, Version 1.1 or 20any later version published by the Free Software Foundation; with the 21Invariant Section being the Preface, with the Front-Cover Texts being 22no Front-Cover Texts, and with the Back-Cover Texts being no Back-Cover 23Texts. A copy of the license is included in the section entitled "GNU 24Free Documentation License". 25 26 27File: emacs-lisp-intro.info, Node: rotate-yk-ptr negative arg, Prev: rotate-yk-ptr arg, Up: yank 28 29Passing a negative argument 30........................... 31 32 Finally, the question arises, what happens if either the remainder 33function, `%', or the `nthcdr' function is passed a negative argument, 34as they quite well may? 35 36 The answers can be found by a quick test. When `(% -1 5)' is 37evaluated, a negative number is returned; and if `nthcdr' is called 38with a negative number, it returns the same value as if it were called 39with a first argument of zero. This can be seen be evaluating the 40following code. 41 42 Here the `=>' points to the result of evaluating the code preceding 43it. This was done by positioning the cursor after the code and typing 44`C-x C-e' (`eval-last-sexp') in the usual fashion. You can do this if 45you are reading this in Info inside of GNU Emacs. 46 47 (% -1 5) 48 => -1 49 50 (setq animals '(cats dogs elephants)) 51 => (cats dogs elephants) 52 53 (nthcdr 1 animals) 54 => (dogs elephants) 55 56 (nthcdr 0 animals) 57 => (cats dogs elephants) 58 59 (nthcdr -1 animals) 60 => (cats dogs elephants) 61 62 So, if a minus sign or a negative number is passed to `yank', the 63`kill-ring-yank-point' is rotated backwards until it reaches the 64beginning of the list. Then it stays there. Unlike the other case, 65when it jumps from the end of the list to the beginning of the list, 66making a ring, it stops. This makes sense. You often want to get back 67to the most recently clipped out piece of text, but you don't usually 68want to insert text from as many as thirty kill commands ago. So you 69need to work through the ring to get to the end, but won't cycle around 70it inadvertently if you are trying to come back to the beginning. 71 72 Incidentally, any number passed to `yank' with a minus sign 73preceding it will be treated as -1. This is evidently a simplification 74for writing the program. You don't need to jump back towards the 75beginning of the kill ring more than one place at a time and doing this 76is easier than writing a function to determine the magnitude of the 77number that follows the minus sign. 78 79 80File: emacs-lisp-intro.info, Node: yank-pop, Prev: yank, Up: Kill Ring 81 82`yank-pop' 83========== 84 85 After understanding `yank', the `yank-pop' function is easy. 86Leaving out the documentation to save space, it looks like this: 87 88 (defun yank-pop (arg) 89 (interactive "*p") 90 (if (not (eq last-command 'yank)) 91 (error "Previous command was not a yank")) 92 (setq this-command 'yank) 93 (let ((before (< (point) (mark)))) 94 (delete-region (point) (mark)) 95 (rotate-yank-pointer arg) 96 (set-mark (point)) 97 (insert (car kill-ring-yank-pointer)) 98 (if before (exchange-point-and-mark)))) 99 100 The function is interactive with a small `p' so the prefix argument 101is processed and passed to the function. The command can only be used 102after a previous yank; otherwise an error message is sent. This check 103uses the variable `last-command' which is discussed elsewhere. (*Note 104copy-region-as-kill::.) 105 106 The `let' clause sets the variable `before' to true or false 107depending whether point is before or after mark and then the region 108between point and mark is deleted. This is the region that was just 109inserted by the previous yank and it is this text that will be 110replaced. Next the `kill-ring-yank-pointer' is rotated so that the 111previously inserted text is not reinserted yet again. Mark is set at 112the beginning of the place the new text will be inserted and then the 113first element to which `kill-ring-yank-pointer' points is inserted. 114This leaves point after the new text. If in the previous yank, point 115was left before the inserted text, point and mark are now exchanged so 116point is again left in front of the newly inserted text. That is all 117there is to it! 118 119 120File: emacs-lisp-intro.info, Node: Full Graph, Next: GNU Free Documentation License, Prev: Kill Ring, Up: Top 121 122A Graph with Labelled Axes 123************************** 124 125 Printed axes help you understand a graph. They convey scale. In an 126earlier chapter (*note Readying a Graph: Readying a Graph.), we wrote 127the code to print the body of a graph. Here we write the code for 128printing and labelling vertical and horizontal axes, along with the 129body itself. 130 131* Menu: 132 133* Labelled Example:: 134* print-graph Varlist:: `let' expression in `print-graph'. 135* print-Y-axis:: Print a label for the vertical axis. 136* print-X-axis:: Print a horizontal label. 137* Print Whole Graph:: The function to print a complete graph. 138 139 140File: emacs-lisp-intro.info, Node: Labelled Example, Next: print-graph Varlist, Prev: Full Graph, Up: Full Graph 141 142Labelled Example Graph 143====================== 144 145 Since insertions fill a buffer to the right and below point, the new 146graph printing function should first print the Y or vertical axis, then 147the body of the graph, and finally the X or horizontal axis. This 148sequence lays out for us the contents of the function: 149 150 1. Set up code. 151 152 2. Print Y axis. 153 154 3. Print body of graph. 155 156 4. Print X axis. 157 158 Here is an example of how a finished graph should look: 159 160 10 - 161 * 162 * * 163 * ** 164 * *** 165 5 - * ******* 166 * *** ******* 167 ************* 168 *************** 169 1 - **************** 170 | | | | 171 1 5 10 15 172 173In this graph, both the vertical and the horizontal axes are labelled 174with numbers. However, in some graphs, the horizontal axis is time and 175would be better labelled with months, like this: 176 177 5 - * 178 * ** * 179 ******* 180 ********** ** 181 1 - ************** 182 | ^ | 183 Jan June Jan 184 185 Indeed, with a little thought, we can easily come up with a variety 186of vertical and horizontal labelling schemes. Our task could become 187complicated. But complications breed confusion. Rather than permit 188this, it is better choose a simple labelling scheme for our first 189effort, and to modify or replace it later. 190 191 These considerations suggest the following outline for the 192`print-graph' function: 193 194 (defun print-graph (numbers-list) 195 "DOCUMENTATION..." 196 (let ((height ... 197 ...)) 198 (print-Y-axis height ... ) 199 (graph-body-print numbers-list) 200 (print-X-axis ... ))) 201 202 We can work on each part of the `print-graph' function definition in 203turn. 204 205 206File: emacs-lisp-intro.info, Node: print-graph Varlist, Next: print-Y-axis, Prev: Labelled Example, Up: Full Graph 207 208The `print-graph' Varlist 209========================= 210 211 In writing the `print-graph' function, the first task is to write 212the varlist in the `let' expression. (We will leave aside for the 213moment any thoughts about making the function interactive or about the 214contents of its documentation string.) 215 216 The varlist should set several values. Clearly, the top of the label 217for the vertical axis must be at least the height of the graph, which 218means that we must obtain this information here. Note that the 219`print-graph-body' function also requires this information. There is 220no reason to calculate the height of the graph in two different places, 221so we should change `print-graph-body' from the way we defined it 222earlier to take advantage of the calculation. 223 224 Similarly, both the function for printing the X axis labels and the 225`print-graph-body' function need to learn the value of the width of 226each symbol. We can perform the calculation here and change the 227definition for `print-graph-body' from the way we defined it in the 228previous chapter. 229 230 The length of the label for the horizontal axis must be at least as 231long as the graph. However, this information is used only in the 232function that prints the horizontal axis, so it does not need to be 233calculated here. 234 235 These thoughts lead us directly to the following form for the varlist 236in the `let' for `print-graph': 237 238 (let ((height (apply 'max numbers-list)) ; First version. 239 (symbol-width (length graph-blank))) 240 241As we shall see, this expression is not quite right. 242 243 244File: emacs-lisp-intro.info, Node: print-Y-axis, Next: print-X-axis, Prev: print-graph Varlist, Up: Full Graph 245 246The `print-Y-axis' Function 247=========================== 248 249 The job of the `print-Y-axis' function is to print a label for the 250vertical axis that looks like this: 251 252 10 - 253 254 255 256 257 5 - 258 259 260 261 1 - 262 263The function should be passed the height of the graph, and then should 264construct and insert the appropriate numbers and marks. 265 266 It is easy enough to see in the figure what the Y axis label should 267look like; but to say in words, and then to write a function definition 268to do the job is another matter. It is not quite true to say that we 269want a number and a tic every five lines: there are only three lines 270between the `1' and the `5' (lines 2, 3, and 4), but four lines between 271the `5' and the `10' (lines 6, 7, 8, and 9). It is better to say that 272we want a number and a tic mark on the base line (number 1) and then 273that we want a number and a tic on the fifth line from the bottom and 274on every line that is a multiple of five. 275 276* Menu: 277 278* Height of label:: What height for the Y axis? 279* Compute a Remainder:: How to compute the remainder of a division. 280* Y Axis Element:: Construct a line for the Y axis. 281* Y-axis-column:: Generate a list of Y axis labels. 282* print-Y-axis Penultimate:: A not quite final version. 283 284 285File: emacs-lisp-intro.info, Node: Height of label, Next: Compute a Remainder, Prev: print-Y-axis, Up: print-Y-axis 286 287What height should the label be? 288-------------------------------- 289 290 The next issue is what height the label should be? Suppose the 291maximum height of tallest column of the graph is seven. Should the 292highest label on the Y axis be `5 -', and should the graph stick up 293above the label? Or should the highest label be `7 -', and mark the 294peak of the graph? Or should the highest label be `10 -', which is a 295multiple of five, and be higher than the topmost value of the graph? 296 297 The latter form is preferred. Most graphs are drawn within 298rectangles whose sides are an integral number of steps long--5, 10, 15, 299and so on for a step distance of five. But as soon as we decide to use 300a step height for the vertical axis, we discover that the simple 301expression in the varlist for computing the height is wrong. The 302expression is `(apply 'max numbers-list)'. This returns the precise 303height, not the maximum height plus whatever is necessary to round up 304to the nearest multiple of five. A more complex expression is required. 305 306 As usual in cases like this, a complex problem becomes simpler if it 307is divided into several smaller problems. 308 309 First, consider the case when the highest value of the graph is an 310integral multiple of five--when it is 5, 10, 15 ,or some higher 311multiple of five. We can use this value as the Y axis height. 312 313 A fairly simply way to determine whether a number is a multiple of 314five is to divide it by five and see if the division results in a 315remainder. If there is no remainder, the number is a multiple of five. 316Thus, seven divided by five has a remainder of two, and seven is not 317an integral multiple of five. Put in slightly different language, more 318reminiscent of the classroom, five goes into seven once, with a 319remainder of two. However, five goes into ten twice, with no 320remainder: ten is an integral multiple of five. 321 322 323File: emacs-lisp-intro.info, Node: Compute a Remainder, Next: Y Axis Element, Prev: Height of label, Up: print-Y-axis 324 325Side Trip: Compute a Remainder 326------------------------------ 327 328 In Lisp, the function for computing a remainder is `%'. The 329function returns the remainder of its first argument divided by its 330second argument. As it happens, `%' is a function in Emacs Lisp that 331you cannot discover using `apropos': you find nothing if you type `M-x 332apropos <RET> remainder <RET>'. The only way to learn of the existence 333of `%' is to read about it in a book such as this or in the Emacs Lisp 334sources. The `%' function is used in the code for 335`rotate-yank-pointer', which is described in an appendix. (*Note The 336Body of `rotate-yank-pointer': rotate-yk-ptr body.) 337 338 You can try the `%' function by evaluating the following two 339expressions: 340 341 (% 7 5) 342 343 (% 10 5) 344 345The first expression returns 2 and the second expression returns 0. 346 347 To test whether the returned value is zero or some other number, we 348can use the `zerop' function. This function returns `t' if its 349argument, which must be a number, is zero. 350 351 (zerop (% 7 5)) 352 => nil 353 354 (zerop (% 10 5)) 355 => t 356 357 Thus, the following expression will return `t' if the height of the 358graph is evenly divisible by five: 359 360 (zerop (% height 5)) 361 362(The value of `height', of course, can be found from `(apply 'max 363numbers-list)'.) 364 365 On the other hand, if the value of `height' is not a multiple of 366five, we want to reset the value to the next higher multiple of five. 367This is straightforward arithmetic using functions with which we are 368already familiar. First, we divide the value of `height' by five to 369determine how many times five goes into the number. Thus, five goes 370into twelve twice. If we add one to this quotient and multiply by 371five, we will obtain the value of the next multiple of five that is 372larger than the height. Five goes into twelve twice. Add one to two, 373and multiply by five; the result is fifteen, which is the next multiple 374of five that is higher than twelve. The Lisp expression for this is: 375 376 (* (1+ (/ height 5)) 5) 377 378For example, if you evaluate the following, the result is 15: 379 380 (* (1+ (/ 12 5)) 5) 381 382 All through this discussion, we have been using `five' as the value 383for spacing labels on the Y axis; but we may want to use some other 384value. For generality, we should replace `five' with a variable to 385which we can assign a value. The best name I can think of for this 386variable is `Y-axis-label-spacing'. 387 388 Using this term, and an `if' expression, we produce the following: 389 390 (if (zerop (% height Y-axis-label-spacing)) 391 height 392 ;; else 393 (* (1+ (/ height Y-axis-label-spacing)) 394 Y-axis-label-spacing)) 395 396This expression returns the value of `height' itself if the height is 397an even multiple of the value of the `Y-axis-label-spacing' or else it 398computes and returns a value of `height' that is equal to the next 399higher multiple of the value of the `Y-axis-label-spacing'. 400 401 We can now include this expression in the `let' expression of the 402`print-graph' function (after first setting the value of 403`Y-axis-label-spacing'): 404 405 (defvar Y-axis-label-spacing 5 406 "Number of lines from one Y axis label to next.") 407 408 ... 409 (let* ((height (apply 'max numbers-list)) 410 (height-of-top-line 411 (if (zerop (% height Y-axis-label-spacing)) 412 height 413 ;; else 414 (* (1+ (/ height Y-axis-label-spacing)) 415 Y-axis-label-spacing))) 416 (symbol-width (length graph-blank)))) 417 ... 418 419(Note use of the `let*' function: the initial value of height is 420computed once by the `(apply 'max numbers-list)' expression and then 421the resulting value of `height' is used to compute its final value. 422*Note The `let*' expression: fwd-para let, for more about `let*'.) 423 424 425File: emacs-lisp-intro.info, Node: Y Axis Element, Next: Y-axis-column, Prev: Compute a Remainder, Up: print-Y-axis 426 427Construct a Y Axis Element 428-------------------------- 429 430 When we print the vertical axis, we want to insert strings such as 431`5 -' and `10 - ' every five lines. Moreover, we want the numbers and 432dashes to line up, so shorter numbers must be padded with leading 433spaces. If some of the strings use two digit numbers, the strings with 434single digit numbers must include a leading blank space before the 435number. 436 437 To figure out the length of the number, the `length' function is 438used. But the `length' function works only with a string, not with a 439number. So the number has to be converted from being a number to being 440a string. This is done with the `number-to-string' function. For 441example, 442 443 (length (number-to-string 35)) 444 => 2 445 446 (length (number-to-string 100)) 447 => 3 448 449(`number-to-string' is also called `int-to-string'; you will see this 450alternative name in various sources.) 451 452 In addition, in each label, each number is followed by a string such 453as ` - ', which we will call the `Y-axis-tic' marker. This variable is 454defined with `defvar': 455 456 (defvar Y-axis-tic " - " 457 "String that follows number in a Y axis label.") 458 459 The length of the Y label is the sum of the length of the Y axis tic 460mark and the length of the number of the top of the graph. 461 462 (length (concat (number-to-string height) Y-axis-tic))) 463 464 This value will be calculated by the `print-graph' function in its 465varlist as `full-Y-label-width' and passed on. (Note that we did not 466think to include this in the varlist when we first proposed it.) 467 468 To make a complete vertical axis label, a tic mark is concatenated 469with a number; and the two together may be preceded by one or more 470spaces depending on how long the number is. The label consists of 471three parts: the (optional) leading spaces, the number, and the tic 472mark. The function is passed the value of the number for the specific 473row, and the value of the width of the top line, which is calculated 474(just once) by `print-graph'. 475 476 (defun Y-axis-element (number full-Y-label-width) 477 "Construct a NUMBERed label element. 478 A numbered element looks like this ` 5 - ', 479 and is padded as needed so all line up with 480 the element for the largest number." 481 (let* ((leading-spaces 482 (- full-Y-label-width 483 (length 484 (concat (number-to-string number) 485 Y-axis-tic))))) 486 (concat 487 (make-string leading-spaces ? ) 488 (number-to-string number) 489 Y-axis-tic))) 490 491 The `Y-axis-element' function concatenates together the leading 492spaces, if any; the number, as a string; and the tic mark. 493 494 To figure out how many leading spaces the label will need, the 495function subtracts the actual length of the label--the length of the 496number plus the length of the tic mark--from the desired label width. 497 498 Blank spaces are inserted using the `make-string' function. This 499function takes two arguments: the first tells it how long the string 500will be and the second is a symbol for the character to insert, in a 501special format. The format is a question mark followed by a blank 502space, like this, `? '. *Note Character Type: (elisp)Character Type, 503for a description of the syntax for characters. 504 505 The `number-to-string' function is used in the concatenation 506expression, to convert the number to a string that is concatenated with 507the leading spaces and the tic mark. 508 509 510File: emacs-lisp-intro.info, Node: Y-axis-column, Next: print-Y-axis Penultimate, Prev: Y Axis Element, Up: print-Y-axis 511 512Create a Y Axis Column 513---------------------- 514 515 The preceding functions provide all the tools needed to construct a 516function that generates a list of numbered and blank strings to insert 517as the label for the vertical axis: 518 519 (defun Y-axis-column (height width-of-label) 520 "Construct list of Y axis labels and blank strings. 521 For HEIGHT of line above base and WIDTH-OF-LABEL." 522 (let (Y-axis) 523 (while (> height 1) 524 (if (zerop (% height Y-axis-label-spacing)) 525 ;; Insert label. 526 (setq Y-axis 527 (cons 528 (Y-axis-element height width-of-label) 529 Y-axis)) 530 ;; Else, insert blanks. 531 (setq Y-axis 532 (cons 533 (make-string width-of-label ? ) 534 Y-axis))) 535 (setq height (1- height))) 536 ;; Insert base line. 537 (setq Y-axis 538 (cons (Y-axis-element 1 width-of-label) Y-axis)) 539 (nreverse Y-axis))) 540 541 In this function, we start with the value of `height' and 542repetitively subtract one from its value. After each subtraction, we 543test to see whether the value is an integral multiple of the 544`Y-axis-label-spacing'. If it is, we construct a numbered label using 545the `Y-axis-element' function; if not, we construct a blank label using 546the `make-string' function. The base line consists of the number one 547followed by a tic mark. 548 549 550File: emacs-lisp-intro.info, Node: print-Y-axis Penultimate, Prev: Y-axis-column, Up: print-Y-axis 551 552The Not Quite Final Version of `print-Y-axis' 553--------------------------------------------- 554 555 The list constructed by the `Y-axis-column' function is passed to 556the `print-Y-axis' function, which inserts the list as a column. 557 558 (defun print-Y-axis (height full-Y-label-width) 559 "Insert Y axis using HEIGHT and FULL-Y-LABEL-WIDTH. 560 Height must be the maximum height of the graph. 561 Full width is the width of the highest label element." 562 ;; Value of height and full-Y-label-width 563 ;; are passed by `print-graph'. 564 (let ((start (point))) 565 (insert-rectangle 566 (Y-axis-column height full-Y-label-width)) 567 ;; Place point ready for inserting graph. 568 (goto-char start) 569 ;; Move point forward by value of full-Y-label-width 570 (forward-char full-Y-label-width))) 571 572 The `print-Y-axis' uses the `insert-rectangle' function to insert 573the Y axis labels created by the `Y-axis-column' function. In 574addition, it places point at the correct position for printing the body 575of the graph. 576 577 You can test `print-Y-axis': 578 579 1. Install 580 581 Y-axis-label-spacing 582 Y-axis-tic 583 Y-axis-element 584 Y-axis-column 585 print-Y-axis 586 587 2. Copy the following expression: 588 589 (print-Y-axis 12 5) 590 591 3. Switch to the `*scratch*' buffer and place the cursor where you 592 want the axis labels to start. 593 594 4. Type `M-:' (`eval-expression'). 595 596 5. Yank the `graph-body-print' expression into the minibuffer with 597 `C-y' (`yank)'. 598 599 6. Press <RET> to evaluate the expression. 600 601 Emacs will print labels vertically, the top one being `10 - '. (The 602`print-graph' function will pass the value of `height-of-top-line', 603which in this case would end up as 15.) 604 605 606File: emacs-lisp-intro.info, Node: print-X-axis, Next: Print Whole Graph, Prev: print-Y-axis, Up: Full Graph 607 608The `print-X-axis' Function 609=========================== 610 611 X axis labels are much like Y axis labels, except that the tics are 612on a line above the numbers. Labels should look like this: 613 614 | | | | 615 1 5 10 15 616 617 The first tic is under the first column of the graph and is preceded 618by several blank spaces. These spaces provide room in rows above for 619the Y axis labels. The second, third, fourth, and subsequent tics are 620all spaced equally, according to the value of `X-axis-label-spacing'. 621 622 The second row of the X axis consists of numbers, preceded by several 623blank spaces and also separated according to the value of the variable 624`X-axis-label-spacing'. 625 626 The value of the variable `X-axis-label-spacing' should itself be 627measured in units of `symbol-width', since you may want to change the 628width of the symbols that you are using to print the body of the graph 629without changing the ways the graph is labelled. 630 631* Menu: 632 633* Similarities differences:: Much like `print-Y-axis', but not exactly. 634* X Axis Tic Marks:: Create tic marks for the horizontal axis. 635 636 637File: emacs-lisp-intro.info, Node: Similarities differences, Next: X Axis Tic Marks, Prev: print-X-axis, Up: print-X-axis 638 639Similarities and differences 640---------------------------- 641 642 The `print-X-axis' function is constructed in more or less the same 643fashion as the `print-Y-axis' function except that it has two lines: 644the line of tic marks and the numbers. We will write a separate 645function to print each line and then combine them within the 646`print-X-axis' function. 647 648 This is a three step process: 649 650 1. Write a function to print the X axis tic marks, 651 `print-X-axis-tic-line'. 652 653 2. Write a function to print the X numbers, 654 `print-X-axis-numbered-line'. 655 656 3. Write a function to print both lines, the `print-X-axis' function, 657 using `print-X-axis-tic-line' and `print-X-axis-numbered-line'. 658 659 660File: emacs-lisp-intro.info, Node: X Axis Tic Marks, Prev: Similarities differences, Up: print-X-axis 661 662X Axis Tic Marks 663---------------- 664 665 The first function should print the X axis tic marks. We must 666specify the tic marks themselves and their spacing: 667 668 (defvar X-axis-label-spacing 669 (if (boundp 'graph-blank) 670 (* 5 (length graph-blank)) 5) 671 "Number of units from one X axis label to next.") 672 673(Note that the value of `graph-blank' is set by another `defvar'. The 674`boundp' predicate checks whether it has already been set; `boundp' 675returns `nil' if it has not. If `graph-blank' were unbound and we did 676not use this conditional construction, in GNU Emacs 21, we would enter 677the debugger and see an error message saying 678`Debugger entered--Lisp error: (void-variable graph-blank)'.) 679 680 Here is the `defvar' for `X-axis-tic-symbol': 681 682 (defvar X-axis-tic-symbol "|" 683 "String to insert to point to a column in X axis.") 684 685 The goal is to make a line that looks like this: 686 687 | | | | 688 689 The first tic is indented so that it is under the first column, 690which is indented to provide space for the Y axis labels. 691 692 A tic element consists of the blank spaces that stretch from one tic 693to the next plus a tic symbol. The number of blanks is determined by 694the width of the tic symbol and the `X-axis-label-spacing'. 695 696 The code looks like this: 697 698 ;;; X-axis-tic-element 699 ... 700 (concat 701 (make-string 702 ;; Make a string of blanks. 703 (- (* symbol-width X-axis-label-spacing) 704 (length X-axis-tic-symbol)) 705 ? ) 706 ;; Concatenate blanks with tic symbol. 707 X-axis-tic-symbol) 708 ... 709 710 Next, we determine how many blanks are needed to indent the first tic 711mark to the first column of the graph. This uses the value of 712`full-Y-label-width' passed it by the `print-graph' function. 713 714 The code to make `X-axis-leading-spaces' looks like this: 715 716 ;; X-axis-leading-spaces 717 ... 718 (make-string full-Y-label-width ? ) 719 ... 720 721 We also need to determine the length of the horizontal axis, which is 722the length of the numbers list, and the number of tics in the horizontal 723axis: 724 725 ;; X-length 726 ... 727 (length numbers-list) 728 729 ;; tic-width 730 ... 731 (* symbol-width X-axis-label-spacing) 732 733 ;; number-of-X-tics 734 (if (zerop (% (X-length tic-width))) 735 (/ (X-length tic-width)) 736 (1+ (/ (X-length tic-width)))) 737 738 All this leads us directly to the function for printing the X axis 739tic line: 740 741 (defun print-X-axis-tic-line 742 (number-of-X-tics X-axis-leading-spaces X-axis-tic-element) 743 "Print tics for X axis." 744 (insert X-axis-leading-spaces) 745 (insert X-axis-tic-symbol) ; Under first column. 746 ;; Insert second tic in the right spot. 747 (insert (concat 748 (make-string 749 (- (* symbol-width X-axis-label-spacing) 750 ;; Insert white space up to second tic symbol. 751 (* 2 (length X-axis-tic-symbol))) 752 ? ) 753 X-axis-tic-symbol)) 754 ;; Insert remaining tics. 755 (while (> number-of-X-tics 1) 756 (insert X-axis-tic-element) 757 (setq number-of-X-tics (1- number-of-X-tics)))) 758 759 The line of numbers is equally straightforward: 760 761 First, we create a numbered element with blank spaces before each 762number: 763 764 (defun X-axis-element (number) 765 "Construct a numbered X axis element." 766 (let ((leading-spaces 767 (- (* symbol-width X-axis-label-spacing) 768 (length (number-to-string number))))) 769 (concat (make-string leading-spaces ? ) 770 (number-to-string number)))) 771 772 Next, we create the function to print the numbered line, starting 773with the number "1" under the first column: 774 775 (defun print-X-axis-numbered-line 776 (number-of-X-tics X-axis-leading-spaces) 777 "Print line of X-axis numbers" 778 (let ((number X-axis-label-spacing)) 779 (insert X-axis-leading-spaces) 780 (insert "1") 781 (insert (concat 782 (make-string 783 ;; Insert white space up to next number. 784 (- (* symbol-width X-axis-label-spacing) 2) 785 ? ) 786 (number-to-string number))) 787 ;; Insert remaining numbers. 788 (setq number (+ number X-axis-label-spacing)) 789 (while (> number-of-X-tics 1) 790 (insert (X-axis-element number)) 791 (setq number (+ number X-axis-label-spacing)) 792 (setq number-of-X-tics (1- number-of-X-tics))))) 793 794 Finally, we need to write the `print-X-axis' that uses 795`print-X-axis-tic-line' and `print-X-axis-numbered-line'. 796 797 The function must determine the local values of the variables used 798by both `print-X-axis-tic-line' and `print-X-axis-numbered-line', and 799then it must call them. Also, it must print the carriage return that 800separates the two lines. 801 802 The function consists of a varlist that specifies five local 803variables, and calls to each of the two line printing functions: 804 805 (defun print-X-axis (numbers-list) 806 "Print X axis labels to length of NUMBERS-LIST." 807 (let* ((leading-spaces 808 (make-string full-Y-label-width ? )) 809 ;; symbol-width is provided by graph-body-print 810 (tic-width (* symbol-width X-axis-label-spacing)) 811 (X-length (length numbers-list)) 812 (X-tic 813 (concat 814 (make-string 815 ;; Make a string of blanks. 816 (- (* symbol-width X-axis-label-spacing) 817 (length X-axis-tic-symbol)) 818 ? ) 819 ;; Concatenate blanks with tic symbol. 820 X-axis-tic-symbol)) 821 (tic-number 822 (if (zerop (% X-length tic-width)) 823 (/ X-length tic-width) 824 (1+ (/ X-length tic-width))))) 825 (print-X-axis-tic-line tic-number leading-spaces X-tic) 826 (insert "\n") 827 (print-X-axis-numbered-line tic-number leading-spaces))) 828 829 You can test `print-X-axis': 830 831 1. Install `X-axis-tic-symbol', `X-axis-label-spacing', 832 `print-X-axis-tic-line', as well as `X-axis-element', 833 `print-X-axis-numbered-line', and `print-X-axis'. 834 835 2. Copy the following expression: 836 837 (progn 838 (let ((full-Y-label-width 5) 839 (symbol-width 1)) 840 (print-X-axis 841 '(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16)))) 842 843 3. Switch to the `*scratch*' buffer and place the cursor where you 844 want the axis labels to start. 845 846 4. Type `M-:' (`eval-expression'). 847 848 5. Yank the test expression into the minibuffer with `C-y' (`yank)'. 849 850 6. Press <RET> to evaluate the expression. 851 852 Emacs will print the horizontal axis like this: 853 854 | | | | | 855 1 5 10 15 20 856 857 858File: emacs-lisp-intro.info, Node: Print Whole Graph, Prev: print-X-axis, Up: Full Graph 859 860Printing the Whole Graph 861======================== 862 863 Now we are nearly ready to print the whole graph. 864 865 The function to print the graph with the proper labels follows the 866outline we created earlier (*note A Graph with Labelled Axes: Full 867Graph.), but with additions. 868 869 Here is the outline: 870 871 (defun print-graph (numbers-list) 872 "DOCUMENTATION..." 873 (let ((height ... 874 ...)) 875 (print-Y-axis height ... ) 876 (graph-body-print numbers-list) 877 (print-X-axis ... ))) 878 879* Menu: 880 881* The final version:: A few changes. 882* Test print-graph:: Run a short test. 883* Graphing words in defuns:: Executing the final code. 884* lambda:: How to write an anonymous function. 885* mapcar:: Apply a function to elements of a list. 886* Another Bug:: Yet another bug ... most insidious. 887* Final printed graph:: The graph itself! 888 889 890File: emacs-lisp-intro.info, Node: The final version, Next: Test print-graph, Prev: Print Whole Graph, Up: Print Whole Graph 891 892Changes for the Final Version 893----------------------------- 894 895 The final version is different from what we planned in two ways: 896first, it contains additional values calculated once in the varlist; 897second, it carries an option to specify the labels' increment per row. 898This latter feature turns out to be essential; otherwise, a graph may 899have more rows than fit on a display or on a sheet of paper. 900 901 This new feature requires a change to the `Y-axis-column' function, 902to add `vertical-step' to it. The function looks like this: 903 904 ;;; Final version. 905 (defun Y-axis-column 906 (height width-of-label &optional vertical-step) 907 "Construct list of labels for Y axis. 908 HEIGHT is maximum height of graph. 909 WIDTH-OF-LABEL is maximum width of label. 910 VERTICAL-STEP, an option, is a positive integer 911 that specifies how much a Y axis label increments 912 for each line. For example, a step of 5 means 913 that each line is five units of the graph." 914 (let (Y-axis 915 (number-per-line (or vertical-step 1))) 916 (while (> height 1) 917 (if (zerop (% height Y-axis-label-spacing)) 918 ;; Insert label. 919 (setq Y-axis 920 (cons 921 (Y-axis-element 922 (* height number-per-line) 923 width-of-label) 924 Y-axis)) 925 ;; Else, insert blanks. 926 (setq Y-axis 927 (cons 928 (make-string width-of-label ? ) 929 Y-axis))) 930 (setq height (1- height))) 931 ;; Insert base line. 932 (setq Y-axis (cons (Y-axis-element 933 (or vertical-step 1) 934 width-of-label) 935 Y-axis)) 936 (nreverse Y-axis))) 937 938 The values for the maximum height of graph and the width of a symbol 939are computed by `print-graph' in its `let' expression; so 940`graph-body-print' must be changed to accept them. 941 942 ;;; Final version. 943 (defun graph-body-print (numbers-list height symbol-width) 944 "Print a bar graph of the NUMBERS-LIST. 945 The numbers-list consists of the Y-axis values. 946 HEIGHT is maximum height of graph. 947 SYMBOL-WIDTH is number of each column." 948 (let (from-position) 949 (while numbers-list 950 (setq from-position (point)) 951 (insert-rectangle 952 (column-of-graph height (car numbers-list))) 953 (goto-char from-position) 954 (forward-char symbol-width) 955 ;; Draw graph column by column. 956 (sit-for 0) 957 (setq numbers-list (cdr numbers-list))) 958 ;; Place point for X axis labels. 959 (forward-line height) 960 (insert "\n"))) 961 962 Finally, the code for the `print-graph' function: 963 964 ;;; Final version. 965 (defun print-graph 966 (numbers-list &optional vertical-step) 967 "Print labelled bar graph of the NUMBERS-LIST. 968 The numbers-list consists of the Y-axis values. 969 970 Optionally, VERTICAL-STEP, a positive integer, 971 specifies how much a Y axis label increments for 972 each line. For example, a step of 5 means that 973 each row is five units." 974 (let* ((symbol-width (length graph-blank)) 975 ;; `height' is both the largest number 976 ;; and the number with the most digits. 977 (height (apply 'max numbers-list)) 978 (height-of-top-line 979 (if (zerop (% height Y-axis-label-spacing)) 980 height 981 ;; else 982 (* (1+ (/ height Y-axis-label-spacing)) 983 Y-axis-label-spacing))) 984 (vertical-step (or vertical-step 1)) 985 (full-Y-label-width 986 (length 987 (concat 988 (number-to-string 989 (* height-of-top-line vertical-step)) 990 Y-axis-tic)))) 991 992 (print-Y-axis 993 height-of-top-line full-Y-label-width vertical-step) 994 (graph-body-print 995 numbers-list height-of-top-line symbol-width) 996 (print-X-axis numbers-list))) 997 998 999File: emacs-lisp-intro.info, Node: Test print-graph, Next: Graphing words in defuns, Prev: The final version, Up: Print Whole Graph 1000 1001Testing `print-graph' 1002--------------------- 1003 1004 We can test the `print-graph' function with a short list of numbers: 1005 1006 1. Install the final versions of `Y-axis-column', `graph-body-print', 1007 and `print-graph' (in addition to the rest of the code.) 1008 1009 2. Copy the following expression: 1010 1011 (print-graph '(3 2 5 6 7 5 3 4 6 4 3 2 1)) 1012 1013 3. Switch to the `*scratch*' buffer and place the cursor where you 1014 want the axis labels to start. 1015 1016 4. Type `M-:' (`eval-expression'). 1017 1018 5. Yank the test expression into the minibuffer with `C-y' (`yank)'. 1019 1020 6. Press <RET> to evaluate the expression. 1021 1022 Emacs will print a graph that looks like this: 1023 1024 10 - 1025 1026 1027 * 1028 ** * 1029 5 - **** * 1030 **** *** 1031 * ********* 1032 ************ 1033 1 - ************* 1034 1035 | | | | 1036 1 5 10 15 1037 1038 On the other hand, if you pass `print-graph' a `vertical-step' value 1039of 2, by evaluating this expression: 1040 1041 (print-graph '(3 2 5 6 7 5 3 4 6 4 3 2 1) 2) 1042 1043The graph looks like this: 1044 1045 20 - 1046 1047 1048 * 1049 ** * 1050 10 - **** * 1051 **** *** 1052 * ********* 1053 ************ 1054 2 - ************* 1055 1056 | | | | 1057 1 5 10 15 1058 1059(A question: is the `2' on the bottom of the vertical axis a bug or a 1060feature? If you think it is a bug, and should be a `1' instead, (or 1061even a `0'), you can modify the sources.) 1062 1063 1064File: emacs-lisp-intro.info, Node: Graphing words in defuns, Next: lambda, Prev: Test print-graph, Up: Print Whole Graph 1065 1066Graphing Numbers of Words and Symbols 1067------------------------------------- 1068 1069 Now for the graph for which all this code was written: a graph that 1070shows how many function definitions contain fewer than 10 words and 1071symbols, how many contain between 10 and 19 words and symbols, how many 1072contain between 20 and 29 words and symbols, and so on. 1073 1074 This is a multi-step process. First make sure you have loaded all 1075the requisite code. 1076 1077 It is a good idea to reset the value of `top-of-ranges' in case you 1078have set it to some different value. You can evaluate the following: 1079 1080 (setq top-of-ranges 1081 '(10 20 30 40 50 1082 60 70 80 90 100 1083 110 120 130 140 150 1084 160 170 180 190 200 1085 210 220 230 240 250 1086 260 270 280 290 300) 1087 1088Next create a list of the number of words and symbols in each range. 1089 1090Evaluate the following: 1091 1092 (setq list-for-graph 1093 (defuns-per-range 1094 (sort 1095 (recursive-lengths-list-many-files 1096 (directory-files "/usr/local/emacs/lisp" 1097 t ".+el$")) 1098 '<) 1099 top-of-ranges)) 1100 1101On my machine, this takes about an hour. It looks though 303 Lisp 1102files in my copy of Emacs version 19.23. After all that computing, the 1103`list-for-graph' has this value: 1104 1105 (537 1027 955 785 594 483 349 292 224 199 166 120 116 99 1106 90 80 67 48 52 45 41 33 28 26 25 20 12 28 11 13 220) 1107 1108This means that my copy of Emacs has 537 function definitions with 1109fewer than 10 words or symbols in them, 1,027 function definitions with 111010 to 19 words or symbols in them, 955 function definitions with 20 to 111129 words or symbols in them, and so on. 1112 1113 Clearly, just by looking at this list we can see that most function 1114definitions contain ten to thirty words and symbols. 1115 1116 Now for printing. We do _not_ want to print a graph that is 1,030 1117lines high ... Instead, we should print a graph that is fewer than 1118twenty-five lines high. A graph that height can be displayed on almost 1119any monitor, and easily printed on a sheet of paper. 1120 1121 This means that each value in `list-for-graph' must be reduced to 1122one-fiftieth its present value. 1123 1124 Here is a short function to do just that, using two functions we have 1125not yet seen, `mapcar' and `lambda'. 1126 1127 (defun one-fiftieth (full-range) 1128 "Return list, each number one-fiftieth of previous." 1129 (mapcar '(lambda (arg) (/ arg 50)) full-range)) 1130 1131 1132File: emacs-lisp-intro.info, Node: lambda, Next: mapcar, Prev: Graphing words in defuns, Up: Print Whole Graph 1133 1134A `lambda' Expression: Useful Anonymity 1135--------------------------------------- 1136 1137 `lambda' is the symbol for an anonymous function, a function without 1138a name. Every time you use an anonymous function, you need to include 1139its whole body. 1140 1141Thus, 1142 1143 (lambda (arg) (/ arg 50)) 1144 1145is a function definition that says `return the value resulting from 1146dividing whatever is passed to me as `arg' by 50'. 1147 1148 Earlier, for example, we had a function `multiply-by-seven'; it 1149multiplied its argument by 7. This function is similar, except it 1150divides its argument by 50; and, it has no name. The anonymous 1151equivalent of `multiply-by-seven' is: 1152 1153 (lambda (number) (* 7 number)) 1154 1155(*Note The `defun' Special Form: defun.) 1156 1157If we want to multiply 3 by 7, we can write: 1158 1159 (multiply-by-seven 3) 1160 \_______________/ ^ 1161 | | 1162 function argument 1163 1164 1165 1166 1167This expression returns 21. 1168 1169Similarly, we can write: 1170 1171 ((lambda (number) (* 7 number)) 3) 1172 \____________________________/ ^ 1173 | | 1174 anonymous function argument 1175 1176 1177 1178 1179If we want to divide 100 by 50, we can write: 1180 1181 ((lambda (arg) (/ arg 50)) 100) 1182 \______________________/ \_/ 1183 | | 1184 anonymous function argument 1185 1186 1187 1188 1189This expression returns 2. The 100 is passed to the function, which 1190divides that number by 50. 1191 1192 *Note Lambda Expressions: (elisp)Lambda Expressions, for more about 1193`lambda'. Lisp and lambda expressions derive from the Lambda Calculus. 1194 1195 1196File: emacs-lisp-intro.info, Node: mapcar, Next: Another Bug, Prev: lambda, Up: Print Whole Graph 1197 1198The `mapcar' Function 1199--------------------- 1200 1201 `mapcar' is a function that calls its first argument with each 1202element of its second argument, in turn. The second argument must be a 1203sequence. 1204 1205 The `map' part of the name comes from the mathematical phrase, 1206`mapping over a domain', meaning to apply a function to each of the 1207elements in a domain. The mathematical phrase is based on the metaphor 1208of a surveyor walking, one step at a time, over an area he is mapping. 1209And `car', of course, comes from the Lisp notion of the first of a list. 1210 1211For example, 1212 1213 (mapcar '1+ '(2 4 6)) 1214 => (3 5 7) 1215 1216The function `1+' which adds one to its argument, is executed on _each_ 1217element of the list, and a new list is returned. 1218 1219 Contrast this with `apply', which applies its first argument to all 1220the remaining. (*Note Readying a Graph: Readying a Graph, for a 1221explanation of `apply'.) 1222 1223 In the definition of `one-fiftieth', the first argument is the 1224anonymous function: 1225 1226 (lambda (arg) (/ arg 50)) 1227 1228and the second argument is `full-range', which will be bound to 1229`list-for-graph'. 1230 1231 The whole expression looks like this: 1232 1233 (mapcar '(lambda (arg) (/ arg 50)) full-range)) 1234 1235 *Note Mapping Functions: (elisp)Mapping Functions, for more about 1236`mapcar'. 1237 1238 Using the `one-fiftieth' function, we can generate a list in which 1239each element is one-fiftieth the size of the corresponding element in 1240`list-for-graph'. 1241 1242 (setq fiftieth-list-for-graph 1243 (one-fiftieth list-for-graph)) 1244 1245 The resulting list looks like this: 1246 1247 (10 20 19 15 11 9 6 5 4 3 3 2 2 1248 1 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 4) 1249 1250This, we are almost ready to print! (We also notice the loss of 1251information: many of the higher ranges are 0, meaning that fewer than 125250 defuns had that many words or symbols--but not necessarily meaning 1253that none had that many words or symbols.) 1254 1255