=============================== CMPU-365, Spring 2013 intro-aclemacs.txt =============================== The following goes into *quite* a bit more detail about EMACS and LISP. ---------------------------------------------------- FIRING UP ACLEMACS and SETTING UP A LISP SESSION ---------------------------------------------------- First, open up a Terminal window. If you haven't already done so, type the following to copy the .emacs file from the cs365 course web page into your own home directory: cp ~hunsberg/cs365/handouts-etc/.emacs ~/ Next, create a working directory and CD into it. From within that directory, issue the following command: aclemacs& A new EMACS window should pop up. ****> The rest of these instructions refer to the EMACS window, not the Terminal window! NOTE: If you want to change the size of the font in the EMACS window, hold down the CTRL key and then click on the EMACS window with the left mouse button. Alternatively, you can select the "Set Font" item under the "Options" menu. When EMACS starts up, it automatically creates several "buffers". They have names such as *GNU Emacs*, *scratch* and *Messages*. A "buffer" is nothing more than a chunk of the computer's memory that is being used to store some information. The main EMACS window is used to display the contents of a single buffer. The highlighted bar at the bottom of the EMACS window shows the name of the buffer that is currently being displayed. Initially, the main EMACS window displays the contents of a buffer called *GNU Emacs*. This buffer contains a welcome message and some information about a few basic commands. You can pretty much ignore it if you want to. Killing a buffer ---------------- Since we don't really need the *GNU Emacs* buffer. We can kill it. To try this, type: C-x k (i.e., hold down the CTRL key, type x, then release the CTRL key, and then type k). Several things will happen in the "command strip" at the bottom of the EMACS window: -- "Kill Buffer" will be displayed, indicating that you are about to use the "Kill Buffer" command. -- A default value (most likely, *GNU Emacs*) will be shown within parentheses. -- The cursor will move to the "command strip". At this point, EMACS is waiting for you to tell it how to proceed. If you want to kill the buffer named within parentheses, then just hit the "Enter" key. That buffer (i.e., that chunk of memory) will be trashed and hence no longer available for display. Thus, EMACS will automatically pick one of the remaining buffers to display in the main window. If you want to kill some other buffer (e.g., the *Messages* buffer), then type *Messages* (in the command strip) and hit the "Enter" key. Since the *Messages* buffer is not currently being displayed, destroying it has no visible effect. However, the *Messages* buffer will no longer be available for viewing; it no longer exists. NOTE: If you repeatedly type C-x k (followed by "Enter" each time), you might think that you will eventually kill all of the buffers, leaving no more buffers available to display. However, EMACS will never let that happen. Instead, it will, when necessary, create a new empty *scratch* buffer. Thus, there will always be at least one buffer available for display. Changing which buffer is shown in the main EMACS window. -------------------------------------------------------- Type: C-x b. You will notice the following in the "command strip": -- "Switch to buffer" will be displayed in the "command strip" along with a default value shown in parentheses, followed by the cursor. Once again, EMACS is waiting for you to tell it how to proceed. If you want to accept the default value, then hit the "Enter" key. The main window will now display the contents of the default buffer. If, instead, you want some other buffer to be displayed, then just type its name in the "command strip" and hit "Enter". (As before, you can use the TAB key if you need to be reminded of the names of the available buffers.) Creating a new buffer to contain the contents of a new file ----------------------------------------------------------- For the sake of discussion, suppose you were in the following directory when you started emacs: ~myname/temp/ Suppose further that this directory does *not* contain any file named myfile.txt. Type: C-x C-f In the "command strip" you will see "Find file" followed by the path ~myname/temp/, and then the cursor. Type myfile.txt so that ~myname/temp/myfile.txt now appears in the command strip. Then hit the "Enter" key. In response, EMACS will create a new buffer (i.e., a new chunk of memory) called myfile.txt. Since this is a new buffer, it is empty. This empty buffer will be displayed in the main window. Since a buffer is a chunk of memory, no file exists yet. (Nonetheless, perhaps over-anticipating, the "command strip" says (New File).) Type some text into this new buffer. Notice that (New File) will disappear from the "command strip". If you kill this buffer, the corresponding memory will be trashed. Since no file has yet been created, you will not see any file named myfile.txt in your directory. To "save" the contents of this buffer to a file (of the same name), type: C-x C-s The "command strip" will report that the contents of the buffer were saved to a file. In particular, it will say something like "Wrote ~myname/temp/myfile.txt". The file now exists in your directory. Thus, if you now use C-x k to kill the buffer, you will still see the file in your directory. Loading a pre-existing file into a new buffer (and displaying it). ----------------------------------------------------------------- Same as above, except now suppose that there is already a file in your directory named somefile.txt. Type C-x C-f The "command strip" will show: Find file: ~myname/temp/, followed by the cursor. Type: somefile.txt so that the "command strip" now reads: Find file: ~myname/temp/somefile.txt Then hit the "Enter" key. Since somefile.txt already exists, EMACS creates a new buffer (i.e., chunk of memory) and loads the contents of the file somefile.txt into that buffer. It also displays that buffer in the main window. Notice that the main window is now labeled "somefile.txt". Now, type some new text into that file (or delete some of the pre-existing text). If you kill the buffer (i.e., destroy the memory) before saving it to the file, then the file will not be changed. However, if you save the buffer (i.e., write the buffer to the file), then the file will be changed. Configuring the Definitions and Interactions windows ---------------------------------------------------- Split the main EMACS window into two sub-windows: C-x 2 Each sub-window can display the contents of a buffer, just like the main window seen previously. Start up a Lisp Interactions Window: M-x fi:common-lisp You will need to hit the "Enter" key a bunch of times (approximately six times) to accept a bunch of default parameter values for the interactions window session. This creates a new buffer, called *common-lisp*. That buffer will be displayed in one of the sub-windows. (If not, you can use C-x b, as described earlier, to ask EMACS to display the *common-lisp* buffer in the current sub-window.) You should see a blinking cursor in one of the EMACS sub-windows. If you click on another one of the sub-windows, the cursor will move into that sub-window. Alternatively, you can type: C-x o (The "o" is for "other", as in, "move into anOTHER sub-window".) ===> Every time you start up aclemacs, you should use the above techniques to ensure that you have the main EMACS window split into two sub-windows. The top sub-window should display the contents of your Lisp program/file; the bottom sub-window should display the *common-lisp* buffer. When you get used to it, this start-up process will become second nature. In brief: cd into your working directory type: aclemacs& in emacs: M-x fi:common-lisp (plus "Enter" a bunch of times) C-x 2 to split main emacs window into 2 sub-windows C-x o to switch to "other" sub-window C-x b to select which "buffer" will appear in current sub-window C-x C-f to open up a Lisp file for your program -------------------------------------------------- Using the Definitions and Interactions Windows -------------------------------------------------- NOTE: From now on, I will be less careful about distinguishing a sub-window from the buffer that is currently being displayed in that sub-window... Type the following expression into the upper sub-window (i.e., the program definitions window): ;; A factorial function (defun facty (n) (if (< n 2) 1 (* n (facty (- n 1))))) Then type: C-x C-s to save the file (or use "File" menu item "Save") Next, assuming that your Lisp file is called myfile.lisp, type the following expression into the Interactions Window (i.e., in the *common-lisp* buffer): (load "myfile.lisp") That will load the expressions from the Definitions window into the Lisp session, just as though you'd typed them directly into the Interactions Window yourself. (This is similar to what happens in DrScheme when you hit the "Run" button.) After loading your definitions, you can then use the functions you defined. For example, consider the following Interactions Window session: CL-USER(5): (load "tmp.lisp") ; Loading /home/hunsberg/courses/cs365/2013-getting-started/tmp.lisp T CL-USER(6): (facty 4) 24 CL-USER(7): (facty 5) 120 NOTE: In the above, "CL-USER(n):" is Lisp's "prompt". The number in parentheses is a line number. NOTE: If you run into problems (e.g., cause an error that generates lots of ugly text), you can "reset" the Lisp session by typing :res as illustrated below: CL-USER(8): (facty "a") Error: `"a"' is not of the expected type `REAL' [condition type: TYPE-ERROR] Restart actions (select using :continue): 0: Return to Top Level (an "abort" restart). 1: Abort entirely from this (lisp) process. [1] CL-USER(9): :res CL-USER(10): NOTE: Typing C-c C-p while the cursor is in the Interactions Window will enable you to "scroll through" the history of previous expressions. That can save a lot of typing. (The "p" in "C-x C-p" stands for "previous".) If you scroll too far, you can use C-c C-n to see the "next" expression. NOTE: If Lisp goes into an endless loop, try hitting C-c repeatedly. NOTE: If you're being prompted by EMACS to do something you don't want to do: use C-g to cancel... If one C-g doesn't work, try hitting it several times. -------------------------------------------------- Evaluating SYMBOLS in Lisp -------------------------------------------------- As mentioned briefly in class, each symbol in Lisp can have TWO distinct values associated with it: an "ordinary value" (e.g., when using the symbol as a variable) and a "function value" (e.g., when using the symbol as the name of a function). The DEFUN special form sets the "function value" for a symbol. For example, the DEFUN special form seen earlier sets the function value for the FACTY symbol. You can use the SYMBOL-FUNCTION function to fetch the function value for some symbol, as illustrated below: CL-USER(19): (symbol-function 'facty) # There is a convenient shorthand syntax for the SYMBOL-FUNCTION function, as illustrated below: CL-USER(20): #'facty # NOTE: Actually, #' is not an abbreviation for SYMBOL-FUNCTION, but when applied to a symbol, it has the same effect. #' can also be applied to lambda expressions. Now, so far, the FACTY symbol does not have its "ordinary value" set. So if you tried to look at its ordinary value (e.g., by typing FACTY in the Interactions Window), you would get an error. You can use the DEFVAR or DEFPARAMETER special forms to set the ordinary value of a symbol, as illustrated below: CL-USER(17): (defvar facty 5) ;; <--- set the "ordinary value" of facty FACTY CL-USER(18): facty ;; <--- fetch ordinary value 5 Notice that evaluating FACTY in the Interactions Window automatically fetches the "ordinary value", not the "function value". However, when a symbol appears as the first element in a list, then it will automatically evaluate to its "function value", as illustrated below: CL-USER(30): (facty facty) 120 The first occurrence of FACTY in the above list evaluates to its "function value", which is the factorial function defined earlier. However, the second occurrence of FACTY is NOT in function position; thus, it evaluates to its "ordinary value", which is currently 5. Thus, the factorial function gets applied to the value 5, generating an output of 120. ----------------------------- BOOLEANS: T and NIL ----------------------------- In Lisp, the symbol T evaluates to itself and is used for Boolean True. In Lisp, the smybol NIL evaluates to itself and is used for Boolean False. However, Lisp allows any kind of expression to be used where Boolean values are expected. In particular, anything other than NIL "counts as" Boolean True. NOTE: Lisp is NOT case sensitive. By default, the output in the Interactions Window is upper-case. CL-USER(24): T T <--- T evaluates to itself CL-USER(25): t T <--- t and T are the same CL-USER(26): nil NIL <--- nil (or NIL) evalutes to itself CL-USER(27): NIL NIL CL-USER(28): () <--- the empty list is also NIL!! NIL CL-USER(29): (if t 3 4) <--- T stands for Boolean True 3 CL-USER(30): (if NIL 3 4) <--- NIL stands for Boolean False 4 CL-USER(31): (if 5 3 4) <--- But 5 also "counts as" Boolean True 3 -------------------------------- MAPCAR (like Scheme's MAP) -------------------------------- NOTE: There are several variants of the MAPCAR function. They are described in Graham's book. MAPCAR is the most common. CL-USER(34): (mapcar (lambda (x) (* x x)) <--- don't need #' here '(1 2 3)) (1 4 9) CL-USER(35): (mapcar #'(lambda (x) (* x x)) <--- but it doesn't hurt '(1 2 3)) (1 4 9) CL-USER(36): (mapcar #'facty <---- MUST use #' here!!! '(1 2 3 4 5)) (1 2 6 24 120) Why? Since FACTY is not in function position (i.e., it is not the FIRST element of the list) it would ordinarily evaluate to its "ordinary value", which is NOT what we want. Typically, when passing a function, FUNKY, as an INPUT to some other function, it happens that FUNKY is not in function position. Thus, to fetch its function value, you need to use #'FUNKY. ---------------------------------------------------- Lots of built-in functions to take advantage of ---------------------------------------------------- HASH TABLES CL-USER(37): (setf h (make-hash-table)) # CL-USER(38): (gethash 3 h) NIL NIL CL-USER(39): (setf (gethash 3 h) 45) 45 CL-USER(40): (gethash 3 h) 45 T CL-USER(41): (setf h-equal (make-hash-table :test #'equal)) # CL-USER(43): (setf (gethash '(1 2) h) "hi there") "hi there" CL-USER(44): (gethash '(1 2) h) NIL NIL CL-USER(45): (setf (gethash '(1 2) h-equal) "hi there") "hi there" CL-USER(46): (gethash '(1 2) h-equal) "hi there" T To terminate the Lisp session, type: (exit) in the interactions window. THEN use C-x k to kill the buffer. THEN use C-x C-c to terminate the EMACS program.