The following content is provided under a Creative
Commons license.
Your support will help MIT OpenCourseware continue to
offer high-quality educational resources for free.
To make a donation, or view additional materials from
hundreds of MIT courses, visit MIT OpenCourseware, at
ocw.mit.edu.
PROFESSOR JIM ERICSON: OK, to work.
A word of warning: fasten your seat belts.
Or, another way of saying it is, I'm going to open up the
fire hose a little bit today.
Last lecture, you might have thought this was a SHASS
class, it's not like a philosophy class, and it was
important to set the stage for what we're going to talk
about, but we talked about very high level things.
The notion of recipes, the notion of computation, why you
want to do this, what you're going to learn.
Today we're going to dive into the nitty-gritty, the nuts and
bolts of the basics of computation, and in
particular, what I'm going to do today is, I'm going to talk
about operators and operands, which we did a little bit real
last time, in particular how to create expressions, I'm
going to talk about statements as the key building blocks for
writing code, and I'm going to introduce simple sets of
programs, in particular I'm going to talk about branching,
conditionals, and iteration.
So, a lot to do.
OK?
So, let me jump straight to it.
At the end of last lecture, we started introducing some of
the pieces you want to do.
And I want to remind you of our goal.
We're trying to describe processes.
We want to have things that deduce new kinds of
information.
So we want to write programs to do that.
If we're going to write programs, we need at least two
things: we need some representation
for fundamental data.
And we saw last time two examples of that.
And the second thing we're going to need, is we're going
to need a way to give instructions to the computer
to manipulate that data.
We need to give it a description of the recipe.
In terms of primitive data, what we saw
were two kinds: Right?
Numbers and strings.
A little later on in the lecture we're going to
introduce a third kind of value, but what we're going to
see throughout the term is, no matter how complex a data
structure we create, and we're going to create a variety of
data structures, fundamentally all of them have their basis,
their atomic level if you like, are going to be some
combinations of numbers, of strings, and the third type,
which are booleans, which I'm going to introduce a little
later on in this lecture.
And that kind of makes sense right?
Numbers are there to do numeric things, strings are
our fundamental way of representing textual
information.
And so we're going to see how to combine those
things as we go along.
Second thing we saw was, we saw that associated with every
primitive value was a type.
And these are kind of obvious, right?
Strings are strings.
For numbers, we had some variations; we had integers,
we had floats.
We'll introduce a few more as we go along.
But those types are important, because they tell us something
about what we want to do when we want to put them together.
OK, but nonetheless, I want to stress we have both a value,
yeah, and a type.
All right.
Once we have them, we want to start making
combinations out of them.
We want to put pieces together.
And for that, we combine things in expressions.
And what we saw as expressions are formed of
operands and operators.
And the simple things we did were the sort of things you'd
expect from numerical things.
Now I want to stress one other nuance here.
Which is, and we're going to do some examples of this,
initially we just typed in expressions into the
interpreter; that is, directly into Python.
And as I suggested last time, the interpreter is actually a
program inside of the machine that is basically following
the rules we're describing here to deduce the value and
print it up.
And if we type directly into the interpreter, it
essentially does an eval and a print.
It evaluates, and it prints.
Most of the time, we're going to be doing expressions inside
of some piece of code, inside of a script, which is the
Python word for program.
In there, I want to make this distinction, this nuance: the
evaluator is still going to be taking those expressions and
using its rules to get a value, but it's not going to
print them back out.
Why?
Because typically, you're doing that to use it somewhere
else in the program.
It's going to be stored away in a variable.
It's going to be stuck in a data structure.
It's going to be used for a side effect.
So, inside of code, or inside of a script, there's no print,
unless we make it explicit.
And that's a little bit down in the weeds, it's a detail,
but one I want to stress.
You need to, if you want something to be printed out
inside your code, you need to tell the machine to do that.
OK.
So let's do some simple examples.
We've already seen somebody's.
I just want to remind you, if I wanted to, for example, type
in an expression like that, notice the syntactical form,
it's an expression, a number, followed by an operand,
followed by another expression.
And of course I get out the value I'd like there.
Yes sir.
Oh, you don't like leaning that far to the left?
OK, if you're a Republican I'll be happy to shift this
over a little bit.
Wow, John, I got a laugh for a political
joke, I'm in big trouble.
That better?
Oh damn, all right, I'll have to do it even more.
OK, here we go, here we go, you see, I'm doing it down
here, I can't see it, does that-- ah, I hear his sighs of
relief, OK, good.
There we go.
Better.
All right.
One of the other things we showed last time is that
operators are overloaded.
And this is where you heard John and I disagree.
I don't happen to like this, but he
thinks it's an ok thing.
In a particular-- if we, whoa, we don't do that, we do this--
that is, give a combination of a number multiplication in a
string, this will in fact give us back a new string with that
many replicas, if you like, of the string
concatenated together.
All right?
And if you want to do other things, for example, we can
take two strings and add-- whoops, sorry-- and add them
together, we will get out, again, a
concatenation of that string.
And these will, we'll let you work through the variations,
but these are the simple expressions we can use.
Now, sometimes things get a little interesting.
All right?
What's the value of that expression?
What do you think should happen if I evaluate that
expression?
Somebody with a hand up, so I can see it.
What's going to happen?
STUDENT: [UNINTELLIGIBLE]
PROFESSOR JIM ERICSON: An error?
Why?
STUDENT: [UNINTELLIGIBLE]
PROFESSOR JIM ERICSON: Great.
OK.
That means, let's check it.
It certainly is.
We bribe people.
So I, ah, by the way, John's a Yankees fan, he throws like
Johnny Damon, I'm a Red Sox fan, so we'll see if I, how
about that?
And I almost hit John along the way, great.
My third right, exactly, what can I say?
All right, so we're into bribing you as we go along
here, and all right?
You'll be badly overweight by the end of the term.
Right, it's a syntactic error, because it doesn't know how to
deal with this.
But there's an important thing going on here, if I in fact
wanted to combine those into a string, I should have told the
machine to do that, and I can do that, by explicitly saying,
take that, which is a number, and convert it into a string,
and then-- bleah, I keep doing that-- then
add it to that string.
OK, so there's an important point here.
We've got what's called type conversion.
That is, if I want to combine two things together in a
particular way, I need to make sure that I give it the kind
of operand it expects.
So STR, which I just typed up there, takes in parens, some
input, and it converts it into a string, so that now I can
use that where I was expecting a string.
John.
PROFESSOR JOHN GUTTAG: You've got a static semantic error in
your syntax.
PROFESSOR JIM ERICSON: Thank you.
And I was going to come to that in a second, but thank
you, John, for pointing it out.
All right.
Why is it a static semantic error?
The syntax is OK in the sense of, it is an operand, an
operator, an operand, so syntactically it's OK.
The semantics was what caused the problem, because the
operator was expecting a particular kind
of structure there.
There's a second thing going on here that I want to
highlight, because it's really important.
Yes indeed.
OK, there we go.
The second thing I want to highlight is, that what's
going on, is that Python is doing some type checking.
It caught the error, because it checked the types of the
operands before it applied things, and it says,
I'm going to stop.
Now, you might have said, gee, why didn't it just assume that
I wanted to in fact treat these as strings, and combine
them together?
Sounds like a reasonable thing to do.
But it's a dangerous thing.
Because in doing that, Python would then have a value that
it could pass on into some other part of a computation,
and if it wasn't what I wanted, I might be a long ways
downstream in the computation before I actually hit some
result that makes no sense.
And tracing back where it came from can be really hard.
So I actually want to have type checking as much
as I can early on.
And in fact, under type checking, different languages
sometimes fall on a spectrum from weak to strong typing.
Which basically says, how much type checking do they do?
Now, you're going to hear John and I go back and forth a lot,
as I said I'm an old time-- well I'm certainly old time,
but I'm also an old time Lisp programmer.
I love Lisp, but Lisp is certainly in the category of a
very weakly typed language.
It does not check the types of its arguments at all.
Python is, I wouldn't say completely strong, but it's
much closer to the strong end of the spectrum.
It's going to do a lot of type checking for you before it
actually passes things back.
Nonetheless, I'm also going to argue that it's probably not
as strongly typed as we might like.
So, for example, there's an expression.
Now, less than is just, if you haven't used it before, it's
just the operator you'd expect, it's comparing two
things and it's going to return either true or false
depending on whether the first argument is less than the
second argument.
What's going to happen here?
Again, I need a hand so I can know where to throw candy.
I've also got on my reading glasses
on, I can't see anything.
Anybody.
TAs don't count, they get their own candy.
When it, yep.
STUDENT: [INAUDIBLE]
PROFESSOR JIM ERICSON: Good question.
Sounds like a reasonable guess, right?
How in the world am I going to compare a string to a number?
So, see how good my aim is, ah, not bad.
All right.
A good quest-- sorry, a good thought, but in
fact, son of a gun.
Or as my younger son would say, fudge knuckle.
Yeah.
All right?
So, what in the world's going on here?
This is a place--
I don't know about you, John, I think this is actually
really not good, because right, what this is doing is,
it's allowing-- sorry, let me back up and say it-- it's got
an overload on the less-than that allows you to compare
basically the lexicographic ordering, or this sequence of
ordering of symbols, including numbers inside of the machine.
And this, in my mind, should have been an error.
Why in the world would you want to compare that?
Just to give you an example of that, for instance, I can do
the following: all right, the number four is less than the
string three, whereas the string four, oops, is not less
than the string three.
And this is a place where it's comparing strings and numbers
in a strange way.
So why am I showing you this?
Partly to show you that it's kind of weird, but also to
tell you that one of the things you want to do is
exercise what I'm going to call some type discipline.
Meaning, when you write code, you want to get into the habit
of A, checking out operators or procedures to see what they
do under different circumstances, either check
them out or read the specifications of it, and two,
when you write your own code, you want to be disciplined
about what types of arguments or operands
you apply to operators.
Because this is something that could certainly have screwed
you up if you didn't realize it did it, and you need to
have that discipline to make sure it's
doing the right thing.
OK.
One of the other things you're going to see is that some of
the operators have odd meanings.
And again, I think we looked--
Yup?
STUDENT: So, the string A is less than three, is false
because they're comparing like ASCII values?
PROFESSOR JIM ERICSON: Yes.
I mean, I'm sorry.
The answer is, I don't know if it's ASCII.
John, do you know, are they doing ASCII
encoding inside of here?
I'm assuming so.
Right.
So, in case you didn't understand what the the
question was, basically every symbol gets translated into a
particular encoding, a string of bit, if you like, inside
the machine, there's a particular one called ASCII,
which is, if you like, an ordering of that, and that's
what the machine's actually comparing inside of here,
which is why in under ASCII encoding the numbers are going
to appear after the characters, and you get the
strange kind of thing going on.
All right.
I want a couple of other things, just to quickly remind
you, and one of them is, remember, the operators do
look at the types, so division, for example nine
divided by five is one, because this is integer
division, that is, it's the largest number of integer
multiples of five to go into nine, and there would be a
remainder associated with it, which is in fact four.
And again, you've got to be careful about
how you use the operators.
Right, having done that, we can certainly get to more
complicated things, so for example, suppose I look at
that expression.
Three plus four times five.
All right.
Now.
There are two possible values here, I think.
One is 23, the other's 35.
Because this could be three plus four, times five, or it
could be three, plus four times five.
And of course, you know, when you look at code it doesn't
pause in between them.
But what I do?
I just separated, do I do the addition first or do the
multiplication first?
Anybody know what happens In this case?
Yeah, way up, oh God I'm going to have a hell of time
throwing up there, way up at the back.
STUDENT: Standard order of operations, I guess take the
multiplication first, and add the three.
PROFESSOR JIM ERICSON: Right.
I'm going to try, if I don't make it, you know, just get
somebody to pass back, whoa!
I just hit somebody in the head.
Thank you.
Please pass it back to that guy.
If you want candy, sit much closer down, and that way we
can film you as well as we go along.
Right.
So the point is, there is something here called operator
precedence, which is what the gentleman said.
I'm not going to say much more about it, but basically what
it says is, with all other things being equal, things
like exponentiation are done before you do multiplication
or division, which are done before you do things like
addition and subtraction.
And so, in fact, if I wanted the other version of it, in
fact, if I do this right, it's going to give me 23 because it
does the multiplication first, if I wanted the other version
of it, I need to tell it that, by using, excuse me,
parentheses.
And in general, what I would say is, when
in doubt, use parens.
OK.
Now, that just gives us expressions.
We can start having complex expressions, you can imagine
we can have things are lots of parens, and all sorts of
things in it.
Yes, question.
STUDENT: What does it mean, the operator used, when you
were calculating the remainder between nine and five?
PROFESSOR JIM ERICSON: It's the percent sign.
If you can't read it, I guess I'm going to have to blow that
font up, aren't I, next time around.
Yeah, it's a percent, so this percent sign will
give you the remainder.
OK.
Second thing I need to do, though, is I need to, when I
get those values, I want to hang on to them.
I'd like to give them a name, so I can refer to them in
other places.
And so we saw that as well, the last piece we had here is
the ability to create variables, which have their
own values, and that's done using an assignment statement.
So in particular, that is an assignment statement.
It says, take the name x and create a binding for that name
to the value of the sub-expression and in fact to
do this, to stress a point, let's do that.
It's not just a number, it's any expression.
What Python will do, is it will evaluate that expression
using the kinds of rules we talked about, and then it
creates a binding for x to that value.
And I want to stress this, we're going to come back to it
later on in the term, so the way I'd like you to think
about it for now, is that somewhere in the machine,
there's a big space that contains all possible values.
Right.
It's a slight lie, it doesn't have all possible values, but
you get the idea.
It has, if you like, intellectually,
all possible values.
And when I create a binding, I'm taking a variable name, in
this case x, stored somewhere in a table, and I'm creating a
link or a pointer from that name to that value.
This is a nuance.
It's going to make a lot more sense later on when we
introduce mutation into our language, but I want you to
start thinking of it that way.
Don't think of it as a specific box into which we're
putting things; think of it as a link to a value.
I could have, for example, another assignment statement,
and that creates a binding from y into that same value,
and one of the things as a conservist I can do is, I
could have a statement like, let z be bound to
the value of x.
And I said it deliberately that way.
That statement says, get the value of x, which is this
link, and give z a pointer to the same place.
To the value, not to x.
OK, and we'll just plant that idea, we're going to come back
to later on, as we carry on.
OK.
So if we have variables, one of the questions we can ask
is, what's the type of the variable.
And the answer is, it inherits it from its value.
OK.
Yes.
So if somewhere in my code, I have that statement, that
assignment statement, x now is a variable
whose value is an integer.
Unfortunately, at least in my mind, in Python, these
variable bindings are dynamic, or the
type, rather, is dynamic.
Meaning, it changes depending on what the current value is.
Or said a different way, if somewhere later on in the
program I do this, x now has changed its
type from INT to string.
Now why should you care?
OK, my view is, I don't like it.
Especially in the presence of operator overload.
Because I might have written some code in which I'm
expecting that particular variable to
have an integer value.
If somewhere later on in the code it shifts to string, I
might not be manipulating that and getting actual values out,
but not what I wanted, and it's going to be really hard
for me to chase it back.
So one of the things I would like to suggest is that you
develop some good style here, and in particular, don't
change types arbitrarily.
I can't spell today.
Meaning, sometimes you need to do this, but in general
there's-- at least in my view and I don't, John, would you
agree?-- you just don't want to do this.
You don't want to make those changes.
It just leads to trouble down the road.
OK.
Now, last thing about variables, and then we're
going to start pushing on this, is
where can you use them?
And the answer is, you can use a variable anywhere you can
use the value.
So, any place it's legal to use the value.
OK.
Now.
This is just sort of bringing us back up to speed and adding
a few more details in.
What we really want to do now though is
start using this stuff.
So, operands.
Let us take expressions, get values out, we can store them
away in places, but ultimately we want to do something with
them, so we need to now start talking about what are the
things we can do inside of Python, or any programming
language, to manipulate them.
And for that, we're going to have statements.
Statements are basically, if you want to think about it,
legal, and I was about to use the word expression except
I've misused that elsewhere, so legal commands that Python
can interpret.
You've already seen a couple of them.
Print, assignment, certainly two obvious statements,
they're commands to do something.
Assignment is binding a name to a value, print is saying
put it back out in the screen.
Obviously if you have print as a way of putting things out,
we expect to have ways of getting input in, we're going
to see an example that in the second.
And as we go through the next few lectures, we're going to
add in more and more of these statements.
But let's look at what we could do with this, OK?
And to do this, I'm going to use some code that I've
already typed in.
So I'm hoping you can read that, and it's
also in your handout.
This is a little file I created, all right, and I'm
going to start with a sequence of these things and walk them
along, again I invite you to put comments on that handout
so that you can follow what we're going to do.
All right?
So let's look at the first part of this.
Right now, this is just a text file.
OK.
And I've highlighted in blue up there one of the pieces I'm
going to start with.
And what do I have?
I have a sequence of commands; I've got an assignment
statement, I've got another assignment statement, I've got
a print statement, I've got an input statement, which we'll
come back to in a second.
And I want to basically try and use these things to do
something with them.
Second thing I want to note is, the little hash mark or
the pound sign, that's identifying a comment.
So what's a comment?
It's words to you, or to the reader of the code, that are
telling you what's going on inside of this code.
OK?
Now, these comments, frankly, are brain-damaged, or
computationally challenged if you prefer.
Meaning, why in the world do I have to tell the reader that
I'm binding x to the value three?
All right?
I'm putting them in there to make a point.
In general, good programming style says you put in comments
that are going to be valuable in helping you as a reader
understand what's going on inside of the code.
It could be, what's the intuition behind
this piece of code.
It could be, preconditions I want to have on input.
It could be, explanations of specific things you're doing.
But you need to have those comments there.
Now, this becomes a little bit of one of those motherhood and
apple pie kinds of lectures.
You know, your mother always told you to eat brussels
sprouts because it was good for you.
Well this is a brussels sprouts comment. everybody
goes yeah, yeah, yeah, comments, of course.
Of course we're going to do comments.
And they never do.
So my challenge to you, and I know Professor Guttag can do
this, my challenge to you is, a year from now, come back and
look at code you wrote here.
Can you still understand what it was you were trying to do?
I don't know, John, if you'd agree, right?
If you can read the code a year later, even code you
wrote yourself, it's a good sign that you put good
comments in, right?
Second good piece of style here is
choice of variable names.
These are lousy.
Deliberately.
OK?
I'm just using simple things like x and y and z because I
want to make it, just get through the
lecture if you like.
But in general, the choice of variable name is a great way
of commenting your code.
Use variable names that make sense.
That little problem set zero that you did.
You read in a couple of values, you
probably stored them away.
My bet is, you used simple names like x and y.
A much better name would have been firstname, lastname, as
the name of the variable to tell you what you were trying
to capture there.
OK.
The other piece I want to say about variable names is, once
I have that choice of variable name, I can use it, but in
fact there are a few things that I can't use in terms of
variable names.
So, these are an important way of documenting, but there're
some things excluded.
And in particular, there are some key words that Python is
going to use that have to be excluded.
Let me highlight that.
As I said, right now that's just text file.
I'm going to save this away-- yeah, not that way, I'm going
to save this away-- with the subscript, or the suffix
rather, py to make it a Python file.
Yeah, and I know it's already there but I'm going to do it,
and I get some wonderful colors.
But these are important, OK?
So notice what I have up there now.
Comments appear in red.
I can see those.
There's a keyword, which I'm going to highlight right up
here, print, which is in, I don't know what
that color is, orange?
There's a function in purple, there's a string in green, and
in black I have the assignment statements.
That print is a keyword.
It's a command to Python to do something.
As a consequence, I can't use it as a variable name.
All right, think about it for a second.
If I wanted to use print as a variable name, how do I get
the system to decide gee, do I want print as a value for
something, or do I want print as a command?
So there's a sequence of these that are blocked out, and I--
John, I think there are what, twenty-eight?
Something like that, TAs, is that right?
Twenty-eight keywords that are blocked?
We'll find them as we go along--
OK.
Having done this now, I can simply go ahead and run this,
and in fact if I go up here to run, you'll see I've got both
an option to check the module, though in this case I'm just
going to run it.
Oh, notice what happened.
It ran through that sequence of instructions, in particular
it bound x to the value three, and then it took x times x,
got the value of x multipied by x, which of course is nine,
bound that to the value of x, and then it printed out the
value, and now it's sitting here waiting for an input.
You notice what it did, it printed out that little, right
up here I'd said enter a number and that's what it's
printed out, so I can enter a number and it prints it out.
Great.
Let's run it again.
Actually for that, I can just use, if I'm lucky, function
F5, which didn't work, so let me try it again., here we go.
We're going to run that module.
OK.
Whoa.
What happened?
I said enter a number.
I didn't.
I gave it a string.
And it still took it.
And printed it up.
Well, this is one of the places where I want to come
back to that highlighting of what do things do?
Even though my statement said enter a number, in particular,
raw input here simply takes in a set of characters and treats
it as a string.
And then prints it back out.
So if in fact I wanted to make sure this was a number, I
should have done something like either try and convert it
to a number, which of course failed here, or put in a check
to say where it is.
So it's a way of reminding you, I've got to be careful
about the types of things that I put in.
OK.
This is still boring, so let's step on the accelerator.
What I have now is the following: I can write
expressions, do combinations of things to get out values, I
can store them away, I can print them up.
But literally all I can do at this stage is write what we
would call a straight-line program, that is, a program in
which we execute in which we execute the sequence of
instructions one by one.
Simply walk down that list. That's what we
just did there, right?
We just walked through that list. This is boring.
In fact, you can do some nice things to prove what is the
class of functions you can compute with straight-line
programs, and what you'd see if you did that is, it's not
particularly interesting.
OK.
Let's go back and think about our recipes.
What we use as our motivation here.
Even in real recipes, you have things like,
if needed, add sugar.
That's a decision.
That's a conditional.
That's a branch.
That says, if something is true, do something.
Otherwise, do something different.
So to really add to this, we need to have branching
programs. What I mean by that is, a branching program is
something that can change the order of instructions based on
some test. And that test is usually a value of a variable.
OK.
And these get a whole lot more interesting.
So let's look at a little example, and this is going to,
excuse me, both allow us introduce the syntax as well
as what we want to have as the flow of
control inside of here.
So let me go back up here, and I'm going to comment out that
region, and let's uncomment this region.
I want to write a little piece of code.
It's going to print out even or odd, depending on whether
the value I put in, which is x in this case, is even or odd.
Think about that.
That says, if this thing has some particular value, I want
to do one thing; otherwise, I want to
do something different.
And let's look at the syntax of this.
This is the first of the conditionals that
we're going to see.
Notice the format.
I'm going to go up there.
The first statement right here, that's just an
assignment statement, I'm giving some value to x.
We could make it something different.
And then, notice the structure here.
The next three statements.
First of all, IF is a keyword. which makes sense.
It is followed, as you can see there, by some expression,
followed by a colon.
And in fact, that colon is important, so let me stress
this over here.
The colon is important It's defining the beginning of a
block of instructions.
Yes sir.
STUDENT: [UNINTELLIGIBLE]
PROFESSOR JIM ERICSON: Based on a test. Usually the value
of a variable.
OK, so let me go back to where I am.
I'm looking at that piece of code.
What that colon is saying is, I'm about to begin a sequence
of instructions that I want to treat as a block.
So it identifies a block of instructions.
It's, and in particular, the colon is the start, and the
carriage return is the end.
Now what in the world does that mean?
I'm doing a lot of words here, let me try and say this a
little bit better.
That code says the following: the IF says, I've got an
expression, I'm going to evaluate it.
If that value is true, I want to do a set of things.
And that set of things is identified by the sequence of
commands that are indented in, this one right here, following
the colon but before I get back to the same place in
terms of the indentation.
If that test is not true, I want to skip this instruction,
and there's a second keyword else, followed by a colon, and
that tells me the thing I want to do in the
case that it's false.
So in fact if I run this, ah, and save it, and
it prints out odd.
So, what happened here?
Well, let's look at the code.
Right? x is initially bound to fifteen.
I get to the IF.
The IF says, evaluate that next expression.
In that next expression, I'm actually taking advantage of
the fact that I'm doing integer multiplication and
division here.
Right, that divide is, if x is an integer and two is an
integer, what's it going to do?
If x was even, x divided by two is going to be actually
the half of x, right?
If x is odd, that integer division is going to give me
the number of multiples of two, that go into x, plus a
remainder, which I'm going to throw away.
In either case, I take that value and multiply back by
two, if it was even I get back the original number, if it was
odd, I'm not going to get back the original number, so I can
just check to see if they're the same.
OK, so a little nuance that I'm using there.
So, the first thing that IF does, bleah that IF says is,
evaluate that expression and if it's true, do the next
thing, the thing after the colon.
In this case it's not true, so it's going to skip down and
evaluate the thing printed up the odd.
OK.
What-- yes.
STUDENT: [INAUDIBLE]
PROFESSOR JIM ERICSON: Thank you.
I was hoping somebody would ask that question.
The question was, if you didn't hear, why do I have two
equal signs?
It's like I'm doing this, right?
Anybody have a se--%uFFFD why don't I just
use an equal sign?
I want to know if something's equal to something.
Yeah.
STUDENT: [INAUDIBLE]
PROFESSOR JIM ERICSON: Absolutely.
The equal sign is going to bind--
Nice catch.
John, this is so much fun, throwing candy.
I've got to say, we've got to do this more often--
Right.
Let me, let me get to the point.
What does an equal sign do?
It is an assignment.
It says, take this thing on the left and use it as a name
to bind to the value on the right.
It's not what I want here.
Having already chosen to use equal as an assignment, I need
something else to do comparison.
And that's why I use double equals.
Those two equal signs are saying, is this thing equal
to, in value, the thing on the other side?
OK.
Now, having done that, again I want to stress this idea and
I'm going to write it out one more time, that there's a
particular format here.
So we have if, and that is followed by, I'm going to use
angle braces here just to indicates something goes in
here, some test followed by a colon.
That is followed by a block of instructions.
And we have an ELSE, followed by a colon in some other block
of instructions.
And I want you to get used to this, that colon is important,
it identifies the start, and then the set of indented
things identify all the things at the same level, and when we
reset back to the previous level, that's when we go back
to where we were.
OK.
Now, that's a nice simple little test, let's look at a
couple of other examples to get a sense of
what this will do.
OK, let me comment this out, and let's look at this next
little piece of code.
All right.
I'm binding a z to be some value, and then I'm
going to run this.
Well, let's just run it and see what it does.
Nothing.
OK, so why?
Well, let's look at it.
I'm doing a test there to say, if the string x is less than
the value of b, and x does not appear before b as strings,
then I was going to do, oh, a couple of things, because
they're at the same block level.
Given that that wasn't true, it did nothing.
Now, wait a minute, you say, where's the ELSE clause?
And the answer is, I don't need one.
All right, if this is purely a test of, if this is true do
this otherwise I don't care, I don't need the ELSE clause in
there to identify it.
All right?
Second thing I want to look at is, suppose I compare that the
one below it.
Oops, that I don't want to do.
Comment that out, and let's uncomment this.
Yeah, I've still got a binding for z and I'm giving it the
same test, but notice now I've got the two same commands but
they have different indentation.
In this case, in fact I do get a different behavior.
Why?
Because that block identifies a set of things that I'm going
to do if the test is true.
If the test was not true, notice that that last command
for print Mon is now back at the same level as the IF, so
what this says is the IF does the test, having done the
test, it decides I'm not going to do anything in the block
below it, I'm going to skip down therefore to the next
instruction at the same level as the IF, which gets me to
the second print statement.
OK.
So now we're seeing some of these variations, let's see
what else can we do here.
So let me just to try something a little more
interesting, and then we'll get to writing some simple
programs. So I'm going to comment those out, and let's
go down to this piece of code, and uncomment it.
Ooh yes, that was brilliant.
Let's try this again.
And uncomment that, and uncomment it again.
Right, so here's a little piece of code that's going to
print out the smallest value of three.
And notice what this is showing is that
the IFs can be nested.
All right, it's so if I looked at it, it's going to say that
IF x is y-- sorry, IF x is less than y, THEN check to see
IF x is less than z, and if that's true, print out x is
the smallest. And notice the structure of it, if it's not
true I'm going to go to that next ELSE, and print out that
z is smallest. If the first test wasn't true, I'm going to
skip that whole block and just go down and print out that y
was smallest. So notice the nesting, I can flow my way
through how those tests are actually going to take place.
All right, so let's run this and see what happens.
Great. y is smallest. OK.
Is that code correct?
Is that a tentative hand back there?
Yeah.
STUDENT: Let me compare y to [INAUDIBLE]
PROFESSOR JIM ERICSON: Yeah, it's not doing all of the
comparisons.
All right, and let's just check this out, because I want
to make a point of this, let's go back and do the following.
Let's take y, change it to thirteen, let's run it, hmm.
So what did I miss here?
Two important points.
First one, when I write a piece of code, especially code
that has branches in it, when I design test cases for that
piece of code, I should try and have a specific test case
for each possible path through the code.
And by just doing that, I just spotted, there's a bug here.
And the bug was in my thinking, I did not look for
all of the tests.
So the way I can fix that, is, let me comment that out, and
keep doing that, comment that out, let's uncomment this,
notice the structure here.
I now have multiple tests.
So actually, let's just run it and then we'll talk
about what it does.
I run this, yeah, I have a syntax error, yes indeed,
because I forgot to comment that one out, all right, and
cue, we'll try it again.
Ah-ha!
And let's quickly look at the structure of this.
This now has, gee, a funny thing, it says IF x is less
than y AND x is less than z, then do something.
And then it has a strange thing called ELIF, which is
simply short for else/if in a second test. So the way to
think about this in terms of flow is, it starts with that
if and it says, check both of those things.
And that fact that both of those things is the fact that
we're using Boolean combination here.
It is to say, we can take any logical expressions, combine
them together with AND, OR, or NOT to make a complex
expression, and use the value of that expression as my test.
And that's literally what I've done there, right, I've got x
less than y, that's a test, it returns a Boolean, which by
the way is the other type, at least I would include here, it
has only two values, which are true and false, and what that
code says, if x is less than y, AND, logically, whatever I
put up there, x is less than z, then the combination is
true, and therefore I'm going to do something.
So AND is if both arguments are true it's true, OR is if
either argument it's true it's true, NOT is if the argument
is not true it's true, and then the last piece, as I said
is, I can now have a sequence of things I want to do.
So if this is true do something else, otherwise test
and see if this is true, do something else, as many as I
like followed by the end.
And ELSE, it says, here's what I want to do.
OK.
Now.
Having added this in, I have branching instructions.
I have simple branching programs. These are still
awfully simple.
OK?
And they're awfully simple because, all I can do now, is
decide whether to execute some piece of code or another.
Said a different way, in the case of the straight-line
programs, how long would it take to run a program?
Well, basically, however many instructions I have, because
I've got to do each one in order.
With simple branching, how long is it going to take to
run a piece of code?
Well at most, I'm going to execute each instruction once.
All right?
Because the IFs are saying, if it's true do this, otherwise
skip on it.
Therefore, for simple branching programs, the length
of time, the complexity the code, is what
we would call constant.
That is, it's at most the length of the actual number of
instructions.
It doesn't depend on the input.
Real simple programs.
Let's take another simple example.
Suppose I want to compute the average age
of all the MIT faculty.
There's about a thousand of us.
However I do that, I know that should inherently take more
time than it takes to compute the average age of all the
EECS faculty.
There's only 125 of us.
And that should take more time than what it takes to compute
the average of John's and my ages, instructors in 600,
because there's only two of us.
All right, those pieces of code inherently involved
something that does depend on the size of the input, or on
the particular input.
And that is a preface to an idea of computational
complexity we're going to come back to.
One of the things we want to help you do is identify the
different classes of algorithms, what their costs
are, and how you can map problems into the most
efficient class to do the computation.
OK.
Now.
Think for a second about computing the average age of
the faculty.
You can already kind of see what I want to do.
I, somehow if, I want to walk through some sequence of data
structures, gathering up or doing the same thing, adding
ages in until I get a total age and then divide by the
number faculty.
How do I write a piece of code for that?
Well, let's go back up to our original
starting point of recipes.
And I'm sure you don't remember, but one of the
things I had in my recipe, is beat egg whites until stiff.
OK.
That until is an important word.
It's actually defining a test. Let me rephrase it into
garbled English that'll lead more naturally into what I
want to do.
While the egg whites are not stiff, beat them.
That is a different kind of structure.
It has a test in it, which is that while, while something is
true, do something, but I want to keep doing it.
And so for that, we need to add one last thing.
Which is iteration.
Or loops.
We're going to see variations of this, we're going to see a
variation of it called recursion, a little later on,
but for now we're just going to talk about how do we do
iterations.
And I want to show you an example of this, to lead to
both the syntax, and to the semantics.
And let me comment that out, and let's go to this one.
All right.
What does this piece of code say?
Not, what does it do, but what does it say.
Well, the first three statements are just assignment
statements.
I'm binding x, y, and iters left to some values.
And then notice the structure, I got a keyword WHILE, there's
that color identifying it, and in parentheses I have a test.
I'm expecting the value that test to be a Boolean.
Followed by a colon.
The colon's identifying a block of code.
And what this is saying is, gee.
Check to see if the variable iters left has a value
greater than zero.
If it does, then execute each of the
instructions in that block.
So I'm going to have an assignment of y, I'm going to
have an assignment of iters left, I've got a comment that
I had borrowed in order to do some debugging, and then what
do I want it to do?
I want it to go back around to the test. Once again, say, is
that test true?
If it is true, execute the sequence of instructions.
So in fact we can block this out and see what it does.
If I make a little chart here, I've got x, y, and iters left.
x starts off as I think I set it up as, here I can't even
read it, is x is three, y is zero, iters left is three.
I can hand-simulate it.
It says, is the value of iters left greater than zero?
Yes it is.
So, execute those two instructions.
It says, take value of y and value of x, add them together,
and create that as the new value of y.
All right.
That's the assigned statement.
It says, take iters left, subtract one from it, and bind
that as the new value of iters left.
Having reached the end of the block, go back up and check
the test. Is iters left greater than zero?
Yes it is.
So, evaluate the same sequence of instructions again. y plus
x is six, that's my new value of y, two minus one is one,
that's my new value of iters left, go back up.
Is iters left greater than zero?
Yes it is.
So once more, thank God I didn't take 47 as an example,
x plus y, subtract one from iters left, go back up to the
test. Is iters left's value greater than zero?
No, it is not.
Therefore, skip the rest of that block of code and go to
the next instruction, which is, ah, print out y.
In fact, if we test this, son of a gun.
Got a simple square procedure,.
Right, It's just squaring an integer, is what it's doing.
But notice the structure.
Now I have the ability to create a loop, that is, to
reuse the same pieces of code over and over
again as I go around.
And this adds, now, a lot of power to the kinds
of code I can write.
Notice some other things I want to highlight on this.
All right?
The first one is, that test has to involve-- shouldn't
have to, but almost always is going to involve-- the value
of some variable.
What if I don't change the value of that variable inside
of the code?
Or, another way of saying it is, what if I did this?
Comment it up.
What happens if I run this sucker?
STUDENT: [INAUDIBLE]
PROFESSOR JIM ERICSON: Yeah.
It'll go forever.
Absolutely, right?
It's going to loop into an infinite loop--
I think I can hit this close, ah, no I can't, boy what a
terrible aim--
All right, what has [UNINTELLIGIBLE PHRASE] try
again, the point I'm trying to make here-- thank God we're at
the end of this lecture, my tongue is falling apart-- the
point I'm trying to make is, that test needs to involve
some loop variable that is changing.
Otherwise the test is always going to be true, we're going
to go off here, but this would loop forever if I did that.
All right.
Second question: or maybe a better way of saying this, and
the general format you're likely to see here is, a test
involving a variable name, which must be initialized
outside of the loop, and which interior to the loop gets
changed, so that the test is going to change.
Here's the second question.
What value of inputs, what values of x will this run
correctly for?
Probably should be integers, right?
Otherwise, this is going to be doing something strange, but
all integers?
All right, suppose I do this.
It's my last example.
Yeah, how about that, right?
We knew this was trying to do squaring, so intellectually we
know we can square -4, it ought to be 16, but what
happens here?
Double fudge knuckle.
All right?
It's going to run through the loop, accumulating the
answers, but because I'm subtracting, it's just going
to keep making x more and more negative as it goes along,
again it's off into an infinite loop.
Which is a way of reminding you that I need to think as I
write the code about what are my expectations from the
input, and how might I enforce them.
In this case, I probably want to make sure I use absolute
value of x before I carry it on.
Yes ma'am.
STUDENT: [UNINTELLIGIBLE]
PROFESSOR JIM ERICSON: You're absolutely right, because I
bind iters left to, um, yeah.
Yes.
Thank you.
Boy, two candy for you.
You caught me making an error.
Yes.
The point is, it's not going to work, and she caught both
of them, impressive, it's not going to work because iters
left is already negative, it's just going to skip the whole
loop, and I'm in trouble, so thank you for catching that.
All right.
I was going to do one more example, but I've run you up
to the end of the time.
I'll leave the example on the handout, it shows you another
version that we'll come back to next time.
The key thing to notice is, I now have the ability to create
iterations, which extends well I can do.
And we'll see you next time.