![]() |
|
In this chapter we're taking a tour "behind the scenes" of Berkeley Logo.
Many of the built-in Logo procedures that we've been using all along are
not, strictly speaking, primitive; they're written in Logo itself. When you
invoke a procedure, if the Logo interpreter does not already know a procedure
by that name, it automatically looks in a library of predefined
procedures. For example, in Chapter 6 I used an operation called
gensym
that outputs a new, unique word each time it's invoked. If
you start up a fresh copy of Logo you can try these experiments:
? po "gensym I don't know how to gensym ? show gensym g1 ? po "gensym to gensym if not namep "gensym.number [make "gensym.number 0] make "gensym.number :gensym.number + 1 output word "g :gensym.number end
The first interaction shows that gensym
is not really a Logo
primitive; the error message indicates that there is no such procedure. Then
I invoked gensym
, which made Berkeley Logo read its definition
automatically from the library. Finally, once Logo has read the definition,
I can print it out.
In particular, most of the tools we've used to carry out a computation
repeatedly are not true Logo primitives: for
for numeric
iteration, foreach
for list iteration, and while
for predicate-based iteration are all library procedures. (The word
iteration just means "repetition.") The only iteration mechanisms
that are truly primitive in Logo are repeat
and recursion.
Computers are good at doing things over and over again. They don't get bored or tired. That's why, in the real world, people use computers for things like sending out payroll checks and telephone bills. The first Logo instruction I showed you, in the first volume, was
repeat 50 [setcursor list random 75 random 20 type "Hi]
When you were first introduced to turtle graphics, you probably used an instruction like
repeat 360 [forward 1 right 1]
to draw a circle.
The trouble with repeat
is that it always does exactly
the same thing repeatedly. In a real application, like those payroll
checks, you want the computer to do almost the same thing each
time but with a different person's name on each check. The usual way
to program an almost-repeat
in Logo is to use a recursive
procedure, like this:
to polyspi :side :angle :number if :number=0 [stop] forward :side right :angle polyspi :side+1 :angle :number-1 end
This is a well-known procedure to draw a spiral. What makes it different from
repeat :number [forward :side right :angle]
is that the first input in the recursive invocation is :side+1
instead of just :side
. We've used a similar technique for
almost-repetition in procedures like this one:
to multiply :letters :number if equalp :number 0 [stop] print :letters multiply (word :letters first :letters) :number-1 end
Since recursion can express any repetitive computation, why bother inventing other iteration tools? The answer is that they can make programs easier to read. Recursion is such a versatile mechanism that the intention of any particular use of recursion may be hard to see. Which of the following is easier to read?
to fivesay.with.repeat :text repeat 5 [print :text] end
or
to fivesay.with.recursion :text fivesay1 5 :text end to fivesay1 :times :text if :times=0 [stop] print :text fivesay1 :times-1 :text end
The version using repeat
makes it obvious at a glance
what the program wants to do; the version using recursion takes some
thought. It can be useful to invent mechanisms that are intermediate
in flexibility between repeat
and recursion.
As a simple example, suppose that Logo did not include repeat
as
a primitive command. Here's how we could implement it using recursion:
to rep :count :instr if :count=0 [stop] run :instr rep :count-1 :instr end
(I've used the name rep
instead of repeat
to avoid
conflict with the primitive version.) The use of run
to carry out
the given instructions is at the core of the techniques we'll use
throughout this chapter.
Polyspi
is an example of an iteration in which the value of a
numeric variable changes in a uniform way. The instruction
polyspi 50 60 4
is equivalent to the series of instructions
forward 50 right 60 forward 51 right 60 forward 52 right 60 forward 53 right 60
As you know, we can represent the same instructions this way:
for [side 50 53] [forward :side right 60]
The for
command takes two inputs, very much like
repeat
. The second input, like repeat
's second
input, is a list of Logo instructions. The first input to for
is different, though. It is a list whose first member is the name of a
variable; the second member of the list must be a number (or a Logo
expression whose value is a number), which will be the initial
value of that variable; and the third member must be another number (or
numeric expression), which will be the final value of the
variable. In the example above, the variable name is side
, the
initial value is 50
, and the final value is 53
.
If there is a fourth member in the list, it's the amount to add to the named
variable on each iteration; if there is no fourth member, as in the example
above, then the step amount is either 1 or -1, depending on
whether the final value is greater than or less than the initial value.
As an example in which expressions are used instead of constant numeric
values, here's the polyspi
procedure using for
:
to polyspi :start :angle :number for [side :start [:start+:number-1]] [forward :side right :angle] end
Most of the work in writing for
is in evaluating the expressions
that make up the first input list. Here is the program:
to for :values :instr localmake "var first :values local :var localmake "initial run first butfirst :values localmake "final run item 3 :values localmake "step forstep localmake "tester ~ ifelse :step < 0 [[:value < :final]] [[:value > :final]] forloop :initial end to fo.TH DWM 1 dwm-VERSION .SH NAME dwm \- dynamic window manager .SH SYNOPSIS .B dwm .RB [ \-v ] .SH DESCRIPTION .B dwm is a dynamic window manager for X. It manages windows in tiling and floating modes. Either mode can be applied dynamically, optimizing the environment for the application in use and the task performed. .P In tiling mode windows are managed in a master and stacking column. The master column contains the window which currently needs most attention, whereas the stacking column contains all other windows. In floating mode windows can be resized and moved freely. Dialog windows are always managed floating, regardless of the mode selected. .P Windows are grouped by tags. Each window can be tagged with one or multiple tags. Selecting a certain tag for viewing will display all windows with that tag. .P .B dwm contains a small status bar which displays the text read from standard input. It also displays all active tags and the title of the focused window. .P .B dwm draws a 1-pixel border around windows to indicate the focus state. Unfocused windows contain a small bar in front of them displaying their tags and title. .SH OPTIONS .TP .B \-v prints version information to standard output, then exits. .SH USAGE .SS Status bar .TP .B Standard input is read and displayed in the status text area. .TP .B Button1 click on a tag label views all windows with that .BR tag . .TP .B Button3 click on a tag label adds/removes all windows with that .B tag to/from the view. .SS Keyboard commands .TP .B Mod1-Shift-Return Start .BR xterm (1). .TP .B Mod1-Tab Focus next .BR window . .TP .B Mod1-Shift-Tab Focus previous .BR window . .TP .B Mod1-Return Zoom current .B window to the .B master column .RB ( tiling mode only). .TP .B Mod1-m Maximize current .BR window . .TP .B Mod1-Shift-[0..n] Apply .B nth tag to current .BR window . .TP .B Mod1-Control-Shift-[0..n] Add/remove .B nth tag to/from current .BR window . .TP .B Mod1-Shift-c Close focused .B window. .TP .B Mod1-space Toggle between .B tiled and .B floating mode (affects .BR "all windows" ). .TP .B Mod1-[0..n] View all windows with .BR "tag n" . .TP .B Mod1-Control-[0..n] Add/remove all windows with .B tag n to/from the view. .TP .B Mod1-Shift-q Quit .B dwm. .SS Mouse commands .TP .B Mod1-Button1 Move current .B window while dragging .RB ( floating mode only). .TP .B Mod1-Button2 Zoom current .B window to the .B master column .RB ( tiling mode only). .TP .B Mod1-Button3 Resize current .B window while dragging .RB ( floating mode only). .SH CUSTOMIZATION .B dwm is customized by creating a custom config.h and (re)compiling the source code. This keeps it fast, secure and simple. .SH CAVEATS The status bar displays .B broken pipe when .B dwm has been started by .BR xdm (1), because it closes standard output before executing .BR dwm . .SH SEE ALSO .BR dmenu (1)