COMP203: Lecture 8


Syllabus | Homework and Assignments | Grading Rubric | Midterm Exam | Final Project

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.

readlist and Functional Programming

On page 43 of Chapter 3, the text book has the following procedure:
to primer :name
print (sentence first :name [is for] word :name ".)
print (sentence "Run, word :name ", "run.)
print (sentence "See :name "run.)
end
Is this procedure an operation or a command?

Procedure primer is more difficult to use than procedure converse from chapter 1 because you have to know in advance what input it's expecting.

? primer "Fred
F is for Fred.
Run, Fred, run.
See Fred run.
? primer [Fred Flintstone]
word doesn't like [Fred Flintstone] as input  in primer
[print (sentence first :name [is for] word :name ".)]
? primer
not enough inputs to primer
?
Procedure hi on page 5 of Chapter 1 asked a person for their name and then used it in a sentence too. Why can't we do this here?
to hi
print [Hi. What’s your name?]
print sentence [How are you,] word first readlist "?
ignore readlist
print [That’s nice.]
end
The Logo operation readlist reads what's typed on a line and outputs that value as a list. Can we rewrite the primer2 procedure so that it uses readlist?
to primer2 :firstname :lastname
 print sentence [A student named ] :firstname
 print [took a class on Logo.]
 print sentence [The teacher said hey ] :lastname 
 print [and helped to make the code go.]
end

A common error is to write:

to primer6
 print [What is your name?]

 print (sentence [A student named ] first readlist [took a class on Logo.])
 print (sentence [The teacher said hey ] last readlist [and helped to make the~
 code go.])
end
Try running this procedure. Can you figure out why it stops in the middle?

The texbook author would write this procedure by making the output of readlist be the input procedure.

Functional programming is a style of programming in which variable values never change within the body of a procedure. Functional programming requires a lot of skill and talent, but eliminates a lot of errors that arise from changing variable values using make. (E.g. it's impossible to accidentally change the value of a global variable if your procedures are written in a functional programming style.)

Below is an example of how our primer procedure might appear if it were written using functional programming:

to primer7
 print [What is your name?]
 poem readlist                ; use the output of readlist as the
                              ; input to a procedure.
end

to poem :name
 print (sentence [A student named ] first :name [took a class on Logo.])
 print (sentence [The teacher said hey ] last :name [and helped to make the~
 code go.])
end
Another way to do this would be to use make with readlist to store the ouptut of readlist in some variables:
to primer5
 (local "name "firstname "lastname) ; Use parentheses aroud multiple inputs.
 print [What is your name?]
 make "name readlist          ; This stores the *whole* name
 make "firstname first :name  ; This gets and stores the first name
 make "lastname last :name    ; This gets and stores the last name

 print (sentence [A student named ] :firstname [took a class on Logo.])
 print (sentence [The teacher said hey ] :lastname [and helped to make the~
 code go.])
end
This works the way we want it to, but creates three global variables.

readword and Newton's Method

Last class we wrote a procedure that took a guess at the x-intercept of a function and used Newton's method to improve that guess. For example, to estimate the zeros of the function f(x) = x^3 - 8 we'd use the code:
to improve_guess :guess
  output :guess - (function :guess) / (slope :guess)
end

to function :value
  output :value * :value * :value - 8
end

to slope :value
  output 3 * :value * :value
end
The Logo operation readword reads a value from the keyboard and then outputs it; in that sense it's like readlist. The difference between readword and readlist is that the output of readword is a word and the output of readlist is a list. The operation readword is a good one for reading numerical values from the keyboard (as in a math test program.) Write a Logo program that uses improve_guess to create a user-friendly x-intercept estimation process, as illustrated in the example below.
? findintercept
Please enter a guess at an x-intercept.
1
I think a better guess would be: 2.08134124767158
?
If you finish that without any trouble, you can use variables and the command printout to do something like:
? findintercept
For the function shown below:

to function :value
 output :value * :value * :value - 8
end

Enter a guess at an x-intercept.
1

I have used Newton's method to improve this guess to 2.08134124767158.

The value of the function at 1 is -7.
The value of the function at 2.08134124767158 is 1.01633154961057.
?

Global vs. Local Variables

Recall that changing the values of global variables can cause programs to behave in unexpected ways. Because of this phenomenon, we try to use make only to change the value of variables local to a procedure. Otherwise we can't understand how a procedure works without also understanding every helper procedure that might change the values of the variables used in that procedure.

Below is a test procedure that illustrates the difference between using make with a local variable and with a global variable.

to testmake
 local "localvar

 make "localvar [This is a local variable.]
 make "globalvar [This is a global variable.]
end
Assuming you have not already defined a global variable named globalvar, you'll get the following effect when you run testmake:
? print :localvar
localvar has no value
? print :globalvar
globalvar has no value
? testmake
? print :localvar
localvar has no value
? print :globalvar
This is a global variable.
Because we included the instruction local "localvar in procedure testmake, variable localvar is local to procedure testmake. Running procedure testmake doesn't create or affect any global variables named localvar.

On the other hand, we did not tell Logo that globalvar was to be local to procedure testmake. Because of dynamic scope, Logo looked to the parent procedure (command prompt) for a variable named globalvar. Executing the instruction make "globalvar [This is a global variable.] in procedure testmake caused Logo to create a global variable named globalvar and assign it the value [This is a global variable.]

Almost all variables should be local variables. If your procedure uses a variable, say variable varname, that's not one if its input variables you should probably have the line local "varname at the start of your procedure.

Here's some sample code which uses local variables and comments.

Predicates

Chapter 4 is about predicates. A predicate is an operation whose output is always either true or false. In English grammar, a predicate is an expression that can be true of something. The word was then adopted by mathematical logic to describe properties that are either true of something or not; Old Dominion University has a nice web page on predicates that may help explain this. (This old Grammar Rock video might also help.)

With the exception of random and doctor, the Logo procedures we've seen so far have been very predictable. They do the same thing every time. For example, procedure hi from page 5 ended the conversation with "That's nice." no matter what answer you gave to "How are you?"

When the computer asks a question, we'd like our answer to make a difference. Predicates are the tools Logo uses to be able to do different things depending on the circumstances.

For example, emptyp is a predicate which accepts one input and outputs true if the input is either the empty word or the empty list and false otherwise. Here's how we would use emptyp to make hi more interesting:

to hi
print [Hi. What’s your name?]
print sentence [How are you,] word first readlist "?
if emptyp readlist [print [Cat got your tongue?]]
print [Oh well, have a nice day!]
end
Try running procedure hi. Instead of saying "That's nice" it now says "Oh well, have a nice day!" Also, if you hit enter without answering when it asks how you are, it will remark on your silence.

Here is a list of useful predicates: wordp, listp, numberp, emptyp, lessp, greaterp, equalp and memberp. Use help to find out what inputs they need, then try them out! (Remember, predicates are operations, so you'll need to use a command to display their output.) Pages 62-63 of the text have a lot of examples of what you can do with predicates.

? print memberp "a "apple
true
?

Defining Your Own Predicates

The predicates listed above are all very important and computery, but predicates really are just operations that output true or false; we can write our own! Here's a nice example from the book:
to vowelp :letter
 output memberp :letter [a e i o u]
end
This procedure accepts a letter as input and outputs true if the letter is an a, e, i, o or u and false otherwise. Notice how the author used output to directly output the result of the predicate memberp. In the next section we learn another way to write predicates that isn't quite as slick. For now, consider the following predicate:

to nop :input
 output "false
end
Predicate nop accepts one input and outputs false, every single time. This might be useful for testing procedures, but isn't very interesting otherwise.

Conditional Evaluation

Predicates give a true/false answer to a question. How do we get Logo to do something useful with that answer? Look again at the modified hi procedure:
to hi
 print [Hi. What’s your name?]
 print sentence [How are you,] word first readlist "?
 if emptyp readlist [print [Cat got your tongue?]]
 print [Oh well, have a nice day!]
end
In this procedure, the output of predicate emptyp was input to the Logo command if. if is a Logo command that requires two inputs -- a true/false result and a list of instructions. If the first input to if is true, Logo runs the list of commands in the second input. If the first input to if is false, nothing happens.

In procedure hi the two inputs to if are the output of emptyp and the list of instructions [print [Cat got your tongue?]]. In this example, there is only one instruction in the list. For an example of a list with more than one instruction, let's look at another example from Chapter 1; this one is from page 7.

to music.quiz
 print [Who is the greatest musician of all time?]
 if equalp readlist [John Lennon] [print [That’s right!] stop]
 print [No, silly, it’s John Lennon.]
end
Here the first input to if is the output of equalp and the second input to if is the list [print [That’s right!] stop]. This list contains two instructions: print [That's right!] and stop. (The Logo command stop requires no inputs and has the effect of making a procedure stop running. For more inforomation on stop see page 72.)

Try running music.quiz, then try writing a quiz of your own.

Choosing Between Alternatives

Let's think about procedure hi again. It would be nice if Logo responded the same way a human might in this conversation. For instance, it would be nice to have different responses to "great" and "lousy" after we ask "How are you?"
to hi
 print [Hi. What’s your name?]
 print sentence [How are you,] word first readlist "?
 if emptyp readlist [print [Cat got your tongue?]]
 print [Oh well, have a nice day!]
end
We could try using equalp the way we did in the quiz program in stead of emptyp. What happens when you run the following procedure? (Remember, you should either erase "hi or use the editor to define this new version of hi.)
to hi
 print [Hi. What’s your name?]
 print sentence [How are you,] word first readlist "?
 if equalp readlist [lousy] [print [Gosh, I'm sorry to hear that.]]
 print [That's nice!]
end
In the new version of hi, if you tell Logo that you're feeling lousy it respons with:
Gosh, I'm sorry to hear that.
That's nice!
This is not much of an improvement over the version on page 5. What we'd like Logo to do is say "I'm sorry to hear that" in response to "lousy" and "That's nice!" in response to anything else. We could use a stop instruction as we did in the quiz procedure, but there is a better way. (The stop command is tricky to use; some students have reported that it stops not just the procedure you're in but all the procedures that are running.)

ifelse is a Logo command that requires three inputs -- a true/false result, a list of instructions to follow if the first input is true and a list of instructions to follow if the first input is false..

Erase or edit hi again and let's try using ifelse to improve it:

to hi
 print [Hi. What’s your name?]
 print sentence [How are you,] word first readlist "?
 ifelse ~
  equalp readlist [lousy] ~
  [print [Gosh, I'm sorry to hear that.]] ~
  [print [That's nice!]]
end
Notice how we used three tildes (~) to make the procedure easier to read. The instruction ifelse equalp readlist [lousy] [print [Gosh, I'm sorry to hear that.]] [print [That's nice!]] didn't fit all on one line. We also used indentation to indicate that equalp and the two lists of print instructions were inputs to ifelse. Logo doesn't care if you do that; this is just something you can do to make your (and your grader's) life easier.

Defining More Predicates

Recall procedure vowelp:
to vowelp :letter
 output memberp :letter [a e i o u]
end
Having the output of memberp be the input to output is very elegant, but is probably not the easiest thing for a beginning programmer. Now that we know how ifelse works we can write a simpler, less efficient version of vowelp which may be easier for you to understand:
to vowel2p :letter
 ifelse memberp :letter [a e i o u] [output "true] [output "false]
end  
Procedure vowel2p does exactly the same thing as vowelp; if the input is one of a, e, i, o or u, vowel2p outputs true. Otherwise the output is false.

That's the Basics!

You should now know how to write procedures with inputs and outputs, use helper procedures, and choose between two actions in your procedures. That's all you need to know to write most programs; in particular, you can start your midterm project any time you like.

Of course it takes practice to write good procedures. For starters, try adding to or changing procedure hi to make a more interesting conversation; the example below uses the predicate memberp to find out something about the subject of a response.

to hi2
 print [Hi. What’s your name?]
 print sentence [How are you,] word first readlist "?
 ifelse ~
  equalp readlist [lousy] ~
  [print [Gosh, I'm sorry to hear that.]] ~
  [print [That's nice!]]
 print [What are your favorite subjects?]
 ifelse ~
  memberp "math readlist ~
  [print [Math is one of my favorite subjects too!]] ~
  [print [I like a lot of subjects, but math is my favorite.]]
end
Can you predict how hi2 will respond differently from hi3 below?
to hi3
 print [Hi. What’s your name?]
 print sentence [How are you,] word first readlist "?
 ifelse ~
  equalp readlist [lousy] ~
  [print [Gosh, I'm sorry to hear that.]] ~
  [print [That's nice!]
   print [What are your favorite subjects?]
   ifelse ~
    memberp "math readlist ~
    [print [Math is one of my favorite subjects too!]] ~
    [print [I like a lot of subjects, but math is my favorite.]]
  ]
end
(Notice that Logo considers the words math and math, to be different. Teaching a computer to read English is a difficult task!)

If you finish your hi procedure before the end of class, take a look at this old haunted house procedure to see what you can do with just ifelse and readword.

Improving hi2

Our procedure hi2 had the problem that considers the words math and math, to be different. The obvious solution is to check for both, but the obvious solution doesn't work. Run the program defined below. What goes wrong and why?
to hi4
 print [Hi. What’s your name?]
 print sentence [How are you,] word first readlist "?
 ifelse ~
  equalp readlist [lousy] ~
  [print [Gosh, I'm sorry to hear that.]] ~
  [print [That's nice!]]
 print [What are your favorite subjects?]
 ifelse ~
  memberp "math readlist ~
  [print [Math is one of my favorite subjects too!]] ~
  [ifelse ~
     memberp "math, readlist ~
     [print [Math is one of my favorite subjects too!]] ~
     [print [I like a lot of subjects, but math is my favorite.]]
  ]
end
We were warned about this last week and also on page 45 of the text -- when we use the readlist command the second time, Logo waits for us to type a new answer at the keyboard. This is the same problem we had when trying to use readlist to figure out a person's first and last names.

We could store the output of readlist in a local variable and fix our procedure that way, but instead we'll use a functional programming approach (make the output of readlist the input to a procedure, rather than create a local variable) to illustrate a point.

The predicate about.computersp on page 65 of Chapter 4 is a good example to look at:

to about.computersp :sentence
 if memberp "computer :sentence [output "true]
 if memberp "computers :sentence [output "true]
 if memberp "programming :sentence [output "true]
 output "false
end
Can you see how this works? In particular, Why don't I get the output "true true true false" when I run procedure about.computersp with the input shown below:
? print about.computersp [See some computers in the computer programming class.]
true
(If you're not sure, try using the instruction step "about.computersp to see what the predicate does for different inputs.)

? print about.computersp [Programming a computer is fun.]
true
? print about.computersp [Programming is fun.]
true
? print about.computersp [Programming computers is fun.]
true
? print about.computersp [This sentence is not about computers.]
false
Write a predicate includes.mathp that accepts a list (or sentence) as input and outputs true if one of the words in the list is math or math, or mathematics or mathematics, and which otherwise outputs false.

Now we can rewrite our hi procedure as follows:

to hi5
 print [Hi. What’s your name?]
 print sentence [How are you,] word first readlist "?
 ifelse ~
  equalp readlist [lousy] ~
  [print [Gosh, I'm sorry to hear that.]] ~
  [print [That's nice!]]
 print [What are your favorite subjects?]
 ifelse ~
  includes.mathp readlist ~
  [print [Math is one of my favorite subjects too!]] ~
  [print [I like a lot of subjects, but math is my favorite.]]
end
Try it out!