COMP203: Lecture 13

Logo Exam, 3/24 (next class)
Assignment 6 due 3/26
Project due 3/31

Recursion, for and maybe while

When circumstances allow, I'll be typing up bits of my lecture notes and posting them online. These may or may not bear any resemblance to the actual lectures.

Chapter 7

We've written a maze game like this one:
to maze.game
 print [You are lost in a cave.  Will you go left or right?]
 if equalp readword "left [print [You are saved!] stop]
 maze.game
end
and seen an example of a procedure that computes factorials:
to factorial :number
  if equalp :number 0 [output 1]
  output :number * factorial (:number - 1)
end
These are examples of recursive procedures. Recursive procedures have the following two properties:

Variables and Recursion

In procedure factorial, the value of variable number changes as the procedure is run over and over, and the output of factorial depends on the value input. To do something like split a list into two parts, we need to be comfortable using variables in recursive procedures.

The procedure below accepts a length as input and draws a spiral shape whose longest leg is that length.

to spiral :length
  if lessp :length 1 [stop]
  forward :length
  right 60
  spiral (:length - 10)
end
What is the stop condition? Where is the recursive call in spiral?

Play around with the spiral procedure -- what happens if you change the 60 or the 10? What happens if you change the order the instructions appear? Why?

Finally, let's write a recursive procedure which accepts a list as input and outputs the first half of that list. Logo commands we might need include item, first, butfirst, count, sentence, make, if and output.

We start by making a plan. Our stop condition will be that we're halfway through the list; since we're writing a recursive operation, we'll have to output something when we stop the procedure. At each recursive step we'll take the next item on the list we start with and add it to the new list we're creating.

The input to our procedure will be the list we're cutting in half, and its output will be the first half of that list.

to halflist :oldlist
  local "listsize
  make "listsize count :oldlist

  if lessp count :oldlist (quotient :listsize 2) [output []]

  output sentence first :oldlist (halflist butfirst :oldlist)
end
This procedure should stop when its input is less than half as long as the list we started with, outputting an empty list. At each recursive step, it attaches the first thing in the list to its output and shortens the list by one. Why doesn't it work?

The error message first doesn't like [] as input in halflist is a hint that the list was emptied before the stop condition was reached. How can that be?

Each time we run procedure halflist, we create a new local variable listsize and set it equal to the length of our list. That means that the value stored in variable listsize gets smaller as our list gets shorter. That's not what we want!

We can solve this problem using a helper procedure which sets the value of listsize and then uses a recursive procedure that does not change listsize to finish the task.

to newhalflist :oldlist
  local "listsize
  make "listsize count :oldlist

  output halfhelper :oldlist
end

to halfhelper :oldlist
  if lessp count :oldlist (quotient :listsize 2) [output []]

  output sentence first :oldlist (halfhelper butfirst :oldlist)
end
After testing this, we might want to chagen lessp to not greaterp, but otherwise it seems to work!

Challenge

Can you write a Logo procedure that accepts a list as input and displays each member of the list alone on the screen? The Logo primitives first, butfirst and emptyp may be helpful here, or you might use count and item.

Chapter 5

As we've said, Logo is good at dealing with lists. Chapter 5 introduces a marvelous array of list handling tools which are extremely powerful. We'll look chiefly at for, leaving the other tools for advanced Logo users.

for

Recursion is a powerful tool, but many students find recursive programs confusing. If you find it easier to think about doing something to the first, second, third, ... items in a list rather working on the first thing in a list and then using recursion to handle the rest of the list, you'll like for.

We can compare programming using for to writing a direct proof as opposed to a (recursive) proof by induction.

for is a command which accepts two inputs. The first describes an index variable or counter, the values it counts from and to, and the increments it counts by. The second is a list of instructions to perform, similar to the list of instructions input to repeat or if. The difference between for and repeat is that the instructions repeated by for can depend on the index variable in the first input.

Here are some very simple examples of what for does. Try each one and its associated experiment.

for [i 1 20] [print :i]
Can you make Logo count from 1 to 10? From 10 to 20?
for [value 0 100 5] [print :value]
Can you make Logo count from 0 to 20 by 2's?
for [num 100 0 -10] [print :num]
Can you make Logo count down from 10 to 1 by 1's?
for [num 0 20 7] [print :num]
If Logo overshoots its "target value" when counting, what happens? What happens if you ask Logo to count from 10 to 0 by 3's?
for [length 0 100 10] [forward :length right 90]
Change this procedure to make Logo draw a spiral that's more to your liking (use clearscreen to erase things you don't like.)
for [size 50 200 50] [left 90 for [length 0 :size 10] [forward :length right 90]]
This is an example of two "nested loops". (We think of for as causing Logo to "loop back" and repeat the instructions in the second input.) Can you write a single instruction to make Logo count from 0 to 100 by 1's, 2's, 3's, ... 10's?

Half a List

How would we cut a list in half using for? We could create a new list and add the first, second, third, .... items in the list to the new list until we were halfway through.

Here's a first attempt at a procedure to do this. See if you can figure out why it doesn't work and fix it.

to halflist :mylist
  local "newlist
  for [i 1 [quotient (count :mylist) 2]] ~
      [make "newlist sentence :newlist (item :i :mylist)]
  output :newlist
end
Hint: many of you made the same mistake on your three question quiz.