turtleto move around a screen drawing coloured lines as he goes.
To make logo programming more powerful
, you also get a looping
construct, procedures, and arithmetic expressions, all explained in
more detail below.
Pen down. colour is a character in the range
ato
pinclusive. After calling this, whenever the turtle moves, he will trace a line in the selected colour.
Pen up. After calling this, until you call pd again, any movements the turtle makes won't leave a line behind him or disturb any pixels.
The screen goes from -500 to +500 on both axes.
This shows all the different colours. Colour a
is black. Things
like call, rot, defproc etc. are
explained later.
pu bk 300 lt 90 fd 300 rot
pd b call square 100
pd c call square 100
pd d call square 100
pd e call square 100
pu bk 100 rt 90 fd 100 rt 90
pd f call square 100
pd g call square 100
pd h call square 100
pd i call square 100
pu bk 100 lt 90 fd 300 lt 90
pd j call square 100
pd k call square 100
pd l call square 100
pd m call square 100
pu bk 100 rt 90 fd 100 rt 90
pd n call square 100
pd o call square 100
pd p call square 100
defproc square side
rep 4
fd side lt 90
endrep
pu
fd [side * 2]
endproc
parameters. For example, the
fd command must be followed by exactly
one parameter-- the distance to go forward. The pu command
on the other hand, must not be followed by any parameters. Commands,
parameters, and other commands are all separated by whitespace-- which is 1 or more of either or all of the newline, tab or space characters.
Lists (used in procedure definitions and calls, see below) can't have
whitespace in them. The members of the list are separated with the
: character. The empty list is represented by a single
:.
Procedure and parameter names can use any characters you like so long as they can't be confused with anything else (digits, operators, commands etc.)
Arithmetic expressions must be enclosed in a single set of square brackets.
Comments start with # and carry on to the end of the line.
You can tell the turtle to repeat a series of commands a certain number
of times using the rep command.
e.g.
rep 4
fd 100 rt 90
endrep
will draw a square.
Loops can be nested as many times as you like, so:
rep 36
rep 4 fd 100 rt 90 endrep
rt 10
endrep
will draw a whole fan of squares.
For example:
defproc rectangle width:height
rep 2
fd width lt 90 fd height lt 90
endrep
endproc
defines the procedure rectangle.
Tells the turtle to execute the procedure with those parameters, e.g.
call rectangle 100:200
will call the procedure rectangle with parameters 100 for
width, and 200 for height. This means all the commands
between defproc rectangle width:height and
endproc will have width and
height substituted for 100 and
200 respectively, and given to the turtle to execute.
Procedures can call other procedures, can pass their parameters on to other procedures, and parameters can be included in arithmetic expressions. Here is an example illustrating all these features:
defproc polygon size:n
rep n
fd size
rt [360 / n]
endrep
endproc
defproc polygon_fan size:n
rep [2 * n]
call polygon size:n
rt [360 / (2 * n)]
endrep
endproc
call polygon_fan 200:6
fd 100
(which means go forward 100
), you can tell him:
fd [(30 + 10 * 2) * 2]
which means the same thing.
Arithmetic expressions must be enclosed within square brackets. Inside
the square brackets you can put spaces, tabs, or newlines (whitespace)
between things as you please, but you don't have to. Expressions are
evaluated using the proper rules for precedence, may contain the
operators +, *, /,
-, parentheses (, ), and integer
or decimal numbers.
A special feature of this logo interpreter is that [0 / 0] =
0. In fact, 0 / 0 is a trinity
that can be either 0,
1 or infinity, but in this logo interpreter, it always 0. The more
astute reader will realize that this property means you can write:
pu lt 90 fd 400 rt 90 pd k
defproc zig count
rep [count / count]
rt 45 fd 50 rt 90 fd 50 lt 135
call zig [count - 1]
endrep
endproc
call zig 4
instead of the more conventional:
pu lt 90 fd 400 rt 90 pd k
defproc zig count
rep count
rt 45 fd 100 rt 90 fd 100 lt 135
endrep
endproc
call zig 4
You may think (rightly) that this is a rather convoluted way to do
things. Really, the special rule for division means rep
can be used to synthesize if in the sense of if not zero. With if and recursion, you can write loops without using
rep … but then you used rep to
simulate if and if you had rep in the first
place then when not just use it for the purpose for which it
was obviously intended?
Being able to simulate if with the [0 / 0]
rule does mean however that you can implement more powerful kinds of
looping than rep on its own is capable of. It isn't
possible to write a program to draw a spiral, for example, without
using recursion.
defproc corner length:angle
rep 2
fd length rt angle
endrep
endproc
defproc spiral length:pitch:laps
rep [laps / laps]
call corner [length + laps * pitch]:20
call spiral length:pitch:[laps - 1]
endrep
endproc
pu
lt 90 fd 300 rot
pd m
call spiral 3:1:70
The more elegant solution would have been to provide if
and recursion, and done away with rep. The practical
solution would have been to provide if as well as
rep, and if one was feeling really generous, something
like for as well-- not that you need it though.
The example we've just seen shows how to use tail-recursion to
implement a loop where some of the values inside the loop change with
each iteration-- something we couldn't do with just rep,
which can only repeat exactly a series of turtle commands.
Here's another example using recursion to draw square spirals in the
'Grecian' style with an inner spiral. Note that we call the function to
draw a right hand bend before recursing, and the left hand
bend afterwards. That means we draw count right hand
bends first, and then on the way back up
, another
count left hand bends.
defproc right size
rep 2
fd size rt 90
endrep
endproc
defproc left size
rep 2
fd size lt 90
endrep
endproc
defproc spiral count:size:pitch
rep [count / count]
call right [size * count]
call spiral [count - 1]:size:pitch
call left [(size - pitch) * count]
endrep
rep [1 - count / count]
fd [pitch / 2] rt 90
endrep
endproc
defproc pattern count:size:pitch
rep 2
pd k
call spiral count:size:pitch
pd l
call spiral count:size:pitch
endrep
endproc
call pattern 8:50:6
Two contrasting styles of programming and programming languages are
called imperative
and functional
. Logo is almost a purely
imperative programming language (somewhat corrupted by having
arithmetic expressions), Scheme is almost completely functional.
Therefore it is interesting to implement the logo interpreter in
Scheme. The Scheme is compiled to C with chicken,
and from C to a binary executable with a C compiler.