Tips and tricks
1 Exiting things from the command line
There are many ways to kill or exit a process, but some are much cleaner than others. If you go for the more violent way every time, it can leave a big mess behind that you have to clean up later.
"Not leaving a mess" is especially important if your program is using a bunch of memory/CPU, or if it's editing a file that you care about. So follow these quick instructions to keep things tidy, and save the gory methods for when they're really needed.
1.1 Clean exits from python, scheme, etc
For most interactive command-line interpreters such as Python, ChezScheme, and even the SPL interpreter you will write, the "clean" way to exit is by entering Ctrl-D. This tells the program, essentially, "I'm done typing here", and will generally result in a nice, clean exit
1.2 man and less
These programs are showing you a text file (or something like it), or allowing you to edit a file right in the terminal.
For less
and man
, the standard way to quit is by simply
typing q.
1.3 vi and vim
For vi
or vim
, you also quit with the "q" command, but it's
a little more complicated. If you are in "insert mode" to actually make edits, then you
have to hit escape first. And then you have to type a colon to get to the command prompt.
And then if there are unsaved changes, you have to tell vim how to deal with that.
Here's a summary of how to get out of vim. Make sure you actually type the colon after hitting escape!
Key sequence | What it does |
<Esc>:q | Quit if nothing in the file was changed |
<Esc>:wq | Save your changes, then quit |
<Esc>:q! | Quit right now, changes be damned |
1.4 Violence
Sometimes, being a nice person just isn't enough to get your way. Here are some more violent ways to end a command-line program, in order of increasing levels of destruction.
- <Ctrl>-C: Sends SIGINT, asking nicely for the process to end itself.
- <Ctrl>-Z: Sends SIGSTOP, asking the shell to stop (pause) the process
and bring you back to the shell. At this stage the program is not dead! You still have to kill it!
The shell will show you a line like:
The[1]+ Stopped ./myprogram
[1]
tells you the job number, which might be larger than 1 if you have other stopped jobs in the same terminal. You use%1
to refer to this process for thekill
command.
Try killing with this order of urgency:- Gentle request (SIGINT):
kill -2 %1
- More urgent (SIGTERM):
kill -15 %1
- No more Mr. Nice Guy (SIGKILL):
kill -9 %1
- Gentle request (SIGINT):
2 Debugging with ChezScheme
There is a nice article here about the many ways to debug a program in ChezScheme. My favorite part is the technique of "Staring at the code". Below are two of the more concrete tools that you might find useful.
I am going to use the following (faulty) Scheme program as an example. It is supposed to take any list and return a new list with every other element from the first one.
(define (every-other L)
(if (null? (cdr L))
L
(cons (car L)
(every-other (cdr (cdr L))))))
When we run this, it works sometimes but not other times:
>
(every-other '(1 2 3 4 5))
(1 3 5)
>
(every-other '(1 2 3 4 5 6))
Exception in cdr: () is not a pair
Type (debug) to enter the debugger.
Let's look at two ways we can use ChezScheme to help us figure out what went wrong here.
2.1 trace
The first useful tool to debugging a recursive function is to trace it. What that means is that, every time the function gets called (recursively), the interpreter will show you what the arguments are, as well as the eventual return value. Very useful!
To trace a function, you just run (trace function-name)
in Scheme, where of course you want to replace function-name
with
the name of your function.
Here you can see the same two function calls above, but with tracing enabled:
>
(trace every-other)
(every-other)
>
(every-other '(1 2 3 4 5))
|(every-other (1 2 3 4 5))
| (every-other (3 4 5))
| |(every-other (5))
| |(5)
| (3 5)
|(1 3 5)
(1 3 5)
>
(every-other '(1 2 3 4 5 6))
|(every-other (1 2 3 4 5 6))
| (every-other (3 4 5 6))
| |(every-other (5 6))
| | (every-other ())
Exception in cdr: () is not a pair
Type (debug) to enter the debugger.
2.2 debug
The other option you have is to actually use ChezScheme's built-in debugger.
Start by typing (debug)
when it asks you:
>
(every-other '(1 2 3 4 5 6))
Exception in cdr: () is not a pair
Type (debug) to enter the debugger.
>
(debug)
debug>
At any of the debug
-related prompts, you always have two commands you can type:
- ?: show a help screen with available commands
- q: quit out of the debugger and go back to the Scheme interpreter
If you just want to find out where in the code your error came from, you can start with the i command to inspect the error, and then type show to see the expression that caused the error, or file to get the line and column number in your .scm file where the issue came from:
>
(every-other '(1 2 3 4 5 6))
Exception in cdr: () is not a pair
Type (debug) to enter the debugger.
>
(debug)
debug> i
#
: file line 5, character 11 of error_example.scm
#
: s continuation: #
procedure code: (lambda (L) (if (null? (...)) L ...))
call code: (every-other (cdr (cdr L)))
frame and free variables:
0: 5
This is telling us that the error occurred on line 5, character 11 of my
file. And the specific expression is the recursive call on
(every-other (cdr (cdr L)))
.
(To fix this program, we need another base case for when the list is null.)