Our goal is to create a new function
star, so that star 12, blue
draws a blue twelve-pointed star.
Introduction to Functions
Here is the most important concept in all of programming:
A function is a miniature program. In CoffeeScript,
a function is written with an arrow -> typed
as two symbols next to each other (the minus and
the greater-than) like this:
(input) -> something to do
Writing and Naming Functions
A function that advances the turtle by ten times a distance is
(x) -> fd 10 * x
Name a function like any variable,
using =.
scoot = (x) -> fd 10 * x
After the definition, we can write
scoot 7 or scoot 5 + 2.
In other words, scoot can be used just
like predefined functions like fd or rt.
Making a star Function
Functions can be long.
This code defines a star function
using indented lines after the ->:
star = (n, k) -> # define a function star with two inputs and code to follow:
pen k # use the chosen color.
for [1..n] # repeat the following n times:
fd 100 # move forward.
rt 2 * 360 / n # turn right using a magic star formula.
speed 10 # draw fast!
star 7, red # use our newly-defined function!
Exercises
star
function, and save it as "star1". Some tips:
->.
star = ... will
not run it yet.
To run it you must use it:
star 7, red.
star function with
different numbers, and note the outcomes below:
| Test | Outcome | Test | Outcome |
star 5, red |
star 9, red |
||
star 6, red |
star 10, red |
||
star 7, red |
star 11, red |
||
star 8, red |
star 12, red |
Debugging the Algorithm
There is a problem with
star whenever we ask for
an even number of points:
star = (n, k) ->
pen k
for [1..n]
fd 100
rt 2 * 360 / n
speed 10
star 8, red
What causes the bug?
The problem is in the "magic" formula
2 * 360 / n.
To understand, read on.
The Geometry of Stars
Drawing a star is similar to drawing
a regular polygon. After the turtle has
gone all the way around all n
corners, it will come back to where it
started, facing the same direction it started.
The difference between a star and a polygon is its winding number: In a polygon, all the turns total up to 360° because each of the n turns is (360/n)°. So the turtle winds once around the center: its "winding number" is 1. In a star, the turtle turns total to a larger multiple of 360, such as 2×360, which gives it a winding number of 2.
If we change the "magic formula" to
3 * 360 / n, we will get
a star with winding number 3. That
experiment is shown below.
star = (n, k) ->
pen k
for [1..n]
fd 100
rt 3 * 360 / n
speed 10
star 8, red
The problem is this: if the winding number evenly divides the number of corners, we retrace old corners exactly. For example, when using the winding number 2 with 8 points, 2 divides 8, and we retrace the same 4 corners twice. The winding number 3 works better.
An Algorithm With an Answer: Picking a Winding Number
How do we pick a winding number for
an n-pointed star? We can write
an algorithm for that!
n points:
c
from 2 to n as follows:
c not a divisor
of n:
c is our answer.
Translated from words into CoffeeScript:
winding = (n) -> # define a function named winding.
for c in [2..n] # vary c from 2 to n, starting at 2.
if (n % c) isnt 0 # if c is not a divisor of n then:
return c # c is the answer.
write winding 8 # run our function and write the answer.
Here are tools that we used to write this algorithm in CoffeeScript:
for c in [2..n] |
Control.
Here c is a
looping variable.
When for is used with a
looping variable, it repeats its block
of code once for each value of
in the range, or until a special
command like return
quits the loop.
|
if |
Control.
if condition
runs the indented code only when the condition is true.
|
% |
Arithmetic.
The "modulo" arithmetic operator, written
using the percent symbol
x % y, calculates the remainder
when x is divided by y.
For example,
15 % 515 % 4 is 3.
|
isnt |
Arithmetic.
x isnt y tests
if two values are unequal. For example,
(n % c) isnt 0n is divided by c.
|
return |
Control. When inside a function,
return answer ends the
calculation.
|
Exercises
winding function, and save it as
"winding". Remember:
while
will happen inside the loop.
return
the answer outside the loop, return should
not be doubly-indented. But since the
return is part of the function,
it should be singly-indented.
winding function with
different numbers, and note the outcomes below:
| Test | Outcome | Test | Outcome |
winding 5 |
winding 9 |
||
winding 6 |
winding 10 |
||
winding 7 |
winding 11 |
||
winding 8 |
winding 12 |
Using one Function inside Another
After we define winding, we can
use it anywhere, including inside the
star
function. Here is how:
winding = (n) -> # define the function winding
for c in [2..n]
if (n % c) isnt 0
return c
star = (n, k) -> # define the function star
pen k
w = winding n # use winding to pick w
for [1..n]
fd 100
rt w * 360 / n # use w as the winding number
star 12, purple # draw a star!
The Importance of Testing
It is important to test algorithms! If you
test this algorithm carefully, you will find that
it still does not work for some values
of n. For example,
star 18, red draws a shape
with only 9 stars.
It turns out that a better way to choose a winding number is to choose a number that has no common divisors with n (other than 1).
The following is Euclid's algorithm to calculate the greatest common divisor of two numbers:
gcd = (a, b) ->
while b isnt 0 # repeat the indented lines while b isn't zero:
if a > b # if a is greater than b, then do this:
a = a - b # replace a with the difference (a - b)
else # otherwise (if a is not greter than b):
b = b - a # replace b with the difference (b - a)
return a # after the loop is done, the answer is a
If a and b have
no common divisors, then gcd(a, b)
is 1.
Exercises
winding to use
gcd instead of %:
gcd to your program before you use it.
c that
satisfies the condition gcd(n, c) is 1.
star 18, red? ______
n. Create a program "star4" that
produces pointy stars:
floor
rounds down, so floor(n/2) is half of
n, rounded down.
for [floor(n/2)..1].
n
does star draw the least-pointy
shape? ______
speed Infinity,
pen path and fill gold.