[MUSIC] Let's talk some more about parentheses, and let's particularly
understand that parentheses are not optional in Racket.
They are common, but every time we use them they have a meaning.
So this is a habit you have to break yourself of.
In languages that don't have a syntax like Racket, you're used to just putting
in extra parentheses to help you understand something, and if something
isn't working, maybe you'll just put in more parentheses and then it will start
working. It doesn't work in Racket.
The parentheses always have a meaning. In particular, in most places, if you
write in, something in parentheses that means evaluate e To a result, and then
call it as a function with 0 arguments because the way we do a function call is
parenthesis, thing you want to call, and then argument.
So if there are no arguments, this is call it with 0 arguments.
So, look at this one with 2 parentheses. This says evaluate e, call the result as
a function with 0 arguments. Get back a result and call that as a
function with zero arguments. This makes perfect sense.
We might even do things like this, but if it's not what you mean, you're going to
get an error message. You're going to try to treat something as
a function that is not a function. Now without static typing, this sort of
thing will work just fine when you save your file, when you click run, but once
you actually execute the code, maybe it's inside a function body somewhere, then
you can get an error message. It can be hard to diagnose and your first
reaction is to just start playing with the parentheses,
and it's only going to get you into more trouble.
So let's work through some examples to give you some practice with this.
And the moral of the story is always, slow down, think about your parentheses.
If you're confused why they're there, delete them and start over.
Indent your code well, it'll always work fine.
So what I'm going to do is just show you a bunch of versions of a factorial
function, 1 of my favorite little functions for examples.
And I'm going to start by getting it correct, okay.
So, so far what I've written while I was talking to you is In factors of one
argument function, so these parenthesis have to be here.
Then, I have an if, and I need 3 arguments.
This is the first one. It's a call to the equals function.
This is the second one, which is 1. And then this is the third one, which is
multiplication of n. And then another call.
Always put a parenthesis before the call, a fact, to get the argument to that I
need to call minus with n and 1, close, close, close, close close.
And let's run this, and if I do fact of 5, I get 120 in, and if I do fact of 10 I
think I get about 300,000. Oh, 3, 3.6 million excuse me.
Okay? So, that version was right. Now, let's do a bunch of versions that
are wrong, and try to figure out why. So let me do, I'll call it fact 1.
N if equal n 0, then 1, else n fact 1 of -n1,
close everything, and run it. It all seems to be fine.
I call fact one with five, [COUGH] and I get procedure application expected
procedure given one. No arguments, and that's exactly right.
If you look up here, what I did while I was talking to you and typing is I put
this 1 in parentheses, and as an expression, that means call 1 as a
function with no arguments. There's no type checker to separate our
functions from our integers, but when we went execute this in the base case when 1
was 0 That's when we got the error. So, we had already made about five
recursive calls, and then we got the error.
And we would fix this us by deleting these parentheses.
We can just leave this in here, because As long as we don't call that function,
it won't be a problem. Let me show you a small variant is
fact1b. Suppose I made the same mistake.
Alright, just like this. Alright, so you might think, if you're
not paying very careful attention, that this fact1b is exactly like the first
line, and yet it totally seams to work. In fact, this will work for almost every
argument. The only argument it won't work for is 0.
So what's going on here? well here I, I, I actually made a totally different
mistake, this is wrong. If you call fact1b with a 0 you will try
to apply 1 with 0 argument, but I'm not actually using fact1b recursively here,
I, I wrote fact here, right. I didn't write fact1b I wrote fact, and
so in fact pardon the pun, when I call fact1b with 5 I end up not calling fact1b
again but going up to this first correct version.
And so that's why we didn't get the error unless we called fact1b originally with
0, okay. Good, let's try another 1 how about fact2
excuse me. I need this parenthesis here.
how about if = n 01. Otherwise, n of fact 2.
I will remember, now, to call the correct thing recursively.
[SOUND]. And like that, and click run.
And this is actually a syntax error. So this, it will not allow.
To start. I don't have to test it out to get an
error. Dr.
Racket will detect that, with a good error message here.
If has five parts after the keyword. If is always supposed to have three
parts, and yet clearly, syntactically, it has five,
equal n zero, one, and this multiplication.
And that makes no sense, If has to have three and what happened is I forgot the
parenthesis around the function call to equal, ok? How about if I do it this way?
If, so I'm going to get almost all of this right.
Three terms multiplied by N fact of minus N one.
Just like this, click run, and I get bad syntax multiple expressions after
identifier, indeed I do. So if I say define in a variable, notice
I forgot this parenthesis after the fact3, then it's expecting like a val
binding in ML, right, that I evaluate something here.
But I have 2 things after the variable, this is the first one, this is the second
one, right. I messed up 1 parenthesis, this left
parenthesis is supposed to be over here before the name of the function, not
before the argument. Okay, parenthesis matter, I know I am
typing fast in order to keep the video short but you should think which concerts
am I using. And in this case if you are defining a
function you put a parenthesis before the function name.
You do not put a parenthesis before the argument list.
Look how it up here in the correct version I have this and then I have my
function buddy. Ok, alright lets do another one this one
oh so I need to comment these out these ones so I can get the other ones to go
ahead and click run so I can test them out.
Here is my fourth version of fact how about if equals zero one other wise
Multiply n by my recursive call to fact 4, with -n and 1.
[SOUND]. Click run.
This compiles just fine. Call fact 4 was zero.
I get the right thing, 1. But if I call fact 4 with 5, I get an
error message. And it says that * expects type number as
second argument, given procedure fact4. That's exactly right.
So, it turns out, that multiplication, we saw back in our first or second segment
on Racket, can take any number of arguments, and here I'm passing it three
arguments, n, the function fact4, and n - 1.
Functions are first class, so you can pass them to other functions, but then
the body of multiply says wait a minute, I don't have a number.
I'm going to raise an error. A typechecker in a language like ML would
catch this. In Racket, we have to test our functions
to test this sort of error, then we need to fix up our parentheses.
we need an extra parenthesis before fact4.
And then a after it in order to call multiply with two arguments not three.
OK let's do a couple more here's fact five n if equal n zero one otherwise
multiply n by fact five minus N one. So here, we saw in fact four we had too
few parenthesis. It made us annoyed.
We just wanted to get our homework done so we added some more.
The problem of course is this still works, but we added too many.
And what it says is Procedure Fact five expects one argument given zero.
That's exactly right, right here. I'm calling fact 5 with zero arguments.
Fact 5 which I'm defining recursively right here, expects one argument.
You are not allowed to call functions with the wrong number of arguments, and
we get an error. And then let's do one last one, just
because it's good practice. n times, so this is a very natural thing
to do, because we've all grown accustomed to writing arithmetic a certain way.
And you can probably see the error this time.
I wrote * second. That's supposed to be the function I'm
calling. Instead, what do you think would happen
here? Well, again, the base case is fine, but if n is not 0, then I'm going to try
to treat n as a function. So it expects some sort of error message
where the number I called fact 6 with is trying to be called with some arguments,
alright? When you look at your code carefully you
can sometimes come up with a better error message then you would get from a system
that has no idea what it is your trying to do.
Here I think it will actually do a pretty good job.
So the procedure application, expected procedure given one arguments where
procedure times 1. And, what actually happened is, you would
think this happened when n would be 5. Why, why does it say given 5? Well, it
never got to this call. It went ahead and did the recursion, and
that did the recursion, and that did the recursion, and it's on the way back out
of the recursion that it first detects the error.
When it tries to say, 1 times, and the recursive result, 1.
And if, I'm going to write this out here in Racket, you have 1*1, that's going to
give the exact same error message, because it's going to say that it
expected a procedure in that first position, and you have a 1.