;;; Elisp Exercises ;; The following are jfulton's answers to the exercises in ;; "Programming in Emacs Lisp" by Robert Chassell. I post ;; them here for my own reference. If you want to look at ;; them go ahead. If I made any mistakes that mislead you ;; I apologize. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Ch 3 ;; Write a non-interactive function that doubles the value of its ;; argument, a number. Make that function interactive. (defun my-double (num) "Doubles the value of NUM" (interactive "P") (message "%d" (* 2 num))) ;; Write a function that tests whether the current value of ;; fill-column is greater than the argument passed to the ;; function, and if so, prints an appropriate message. (defun fill-bigger (num) "prints whether NUM is greater than fill-colm" (interactive "P") (if (< fill-column num) (message "Argument bigger than fill-column's (%d < %d)" fill-column num) (message "Argument smaller than fill-column's (%d > %d)" fill-column num))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Ch 4 ;; Write your own simplified-end-of-buffer function defintion (defun simplified-end-of-buffer () "My own end of buffer for fun" (interactive) (push-mark) (goto-char (point-max))) ;; Use if and get-buffer to write a function that prints a message ;; telling you whether a buffer exists (defun my-buffer-exists (buffer) "Homework" (interactive "Bbuffer name: ") (if (get-buffer buffer) (message "The buffer \"%s\" does exist" buffer) (message "The buffer \"%s\" does not exist" buffer))) ;; Using find-tag, find the source for the copy-to-buffer function ; no code to write here ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Ch 5 ;; Write an interactive function with an optional argument that ;; tests whether its argument, a number, is greater or less than ;; the value of fill-column, and tells you which, in a message. ;; However, if you do not pass an argument to the function, use ;; 56 as the default (defun hw (&optional pre-arg) "See above" (interactive "P") (if pre-arg (setq arg (prefix-numeric-value pre-arg)) (setq arg 56)) (if (< fill-column arg) (message "Argument bigger than fill-column's (%d < %d)" fill-column arg) (message "Argument smaller than fill-column's (%d > %d)" fill-column arg))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Ch 6 ;; Write a function that will display the first 60 characters of the ;; current buffer, even if you have narrowed the buffer to its latter ;; half so that the fist line is inaccessible. Restore point, mark ;; and narrowing. For this exercise, you need to use save-restriction, ;; widen, goto-char, point-min, buffer-substring, message, and other ;; functions, a whole potpourri. (defun first-n-chars (num) "see above" (interactive "P") (save-restriction (widen) (save-excursion (goto-char (point-min)) (message "%s" (buffer-substring-no-properties 1 (1+ num)))))) ; (first-n-chars 60) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Ch 7 ;; Construct a list of four metal bands by evaluating several expressions ;; with cons. Find out what happens when you cons a list onto itself. ;; Replace the first element of the list of four metal bands with a hacker. ;; Replace the rest of that list with a list of other hackers. (setq metal-bands (cons 'slayer (cons 'emperor (cons 'dimmu-borgir (cons 'cradle-of-filth ()))))) (setq onto-itself (cons metal-bands metal-bands)) (setcar metal-bands 'RMS) (setcdr metal-bands '(Linus Roland-McGrath Robert-Chassell)) ; metal-bands ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Ch 8 ;; Write an interactive function that searches for a string. If the search ;; finds the string, leave point after it and display a message that says ;; "Found!". (Do not use search-forward for the name of this function; if ;; you do, you will overwrite the existing version of search-forward that ;; comes with Emacs. Use a name such as test-search instead.) (defun test-search (str) "Searches for STR, if found writes Found! to mini-buffer" (interactive "sSearch for: ") (if (search-forward str) (message "Found!"))) ;; Write a function that prints the third element of the kill ring in the ;; echo area, if any; if the kill ring does not contain a third element, ;; print an appropriate message. (defun third-kill () "Prints the third element of the kill ring or message if no third element" (interactive) (setq third (nth 2 kill-ring)) ; kill ring is zero indexed circular stack (if third (message "Third is:%s" third) (message "There is no third"))) ; (setcdr kill-ring nil) ;; used to errase the kill-ring ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Ch 9 ;; Set metal-bands to dimmu-borgir and opeth. Cons two more metal-bands on ;; to this list and set this new list to more-metal-bands. Set the CAR of ;; metal-bands to a fish. What does the more-metal-bands list now contain? (setq metal-bands '(dimmu-borgir opeth)) (setq more-metal-bands (cons 'marduk (cons 'emperor metal-bands))) metal-bands ;--> (dimmu-borgir opeth) more-metal-bands ;--> (marduk emperor dimmu-borgir opeth) (setcar metal-bands 'salmon) metal-bands ;--> (salmon opeth) more-metal-bands ;--> (marduk emperor salmon opeth) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Ch 10 ;; Using C-h v (describe-variable), look at the value of your kill ;; ring. Add several items to your kill ring; look at its value again. ;; Using M-y (yank-pop), move all the way around the kill ring. How ;; many items were in your kill ring? Find the value of kill-ring-max. ;; Was your kill ring full, or could you have kept more blocks of text ;; within it? ;;;;;; ;; I rebound 'C-h' to be my backspace (an old habbit) so I had to ;; type 'F1' (on a powerbook 'fn F1'). ;; ;; 'M-y' indicated that there were about 20 items in my kill ring. ;; Evaluating the following indicated that kill-ring max is 60 ;; ;; kill-ring-max ;; ;; So my kill ring could have held more items. ;; ;; I reset my kill ring with the following ;; ;; (setcdr kill-ring nil) ;; ;; I then cut and yanked two things. M-y cycled through both, i.e. ;; it inserted both cut items into the buffer, and typing it again ;; insert over the previous insertion. ;;;;;; ;; Using nthcdr and car, construct a series of expressions to return the ;; first, second, third, and fourth elements of a list. ; make list (setq bands '(marduk emperor dimmu-borgir opeth)) ;; first (car (nthcdr 0 bands)) ;; second (car (nthcdr 1 bands)) ;; third (car (nthcdr 2 bands)) ;; fourth (car (nthcdr 3 bands)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Ch 11 ;; Write a function similar to triangle in which each row has a ;; value which is the square of the row number. Use a while loop. ;; As a review lets look at the while-loop version triangle: (defun triangle (number-of-rows) "Add up the number of pebbles in a triangle. The first row has one pebble, the second row two pebbles, the third row three pebbles, and so on. The argument is NUMBER-OF-ROWS." (let ((total 0) (row-number 1)) (while (<= row-number number-of-rows) (setq total (+ total row-number)) (setq row-number (1+ row-number))) total)) ;; Here is the answer (defun sum-of-squares-while (n) "returns sub of squares up to N" (let ((ret 0) ; return value (i 1)) ; index (while (<= i n) (setq ret (+ (* i i) ret)) (setq i (+ i 1))) ret)) ;; Write a function similar to triangle that multiplies ;; instead of adds the values. (defun factorial-while (n) "takes the product from 1 to N" (let ((ret 1) ; return value (i 1)) ; index (while (<= i n) (setq ret (* i ret)) (setq i (+ i 1))) ret)) ;; Rewrite these two functions recursively. (defun sum-of-squares-recur-if (n) "returns sub of squares up to N" (if (= n 0) n (+ (* n n) (sum-of-squares-recur-if (- n 1))))) (defun factorial-recur-if (n) "returns factorial of N" (if (= n 1) n (* n (factorial-recur-if (- n 1))))) ;; As a review lets look at the recursive version triangle: (defun triangle-recursively (number) "Return the sum of the numbers 1 through NUMBER inclusive. Uses recursion." (if (= number 1) ; do-again-test 1 ; then-part (+ number ; else-part (triangle-recursively ; recursive call (1- number))))) ; next-step-expression ;; Rewrite these functions using cond. (defun sum-of-squares-recur-cond (n) "returns sub of squares up to N" (cond ((= n 0) n) (t (+ (* n n) (sum-of-squares-recur-if (- n 1)))))) (defun factorial-recur-cond (n) "returns factorial of N" (cond ((= n 1) n) (t (* n (factorial-recur-if (- n 1)))))) ;; Write a function for Texinfo mode that creates an index ;; entry at the beginning of a paragraph for every @dfn within ;; the paragraph. (In a Texinfo file, @dfn marks a definition. ;; For more information, see Indicating.) ;; I.e. write a function that creates some syntax at the ;; beginning of a paragraph for every @dfn within the ;; paragraph. ;; (Skipping for now) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Ch. 12 ;; Write a function to search for a regular expression that matches ;; two or more blank lines in sequence. (defun two-or-more-blanks () "Searches for two or more blank lines, leaves point after last character in the target" (interactive) (re-search-forward " +")) ;; match a literal two-spaces at least once ;; The above was tested on the following: ;; no no yes yes yes yes yes yes ;;; ;; Write a function to search for duplicated words, such as `the the'. ;; See section `Syntax of Regular Expressions' in The GNU Emacs Manual, ;; for information on how to write a regexp (a regular expression) to ;; match a string that is composed of two identical halves. You can devise ;; several regexps; some are better than others. The function I use is ;; described in an appendix, along with several regexps. See section ;; the-the Duplicated Words Function. ;; I'll share the steps I took to work on this problem... (defun my-find-word () "Searches for any word, leaves point at match" (interactive) (re-search-forward "\\(\\w+\\)")) (defun two-foo () "Searches for \"foo foo\", leaves point at match" (interactive) (re-search-forward "\\(foo[ ]*\\)\\{2\\}")) ;; Testing: ;; foofoo ;; foo foo ;; foo foo (defun two-words () "Searches for any two words, leaves point at match" (interactive) (re-search-forward "\\(\\(\\w+\\)[ ]*\\)\\{2\\}")) (defun two-words-space () "Searches for any two words, leaves point at match (better spacing)" (interactive) (re-search-forward "\\(\\(\\w+\\)[ \t\n]*\\)\\{2\\}")) ;; not sure... going to look at the-the (defun the-the () "Search forward for for a duplicated word." (interactive) (message "Searching for for duplicated words ...") (push-mark) ;; This regexp is not perfect ;; but is fairly good over all: (if (re-search-forward "\\b\\([^@ \n\t]+\\)[ \n\t]+\\1\\b" nil 'move) (message "Found duplicated word.") (message "End of buffer"))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Ch. 13 ;; Using a while loop, write a function to count the number of ;; punctuation marks in a region -- period, comma, semicolon, colon, ;; exclamation mark, and question mark. (defun count-punctuation-region (beginning end) "Print number of punctuation marks in the region." (interactive "r") (message "Counting punctuation in region ... ") (save-excursion ;; setup conditions (let ((count 0)) (goto-char beginning) (while (and (< (point) end) ;; do it (re-search-forward "[\\.\\,\\;\\:\\!\\?]" end t)) (setq count (1+ count))) (message "The region has %d mark(s) of punctuation." count)))) ;; start test region ;; . : ? ! , ... ;; end test region ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Do the same using recursion. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; I'll use the other count-words-region as an example (defun recursive-count-punctuation (region-end) "Number of punctuation between point and REGION-END." (if (and (< (point) region-end) (re-search-forward "[\\.\\,\\;\\:\\!\\?]" end t)) (1+ (recursive-count-punctuation region-end)) 0)) (defun count-punctuation-region (beginning end) "Print number of punctuation marks in the region." (interactive "r") (message "Counting punctuation marks in region ... ") (save-excursion (goto-char beginning) (let ((count (recursive-count-punctuation end))) (cond ((zerop count) (message "The region does NOT have any punctuation marks.")) ((= 1 count) (message "The region has 1 punctuation mark.")) (t (message "The region has %d punctuation marks." count)))))) ;; start test region ;; . : ? ! , ... ;; End test region ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; I thought of an application of this. I thought I saw a better ;; version of this function in emacs' SGML mode. (defun balance-tag (beginning end) "Searches backwards for HTML, determines tag, places closing tag at point" (interactive "r") (message "Looking for tag to balance") (save-excursion (search-backward "<") (forward-char) (set-mark (point)) (search-forward ">") (backward-char) (copy-region-as-kill (point) (mark))) (insert-string "")) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;