esoe
2 years ago
3 changed files with 3336 additions and 0 deletions
@ -0,0 +1,733 @@ |
|||||||
|
*vlime-tutor.txt* A tutorial for Vlime *vlime-tutor* |
||||||
|
* [soure of text](https://github.com/vlime/vlime/blob/master/vim/doc/vlime-tutor.txt) |
||||||
|
|
||||||
|
====================================================================== |
||||||
|
CONTENTS *vlime-tutor-contents* |
||||||
|
|
||||||
|
1. Introduction .............................. |vlime-tutor-intro| |
||||||
|
2. Starting the Server ................ |vlime-tutor-start-server| |
||||||
|
3. Connecting .............................. |vlime-tutor-connect| |
||||||
|
4. The REPL ................................... |vlime-tutor-repl| |
||||||
|
5. Coding ..................................... |vlime-tutor-code| |
||||||
|
6. Compiling ............................... |vlime-tutor-compile| |
||||||
|
7. Debugging ................................. |vlime-tutor-debug| |
||||||
|
8. The inspector ......................... |vlime-tutor-inspector| |
||||||
|
9. Conclusion ........................... |vlime-tutor-conclusion| |
||||||
|
|
||||||
|
====================================================================== |
||||||
|
1. Introduction *vlime-tutor-intro* |
||||||
|
|
||||||
|
This is a guided tour to show the major features of Vlime. For a |
||||||
|
complete list of the features and key mappings etc., see |vlime.txt|. |
||||||
|
|
||||||
|
This tutorial was written on a Linux machine, so there may be some |
||||||
|
Linux-specific commands. Vlime also works fine on Windows, and maybe |
||||||
|
MacOS, but these systems will not be covered here. |
||||||
|
|
||||||
|
Before we get started, please make sure your system has all these |
||||||
|
prerequisites: |
||||||
|
|
||||||
|
* A decent Common Lisp implementation. SBCL is strongly |
||||||
|
recommended, but other implementations that support the debugger |
||||||
|
may also be fine. See |vlime-intro| for a list of supported |
||||||
|
implementations. |
||||||
|
|
||||||
|
* The Paredit plugin for Vim (https://github.com/kovisoft/paredit). |
||||||
|
|
||||||
|
* Vlime being properly installed. See README.md in the Vlime |
||||||
|
source repo for installation instructions. |
||||||
|
|
||||||
|
We will be building a Fibonacci sequence generator in the rest of this |
||||||
|
tutorial. Fascinating, right? |
||||||
|
|
||||||
|
====================================================================== |
||||||
|
2. Starting the Server *vlime-tutor-start-server* |
||||||
|
|
||||||
|
Vlime works in a client-server fashion. The server is written in |
||||||
|
Common Lisp. You can let Vlime start the server for you (see |
||||||
|
|vlime-start-up|), but let's do it manually for the first time. To |
||||||
|
start the server on the local machine: |
||||||
|
|
||||||
|
sbcl --load <vlime repo>/lisp/start-vlime.lisp |
||||||
|
|
||||||
|
To run the server on a remote machine, see |vlime-remote-server|. |
||||||
|
|
||||||
|
The server is fully functional when we see something like this in the |
||||||
|
console: |
||||||
|
|
||||||
|
<INFO> [10:08:07] vlime-usocket - Server created: .... |
||||||
|
|
||||||
|
It will be listening on port 7002 by default. |
||||||
|
|
||||||
|
====================================================================== |
||||||
|
3. Connecting *vlime-tutor-connect* |
||||||
|
|
||||||
|
Now we can create a new *.lisp file in Vim: |
||||||
|
|
||||||
|
:tabedit ad-hoc.lisp |
||||||
|
|
||||||
|
Then type "\cc" (without the quote marks) in normal mode to create a |
||||||
|
connection. The backslash "\" is the default mapping for |
||||||
|
|<LocalLeader>|. See |vlime-mappings|. |
||||||
|
|
||||||
|
Vlime will show a message in Vim if everything went well: |
||||||
|
|
||||||
|
Vlime Connection 1 established. |
||||||
|
|
||||||
|
There can be multiple Vlime connections in a single Vim process. You |
||||||
|
can use "\cc" to make more connections to the same server (though that |
||||||
|
usually doesn't make any sense), or call vlime#plugin#ConnectREPL() in |
||||||
|
Vim to connect to another server. For example: |
||||||
|
|
||||||
|
:call vlime#plugin#ConnectREPL("127.0.0.1", 9999) |
||||||
|
|
||||||
|
This will try to make a connection to 127.0.0.1:9999. |
||||||
|
|
||||||
|
Now, just for demostrating, let's type "\cc" a few more times, to |
||||||
|
create more connections. Then type "\cs" (without the quote marks) in |
||||||
|
the ad-hoc.lisp buffer to see the connection list. A list with three |
||||||
|
connections should look like this: |
||||||
|
|
||||||
|
Which connection to use? |
||||||
|
1. Vlime Connection 1 (127.0.0.1:7002) |
||||||
|
2. Vlime Connection 2 (127.0.0.1:7002) |
||||||
|
3. Vlime Connection 3 (127.0.0.1:7002) |
||||||
|
Type number and <Enter> or click with mouse (empty cancels): |
||||||
|
|
||||||
|
From there you can also choose a connection for the current buffer to |
||||||
|
use by typing it's ID and then <Enter>. See |
||||||
|
|vlime-current-connection|. |
||||||
|
|
||||||
|
When you're done with a connection, type "\cd" (without the quote |
||||||
|
marks) in a *.lisp buffer to close it. The connection list may pop up |
||||||
|
if Vlime was not sure which connection to close. |
||||||
|
|
||||||
|
You may try out the "\cc", "\cs", and "\cd" commands now, until you |
||||||
|
feel comfortable to move on. Please leave at least one connection open |
||||||
|
before proceeding to the next section. |
||||||
|
|
||||||
|
====================================================================== |
||||||
|
4. The REPL *vlime-tutor-repl* |
||||||
|
|
||||||
|
Now that we have an active connection, let's try something simple. |
||||||
|
|
||||||
|
Vlime has good REPL integration, but it's different from other Lisp |
||||||
|
environments, in that Vlime's REPL buffer is dedicated for output, |
||||||
|
i.e. you can not type and evaluate an expression directly in the REPL |
||||||
|
buffer. To evaluate something in the REPL, you send it using key |
||||||
|
mappings listed in |vlime-mappings-send|. |
||||||
|
|
||||||
|
Let's go to ad-hoc.lisp, the file we created in |vlime-tutor-connect|, |
||||||
|
and write a simple expression in it: |
||||||
|
|
||||||
|
(cons 1 2) |
||||||
|
|
||||||
|
Note: You may notice some "strange" behaviors when typing the |
||||||
|
expression, such as parentheses being paired up automatically. |
||||||
|
They are in fact features to aid you in code editing. We will |
||||||
|
get to them in |vlime-tutor-code|. |
||||||
|
|
||||||
|
Make sure the cursor is on or inside the parentheses, then type "\ss" |
||||||
|
(without the quote marks) in normal mode. A new buffer will pop up at |
||||||
|
the bottom, showing some info about the server and the evaluation |
||||||
|
result: |
||||||
|
|
||||||
|
SWANK version 2016-04-19, pid 14403 |
||||||
|
=================================== |
||||||
|
-- |
||||||
|
(1 . 2) |
||||||
|
|
||||||
|
This is the REPL buffer. |
||||||
|
|
||||||
|
Try typing "i" (without the quote marks) in the REPL buffer. It will |
||||||
|
result in an error message: |
||||||
|
|
||||||
|
E21: Cannot make changes, 'modifiable' is off |
||||||
|
|
||||||
|
Indeed, it's read-only. |
||||||
|
|
||||||
|
To make sending things to the REPL easier, Vlime has an interaction |
||||||
|
mode for *.lisp buffers. Go back to the ad-hoc.lisp buffer at the top, |
||||||
|
then type "\i" (without the quote marks) to activate interaction mode. |
||||||
|
|
||||||
|
To send something to the REPL in interaction mode, simply press <CR>. |
||||||
|
Consider the expression we just wrote: |
||||||
|
|
||||||
|
(cons 1 2) |
||||||
|
|
||||||
|
Again, make sure the cursor is on or inside the parentheses, then |
||||||
|
press <CR>, the new result will also come out in the REPL buffer: |
||||||
|
|
||||||
|
SWANK version 2016-04-19, pid 14403 |
||||||
|
=================================== |
||||||
|
-- |
||||||
|
(1 . 2) |
||||||
|
-- |
||||||
|
(1 . 2) |
||||||
|
|
||||||
|
Be careful with nested expressions though. Vlime will only match the |
||||||
|
nearest parentheses that enclose the cursor. Let's add one more line |
||||||
|
to ad-hoc.lisp: |
||||||
|
|
||||||
|
(cons 1 2) |
||||||
|
(+ 1 (- 10 2)) ; the new line |
||||||
|
|
||||||
|
When <CR> is pressed, if the cursor was on the "+" operator, the whole |
||||||
|
expression "(+ 1 (- 10 2))" will be sent, and the result will be 9. If |
||||||
|
the cursor was on the "-" operator, only the nested expression |
||||||
|
"(- 10 2)" will be sent, and the result will be 8. |
||||||
|
|
||||||
|
To disable interaction mode, type "\i" (without the quote marks) again |
||||||
|
in the ad-hoc.lisp buffer. |
||||||
|
|
||||||
|
But in what package were those expressions evaluated? Let's have a |
||||||
|
look. Add one more line to ad-hoc.lisp and evaluate it by typing "\ss" |
||||||
|
(without the quote marks) in normal mode or <CR> in interaction mode: |
||||||
|
|
||||||
|
(cons 1 2) |
||||||
|
(+ 1 (- 10 2)) |
||||||
|
(symbol-value (find-symbol "*PACKAGE*" "COMMON-LISP")) ; the new line |
||||||
|
|
||||||
|
The result should look like |
||||||
|
|
||||||
|
-- |
||||||
|
#<PACKAGE "COMMON-LISP-USER"> |
||||||
|
|
||||||
|
When evaluating an expression, Vlime would use the package associated |
||||||
|
with the buffer containing that expression (see |
||||||
|
|vlime-current-package|). And the default package is COMMON-LISP-USER. |
||||||
|
|
||||||
|
In the examples above, we never explicitly set the package for |
||||||
|
ad-hoc.lisp, so it used the default package, and all the expressions |
||||||
|
are evaluated in the default package too. |
||||||
|
|
||||||
|
There are two methods to set the package for a buffer: |
||||||
|
|
||||||
|
1. Write an "in-package" expression in the buffer. Change the |
||||||
|
content of ad-hoc.list to |
||||||
|
|
||||||
|
(cons 1 2) |
||||||
|
(+ 1 (- 10 2)) |
||||||
|
(in-package :vlime) ; the new line |
||||||
|
(symbol-value (find-symbol "*PACKAGE*" "COMMON-LISP")) |
||||||
|
|
||||||
|
And then evaluate the last line again, the result would be |
||||||
|
|
||||||
|
-- |
||||||
|
#<PACKAGE "VLIME"> |
||||||
|
|
||||||
|
2. Type "\p" (without the quote marks) in a buffer in normal mode. |
||||||
|
Vim will then prompt for the new package name, with the current |
||||||
|
package displayed as the default. You can edit the package name |
||||||
|
like you edit any other text in a normal buffer, and then press |
||||||
|
<CR> in normal mode to submit (see |vlime-input-buffer|). The |
||||||
|
name to enter can be a nickname, and is case-insensitive. This |
||||||
|
method will take precedence over method 1. |
||||||
|
|
||||||
|
Now type "\p" in ad-hoc.lisp, and set the package to "cl-user" |
||||||
|
(without the quote marks). Evaluate the last line again, the |
||||||
|
result would be |
||||||
|
|
||||||
|
-- |
||||||
|
#<PACKAGE "COMMON-LISP-USER"> |
||||||
|
|
||||||
|
You may try out the REPL and the interaction mode now, until you feel |
||||||
|
comfortable to move on. Try writing some complex expressions in |
||||||
|
ad-hoc.lisp, such as multi-line DEFUNs, and evaluate them using |
||||||
|
interaction mode. |
||||||
|
|
||||||
|
====================================================================== |
||||||
|
5. Coding *vlime-tutor-code* |
||||||
|
|
||||||
|
Finally, we are going to write some real code. Our goal is to build a |
||||||
|
Fibonacci sequence generator. Let's create a new file in Vim: |
||||||
|
|
||||||
|
:tabedit fibonacci.lisp |
||||||
|
|
||||||
|
And then type the content in it: |
||||||
|
|
||||||
|
(in-package #:cl-user) |
||||||
|
|
||||||
|
(defpackage #:fibonacci |
||||||
|
(:use #:cl) |
||||||
|
(:export #:generate)) |
||||||
|
|
||||||
|
(in-package #:fibonacci) |
||||||
|
|
||||||
|
(defun generate (n &optional (a 0) (b 1) (acc (list))) |
||||||
|
(if (<= n 0) |
||||||
|
(reverse acc) |
||||||
|
(generate (1- n) b (+ a b) (push acc a)))) |
||||||
|
|
||||||
|
The GENERATE function should build a list of N Fibonacci numbers. If |
||||||
|
you typed the code manually instead of copy-and-paste, You may have |
||||||
|
noticed a few things when typing: |
||||||
|
|
||||||
|
1. The parentheses are automatically paired. This is done by the |
||||||
|
Paredit plugin. It's an invaluable tool for editing Lisp code. |
||||||
|
See |paredit.txt| for details and more advanced usage. |
||||||
|
|
||||||
|
2. When pressing <Space> or <CR> inside a pair of parentheses in |
||||||
|
insert mode, a small preview window may pop up at the top, |
||||||
|
showing the argument list for the current expression. |
||||||
|
|
||||||
|
3. The code is automatically indented. See |vlime-auto-indent|. |
||||||
|
|
||||||
|
4. You can press <Tab> or the old-school CTRL-x CTRL-o keys in |
||||||
|
insert mode to activate the omni-completion menu, and then |
||||||
|
use CTRL-n or CTRL-p to select a candidate. See |
||||||
|
|vlime-completions|. |
||||||
|
|
||||||
|
Note: If you have other completion plugins installed, the <Tab> key |
||||||
|
may be mapped to the other plugins, and may not work as |
||||||
|
expected. You can fall back to CTRL-x CTRL-o, or remap the |
||||||
|
<Tab> key as described in |vlime-mappings-remap|. |
||||||
|
|
||||||
|
These are the most frequently used features when editing the source |
||||||
|
code. |
||||||
|
|
||||||
|
Vlime also has support for showing document strings and cross |
||||||
|
references. Move the cursor inside the word "reverse", and type "\dda" |
||||||
|
(without the quote marks), a preview buffer will then show a brief |
||||||
|
description of the REVERSE function: |
||||||
|
|
||||||
|
Documentation for the symbol REVERSE: |
||||||
|
|
||||||
|
Function: |
||||||
|
Arglist: (SEQUENCE) |
||||||
|
|
||||||
|
Return a new sequence containing the same elements but in reverse order. |
||||||
|
|
||||||
|
This information varies between Common Lisp implementations, but is |
||||||
|
generally helpful. See |vlime-mappings-describe| for more operations |
||||||
|
on describing symbols. |
||||||
|
|
||||||
|
Now keep the cursor inside the word "reverse", and type "\xc" (without |
||||||
|
the quote marks). The cross reference (a.k.a. xref) buffer will pop up |
||||||
|
with some content like this: |
||||||
|
|
||||||
|
SB-WALKER::LET*-BINDINGS |
||||||
|
SB-SYS:CLOSE-SHARED-OBJECTS |
||||||
|
SB-PCL::MAKE-PRELIMINARY-LAYOUT |
||||||
|
SB-PCL::STD-COMPUTE-SLOTS |
||||||
|
SB-PCL::STANDARD-COMPUTE-EFFECTIVE-METHOD |
||||||
|
SB-PCL::COMPUTE-STD-CPL-PHASE-3 |
||||||
|
SB-PCL::%UPDATE-LISP-CLASS-LAYOUT |
||||||
|
.... |
||||||
|
|
||||||
|
Note: Depending on how your Common Lisp implementation was built, you |
||||||
|
may get the "No xref found" error message instead. Don't worry. |
||||||
|
You can try other xrefs listed in |vlime-mappings-invoke-xref|, |
||||||
|
or skip this operation and proceed to the next section. |
||||||
|
|
||||||
|
These are the locations where the REVERSE function is called. Press |
||||||
|
<CR> on one of them and Vlime will take you directly to the referenced |
||||||
|
location, provided the source code is readable. Use |CTRL-O| to go |
||||||
|
back to previous cursor locations. There are other kinds of cross |
||||||
|
references, see |vlime-mappings-invoke-xref|. |
||||||
|
|
||||||
|
Before proceeding to the next section, you can close all Vlime windows |
||||||
|
in the current tab page by typing "\wA" (without the quote marks) in |
||||||
|
fibonacci.lisp in normal mode. See |vlime-mappings-close-window| for |
||||||
|
all window-manipulating commands. |
||||||
|
|
||||||
|
====================================================================== |
||||||
|
6. Compiling *vlime-tutor-compile* |
||||||
|
|
||||||
|
Make sure you have saved fibonacci.lisp, and then type "\of" (without |
||||||
|
the quote marks) in it's buffer in normal mode. Go back to the REPL |
||||||
|
buffer, you'll see the compilation result: |
||||||
|
|
||||||
|
-- |
||||||
|
; compiling file "/home/user/fibonacci.lisp" (written 28 FEB 2017 11:16:53 AM): |
||||||
|
|
||||||
|
; /home/user/fibonacci.fasl written |
||||||
|
; compilation finished in 0:00:00.093 |
||||||
|
|
||||||
|
Note: If you have closed the window containing the REPL buffer, it |
||||||
|
should now pop up again. |
||||||
|
|
||||||
|
This result means our code compiled cleanly, with no errors or |
||||||
|
warnings etc. To generate a warning message, let's change the |
||||||
|
expression calling the GENERATE function from |
||||||
|
|
||||||
|
(generate (1- n) b (+ a b) (push acc a)) |
||||||
|
|
||||||
|
To |
||||||
|
|
||||||
|
(some-other-func (1- n) b (+ a b) (push acc a)) |
||||||
|
|
||||||
|
SOME-OTHER-FUNC is not defined, of course. Save the file, and Compile |
||||||
|
it again by typing "\of" (without the quote marks). A buffer will pop |
||||||
|
up with a warning message: |
||||||
|
|
||||||
|
STYLE-WARNING: undefined function: SOME-OTHER-FUNC |
||||||
|
|
||||||
|
Pressing <CR> on the message will take you to the exact location where |
||||||
|
the undefined function is called. |
||||||
|
|
||||||
|
Let's change the function call back to GENERATE, then save and compile |
||||||
|
fibonacci.lisp again. The compiler messages (a.k.a. notes) buffer |
||||||
|
should say: |
||||||
|
|
||||||
|
No message from the compiler. |
||||||
|
|
||||||
|
We will see whether the GENERATE function can work as expected in |
||||||
|
|vlime-tutor-debug|. See |vlime-mappings-compile| for other compiling |
||||||
|
operations. |
||||||
|
|
||||||
|
You can close the notes window before proceeding to the next section. |
||||||
|
|
||||||
|
====================================================================== |
||||||
|
7. Debugging *vlime-tutor-debug* |
||||||
|
|
||||||
|
The first step of debugging in Common Lisp is usually trying out the |
||||||
|
new function in the REPL. We'll do the same here. |
||||||
|
|
||||||
|
Go back to ad-hoc.lisp, write an expression to call |
||||||
|
FIBONACCI:GENERATE: |
||||||
|
|
||||||
|
(fibonacci:generate 10) |
||||||
|
|
||||||
|
And send it to the REPL, via either "\ss" or interaction mode. |
||||||
|
|
||||||
|
A new buffer came out instead of the evaluation result. The buffer |
||||||
|
content looks quite scary: |
||||||
|
|
||||||
|
Thread: 1; Level: 1 |
||||||
|
|
||||||
|
The value |
||||||
|
34 |
||||||
|
is not of type |
||||||
|
LIST |
||||||
|
[Condition of type TYPE-ERROR] |
||||||
|
|
||||||
|
Restarts: |
||||||
|
0. RETRY - Retry SLIME REPL evaluation request. |
||||||
|
1. *ABORT - Return to SLIME's top level. |
||||||
|
2. ABORT - abort thread (#<THREAD "repl-thread" RUNNING ... |
||||||
|
|
||||||
|
Frames: |
||||||
|
0. (SB-IMPL::LIST-REVERSE 34) |
||||||
|
1. (SB-INT:SIMPLE-EVAL-IN-LEXENV (FIBONACCI:GENERATE 10) ... |
||||||
|
2. (EVAL (FIBONACCI:GENERATE 10)) |
||||||
|
3. (SWANK::EVAL-REGION "(fibonacci:generate 10)") |
||||||
|
4. ((LAMBDA NIL :IN SWANK-REPL::REPL-EVAL)) |
||||||
|
5. (SWANK-REPL::TRACK-PACKAGE #<CLOSURE (LAMBDA NIL :IN ... |
||||||
|
6. (SWANK::CALL-WITH-RETRY-RESTART "Retry SLIME REPL ... |
||||||
|
7. (SWANK::CALL-WITH-BUFFER-SYNTAX NIL #<CLOSURE (LAMBDA ... |
||||||
|
8. (SWANK-REPL::REPL-EVAL "(fibonacci:generate 10)") |
||||||
|
9. (SB-INT:SIMPLE-EVAL-IN-LEXENV (SWANK-REPL:LISTENER-EVAL ... |
||||||
|
.... |
||||||
|
|
||||||
|
This is the debugger. It pops up whenever an unhandled condition |
||||||
|
occurs. The content can be divided into four parts, separated by an |
||||||
|
empty line: |
||||||
|
|
||||||
|
* The thread and netsted level this condition occured in. |
||||||
|
* The description of this condition. |
||||||
|
* Some available restarts. |
||||||
|
* The stack frames. |
||||||
|
|
||||||
|
Our GENERATE function is recursive, but we see no GENERATE function |
||||||
|
call in the stack frames. That's because the compiler did it's job and |
||||||
|
optimized our code a bit, so the debugger has fewer info available. |
||||||
|
|
||||||
|
We can't determine what went wrong yet, so let's abort the evaluation |
||||||
|
for now. Select the restart labeled "ABORT - Return to SLIME's top |
||||||
|
level" by moving the cursor to that line and press <CR>. The debugger |
||||||
|
will then disappear, leaving a printed condition in the REPL buffer to |
||||||
|
indicate the evaluation was aborted: |
||||||
|
|
||||||
|
-- |
||||||
|
#<TYPE-ERROR expected-type: "LIST"datum: 34> |
||||||
|
|
||||||
|
There's an option in Vlime to tell the compiler our preference. Let's |
||||||
|
use it now to preserve all available debug info: |
||||||
|
|
||||||
|
:let g:vlime_compiler_policy = {"DEBUG": 3} |
||||||
|
|
||||||
|
See |g:vlime_compiler_policy| for a detailed description of this |
||||||
|
variable. |
||||||
|
|
||||||
|
With the compiler policy set, we compile fibonacci.lisp and call |
||||||
|
FIBONACCI:GENERATE in the REPL again. Of course the debugger will |
||||||
|
still pop up, but this time it has more detailed stack info: |
||||||
|
|
||||||
|
Thread: 1; Level: 1 |
||||||
|
|
||||||
|
The value |
||||||
|
34 |
||||||
|
is not of type |
||||||
|
LIST |
||||||
|
[Condition of type TYPE-ERROR] |
||||||
|
|
||||||
|
Restarts: |
||||||
|
0. RETRY - Retry SLIME REPL evaluation request. |
||||||
|
1. *ABORT - Return to SLIME's top level. |
||||||
|
2. ABORT - abort thread (#<THREAD "repl-thread" RUNNING {1003907F93}>) |
||||||
|
|
||||||
|
Frames: |
||||||
|
0. (SB-IMPL::LIST-REVERSE 34) |
||||||
|
1. (FIBONACCI:GENERATE 0 55 89 (((# . 13) . 21) . 34)) |
||||||
|
2. (FIBONACCI:GENERATE 1 (((# . 13) . 21) . 34) 55 (((# . 8) . 13) . 21)) |
||||||
|
3. (FIBONACCI:GENERATE 2 (((# . 8) . 13) . 21) 34 (((# . 5) . 8) . 13)) |
||||||
|
4. (FIBONACCI:GENERATE 3 (((# . 5) . 8) . 13) 21 (((# . 3) . 5) . 8)) |
||||||
|
5. (FIBONACCI:GENERATE 4 (((# . 3) . 5) . 8) 13 (((# . 2) . 3) . 5)) |
||||||
|
6. (FIBONACCI:GENERATE 5 (((# . 2) . 3) . 5) 8 (((# . 1) . 2) . 3)) |
||||||
|
7. (FIBONACCI:GENERATE 6 (((# . 1) . 2) . 3) 5 (((# . 1) . 1) . 2)) |
||||||
|
8. (FIBONACCI:GENERATE 7 (((# . 1) . 1) . 2) 3 (((NIL . 0) . 1) . 1)) |
||||||
|
9. (FIBONACCI:GENERATE 8 (((NIL . 0) . 1) . 1) 2 ((NIL . 0) . 1)) |
||||||
|
10. (FIBONACCI:GENERATE 9 ((NIL . 0) . 1) 1 (NIL . 0)) |
||||||
|
11. (FIBONACCI:GENERATE 10 (NIL . 0) 1 NIL) |
||||||
|
12. (SB-INT:SIMPLE-EVAL-IN-LEXENV (FIBONACCI:GENERATE 10) #<NULL-LEXENV>) |
||||||
|
13. (EVAL (FIBONACCI:GENERATE 10)) |
||||||
|
.... |
||||||
|
|
||||||
|
This stack deserves careful examination. We can see each call of the |
||||||
|
FIBONACCI:GENERATE function and the values of it's arguments. To see |
||||||
|
more details, move the cursor to frame 2 and press "d" (without the |
||||||
|
quote marks). A preview buffer will show all local variable names and |
||||||
|
their values, plus other useful info: |
||||||
|
|
||||||
|
Frame: 2 (Restartable) |
||||||
|
|
||||||
|
Locals: |
||||||
|
A: ((((# . 8) . 13) . 21) . 34) |
||||||
|
ACC: ((((# . 5) . 8) . 13) . 21) |
||||||
|
B: 55 |
||||||
|
N: 1 |
||||||
|
|
||||||
|
Location: |
||||||
|
File: /home/user/fibonacci.lisp |
||||||
|
Position: 239 |
||||||
|
Snippet: |
||||||
|
(generate (1- n) b (+ a b) (push acc a)))) |
||||||
|
.... |
||||||
|
|
||||||
|
The variables A and B should be adjacent Fibonacci numbers, but A is |
||||||
|
in fact a strange cons struct. |
||||||
|
|
||||||
|
To make it more challenging, I'll let you use what you learnt to |
||||||
|
figure out what's wrong with our program. Don't worry if you can't |
||||||
|
make it right. There is a corrected version of fibonacci.lisp in the |
||||||
|
next section. See |vlime-mappings-debugger| for all available debugger |
||||||
|
operations. |
||||||
|
|
||||||
|
====================================================================== |
||||||
|
8. The Inspector *vlime-tutor-inspector* |
||||||
|
|
||||||
|
Now that we have fixed fibonacci.lisp, it should look like this: |
||||||
|
|
||||||
|
(in-package #:cl-user) |
||||||
|
|
||||||
|
(defpackage #:fibonacci |
||||||
|
(:use #:cl) |
||||||
|
(:export #:generate)) |
||||||
|
|
||||||
|
(in-package #:fibonacci) |
||||||
|
|
||||||
|
(defun generate (n &optional (a 0) (b 1) (acc (list))) |
||||||
|
(if (<= n 0) |
||||||
|
(reverse acc) |
||||||
|
(generate (1- n) b (+ a b) (push a acc)))) |
||||||
|
|
||||||
|
It works well, but It's a boring function after all. We should |
||||||
|
probably add some fancy classes and methods: |
||||||
|
|
||||||
|
(in-package #:cl-user) |
||||||
|
|
||||||
|
(defpackage #:fibonacci |
||||||
|
(:use #:cl) |
||||||
|
(:export #:generate |
||||||
|
#:generator |
||||||
|
#:next)) |
||||||
|
|
||||||
|
(in-package #:fibonacci) |
||||||
|
|
||||||
|
(defun generate (n &optional (a 0) (b 1) (acc (list))) |
||||||
|
(if (<= n 0) |
||||||
|
(values (reverse acc) a b) |
||||||
|
(generate (1- n) b (+ a b) (push a acc)))) |
||||||
|
|
||||||
|
(defclass generator () |
||||||
|
((param-a :accessor generator-param-a :initform 0) |
||||||
|
(param-b :accessor generator-param-b :initform 1))) |
||||||
|
|
||||||
|
(defgeneric next (obj) |
||||||
|
(:method ((obj generator)) |
||||||
|
(with-slots (param-a param-b) obj |
||||||
|
(multiple-value-bind (result a b) (generate 1 param-a param-b) |
||||||
|
(setf param-a a |
||||||
|
param-b b) |
||||||
|
(car result))))) |
||||||
|
|
||||||
|
We use a modified GENERATE function as a backend to build a GENERATOR |
||||||
|
class. Now let's test it by first creating an instance. Save |
||||||
|
fibonacci.lisp, and compile it by typing "\of" (without the quote |
||||||
|
marks), then go back to ad-hoc.lisp, write down this MAKE-INSTANCE |
||||||
|
expression and send it to the REPL: |
||||||
|
|
||||||
|
(make-instance 'fibonacci:generator) |
||||||
|
|
||||||
|
The REPL buffer will show the result: |
||||||
|
|
||||||
|
-- |
||||||
|
#<FIBONACCI:GENERATOR {1003D83903}> |
||||||
|
|
||||||
|
We can call the accessors or SLOT-VALUE to see whether the slots are |
||||||
|
initialized correctly, but there is an easier way. We should bring up |
||||||
|
the inspector. |
||||||
|
|
||||||
|
Go to the REPL buffer, and move the cursor to the printed |
||||||
|
representation of our newly created instance, then type "\I" (without |
||||||
|
the quote marks). The inspector buffer should appear. The info |
||||||
|
displayed in the inspector will look like this: |
||||||
|
|
||||||
|
#<FIBONACCI:GENERATOR {1003D83903}> |
||||||
|
=================================== |
||||||
|
|
||||||
|
Class: #<STANDARD-CLASS FIBONACCI:GENERATOR> |
||||||
|
-------------------- |
||||||
|
Group slots by inheritance [ ] |
||||||
|
Sort slots alphabetically [X] |
||||||
|
|
||||||
|
All Slots: |
||||||
|
[ ] PARAM-A = 0 |
||||||
|
[ ] PARAM-B = 1 |
||||||
|
|
||||||
|
[set value] [make unbound] |
||||||
|
|
||||||
|
We can see that PARAM-A is 0 and PARAM-B is 1. They are initialized |
||||||
|
correctly. |
||||||
|
|
||||||
|
Now we are going to test the FIBONACCI:NEXT method. We want to call |
||||||
|
the method on the instance we just created, and see if the slot values |
||||||
|
are changed correctly. |
||||||
|
|
||||||
|
We didn't save a reference to the generator instance, but that's not |
||||||
|
a problem. The REPL buffer remembers all evaluation results it has |
||||||
|
seen. We can just yank the result from the REPL buffer. |
||||||
|
|
||||||
|
Keep the inspector open, go back to the REPL buffer, and make sure the |
||||||
|
cursor is still on the printed representation of our |
||||||
|
FIBONACCI:GENERATOR instance. Type "\y" (without the quote marks) to |
||||||
|
yank the object into the register |quotequote|. |
||||||
|
|
||||||
|
Then add a new line in ad-hoc.lisp: |
||||||
|
|
||||||
|
(fibonacci:next ) |
||||||
|
|
||||||
|
Place the cursor on the space character just before the right |
||||||
|
parentheses, and type "p" (without the quote marks). The new line |
||||||
|
should now look like this: |
||||||
|
|
||||||
|
(fibonacci:next (swank:lookup-presented-object 10)) |
||||||
|
|
||||||
|
You may have a number other than "10", and that's OK. The |
||||||
|
SWANK:LOOKUP-PRESENTED-OBJECT expression returns the |
||||||
|
FIBONACCI:GENERATOR instance saved by the REPL. |
||||||
|
|
||||||
|
Evaluate the whole FIBONACCI:NEXT expression a few times, and we'll |
||||||
|
get a series of Fibonacci numbers in the REPL buffer. |
||||||
|
|
||||||
|
Now go to the inspector and press "R" (without the quote marks) to |
||||||
|
refresh it. We can see that PARAM-A and PARAM-B are successfully |
||||||
|
changed: |
||||||
|
|
||||||
|
#<FIBONACCI:GENERATOR {100257FFE3}> |
||||||
|
=================================== |
||||||
|
|
||||||
|
Class: #<STANDARD-CLASS FIBONACCI:GENERATOR> |
||||||
|
-------------------- |
||||||
|
Group slots by inheritance [ ] |
||||||
|
Sort slots alphabetically [X] |
||||||
|
|
||||||
|
All Slots: |
||||||
|
[ ] PARAM-A = 377 |
||||||
|
[ ] PARAM-B = 610 |
||||||
|
|
||||||
|
[set value] [make unbound] |
||||||
|
|
||||||
|
The inspector is also capable of setting slot values. The square |
||||||
|
brackets in the inspector buffer denote buttons, we can use these |
||||||
|
buttons to manipulate PARAM-A and PARAM-B. |
||||||
|
|
||||||
|
Say we want to fast forward the generator to the 50th Fibonacci |
||||||
|
number. Move the cursor inside the brackets just before PARAM-A, and |
||||||
|
press <CR> or <Space>. An "X" will appear to indicate that this slot |
||||||
|
is selected. Do the same with PARAM-B. The inspector should now look |
||||||
|
like this: |
||||||
|
|
||||||
|
#<FIBONACCI:GENERATOR {100257FFE3}> |
||||||
|
=================================== |
||||||
|
|
||||||
|
Class: #<STANDARD-CLASS FIBONACCI:GENERATOR> |
||||||
|
-------------------- |
||||||
|
Group slots by inheritance [ ] |
||||||
|
Sort slots alphabetically [X] |
||||||
|
|
||||||
|
All Slots: |
||||||
|
[X] PARAM-A = 377 |
||||||
|
[X] PARAM-B = 610 |
||||||
|
|
||||||
|
[set value] [make unbound] |
||||||
|
|
||||||
|
Now move the cursor to "[set value]" and press <CR> or <Space>. A |
||||||
|
buffer will pop up at the bottom prompting for the value of slot |
||||||
|
PARAM-A. Let's write an expression to calculate the 50th Fibonacci |
||||||
|
number: |
||||||
|
|
||||||
|
; Set slot FIBONACCI::PARAM-A to (evaluated) : |
||||||
|
(car (last (fibonacci:generate 50))) |
||||||
|
|
||||||
|
Then go back to normal mode and press <CR>. Another buffer will |
||||||
|
appear, prompting for the value of PARAM-B. We give it the 51st |
||||||
|
number: |
||||||
|
|
||||||
|
; Set slot FIBONACCI::PARAM-B to (evaluated) : |
||||||
|
(car (last (fibonacci:generate 51))) |
||||||
|
|
||||||
|
Remember to press <CR> in normal mode after you have done editing the |
||||||
|
expression. The inspector should be automatically updated and look |
||||||
|
like this: |
||||||
|
|
||||||
|
#<FIBONACCI:GENERATOR {100257FFE3}> |
||||||
|
=================================== |
||||||
|
|
||||||
|
Class: #<STANDARD-CLASS FIBONACCI:GENERATOR> |
||||||
|
-------------------- |
||||||
|
Group slots by inheritance [ ] |
||||||
|
Sort slots alphabetically [X] |
||||||
|
|
||||||
|
All Slots: |
||||||
|
[X] PARAM-A = 7778742049 |
||||||
|
[X] PARAM-B = 12586269025 |
||||||
|
|
||||||
|
[set value] [make unbound] |
||||||
|
|
||||||
|
We can call FIBONACCI:NEXT again to confirm that the internal state of |
||||||
|
our generator instance is indeed changed: |
||||||
|
|
||||||
|
(fibonacci:next (swank:lookup-presented-object 10)) |
||||||
|
|
||||||
|
And the REPL buffer shows: |
||||||
|
|
||||||
|
-- |
||||||
|
7778742049 |
||||||
|
|
||||||
|
The inspector can also look inside lists, vectors, hashtables, and |
||||||
|
packages. You can try to create some of these data structures and peek |
||||||
|
inside. See |vlime-mappings-inspector| for all available operations in |
||||||
|
the inspector. |
||||||
|
|
||||||
|
You can simply close the inspector window when you've done playing |
||||||
|
with it. |
||||||
|
|
||||||
|
====================================================================== |
||||||
|
9. Conclusion *vlime-tutor-conclusion* |
||||||
|
|
||||||
|
Our Finbonacci sequence generator is working, and we have covered the |
||||||
|
most frequently used features of Vlime. You should now be able to use |
||||||
|
Vlime to write and debug Common Lisp programs. You can then read the |
||||||
|
full documentation in |vlime.txt|. |
Loading…
Reference in new issue