 Now that we've finished learning Scheme, every week a mind-blowing new idea. This week's mind-blowing new idea is function is data. So this is your brain. And unless you're one of the handful of born mathematicians, in your brain, there's this big brick wall between the notion of stuff and the notion of actions. So that's why in our languages, every human language has nouns and verbs as different categories. We talk about knowing that such-and-such versus know-how to do such-and-such and in computer programming, there's this big distinction between data and procedures as two different kinds of things. Data is stuff like numbers or character strings or whatever and procedures, you know what procedures are. So our job throughout the course of the semester is to try to break down this wall in your heads. But it really is going to be hard. It's something you're going to have to stretch your minds around. This wall, for example, to take a non-computing example, this is the reason freshman calculus is notoriously difficult even for people, some people who did well in math in high school. There's this sort of big, you know, hitting the wall about calculus. And calculus students think it's because of details about, you know, integration by parts or something, but it's not. It's because the integral itself, as a thing, is a function of functions. So the integral doesn't take some number and give you out a different number. It takes a function like f of x equals 3x squared and gives you a different function like f prime of x equals 6x. Right? And it's wrapping your mind around this idea that you can have a function whose argument is another function that makes calculus hard. It's also what makes this week's topic hard, functions as data in the computer programming context. So we're going to sort of inch up on it by starting with a simpler idea, which is generalizing patterns. And remember, as we do this, I'm going to show you a very, very simple example of it. So keep in the back of your mind. The reason is we're building up to procedures as data. So up on the top half of the screen, after we define pi, there are four procedures that compute the areas of various geometric figures. So if we have a square of side r, its area is r squared. If we have a circle of radius r, its area is pi r squared. If we have a sphere of radius r, its surface area is 4 pi r squared. If we have a regular hexagon of radius or side, because they're the same thing in hexagon r, its area is 3 root 3 over 2 r squared. Okay, one and a half root 3 r squared. So we can use these procedures. Whoops, stop doing that. So I can say, you know, circle area 5, and I get 25 pi, which is a little more than 25 times 3. But I don't like the idea of having a separate procedure for each of those. I would like, and I notice, that there's a pattern in here. What's the pattern of those four area procedures? Yeah, it's all some number times r squared, right? Because area is two-dimensional, r is one dimension, so okay. So here's how I'm going to do that. My original procedures took one argument, which was the linear dimension of the thing. My new procedure takes two arguments. I've added an argument that's going to embody what's different from one of these procedures to another. So what's the same is for every shape, it's some constant times r squared, and what's different from one figure to another is what the constant is. So for a square, the constant is one. It's just r squared. For a circle, it's pi r squared. For a sphere, it's 4 pi r squared and so on. Okay, so now I can say area circle 5 and get the same answer, and the same will work for any of these other figures. Okay, so I want to make sure you get how this is a generalization that the original area procedures, each one took one argument for the linear dimension. The generalized procedure still has that argument for the linear dimension, but now it has another argument that says what's different from one pattern to another. Okay, any questions about this so far? Great. Okay. What makes this a really simple example is that I was able to capture the differences in just a number, right, the constant factor. So I can define circle to just be pi, but sometimes the pattern is a little more complicated. So here I have, this is an example taken from the book. I have a procedure that computes the sum of the squares of the numbers from A to B. So if I say sum square 3 to 5, I should get 9 plus 16 plus 25, which is 50. Okay, so it's clear how that works. It's a squared plus a plus 1 squared plus a plus 2. squared dot dot dot up to B squared. So how does this procedure work? It says, well, if there's going to be a recursion, so we have to have a base case. The base case is if A is greater than B, then we're not adding any terms at all. So therefore the answer is zero. If A is less than or equal to B, then there's at least one term to add, namely A. So we're going to add A squared, which is this, to a recursive call going from A plus 1 to B. Okay, similarly we have some cube. If I say some cube from 3 to 5, I get some number that's much too hard for me to compute in my head, namely 216. And that's 3 cubed plus 4 cubed plus 5 cubed. Okay? Now, what I want you to see about those two procedures is that they're almost the same. Namely, they both say if A is greater than B, return zero, otherwise add some function of A to a recursive call for A plus 1 to B. So the only thing that's different here is that in this case, it's the square of A and in this case, it's the cube of A. Okay? So I'd like to generalize this pattern. But the way, excuse me, the way I have to represent what's different between these two versions is no longer capturable as just a number. Okay, these are two different functions. So I'm still going to have an extra argument for what's different. But this time, instead of a number, the argument is going to have to be a function. Okay, so I'm going to say to find sum, just plain old sum, of some function. I'm putting it in capital letters just so you can see what's different here and A and B. So one new argument function, and then it's going to say if oops, greater than A, B. Don't ask me why that. Then return zero. Otherwise add Now I have to take that function and apply it to A. Right? Well, that's how we do that. To call a function, you put it in parentheses along with its arguments. It's a function of A and then sum of function A plus 1 and B. Close, close, close, close. Okay? Yes. Shouldn't the A be a lower case A? This is a sensible programming language. It doesn't pay attention to case of letters. Sadly, this is no longer true if you look at the latest scheme standard. It got hijacked by Java fans, and so they're making identifiers case sensitive. But if you have three things in your program called foo, and one of them is lowercase foo, and one of them is capital F lowercase oo, and the third one is capital F capital O capital O. You're asking for trouble. So sensible programming languages, that doesn't matter. Okay? It's a bad habit. Get out of it. They taught you in high school. Sorry, I'm not making fun of you or making fun of your high school teacher. Okay, and it isn't even your high school teacher's fault really. I'm making fun of the college board who has the AP curriculum in Java instead of scheme. Okay, other questions? Okay, so now I'd like to use this. So I say sum x squared from three to five. Is this going to work? No, it's not going to work. Why not? It says unbound variable x. When I said times xx here, remember scheme uses applicative order. That means before it calls sum, it's going to try to multiply x by x. But I don't have an x. It wouldn't help if I said a, because there isn't one of those either. Okay, so we have to find a way to give it a function as an argument rather than a number. So right now I'm going to do that this way. I'm going to define a function square. And now I'm going to say sum square from three to five. And I get the answer I wanted. Okay? Please note that there is no open parenthesis before this word square. You all know, right, that the singular parenthesis is not parenthesis. It's parenthesis, S-I-S. So there's no open parenthesis before the word square. That's because I'm not invoking the function square. I'm just taking square as a thing and passing it as an argument to sum. So you see how we're breaking down this wall? Because square started out life over here as an action. And now I'm using it as a thing, as data for this other procedure sum. Okay? Yes. Won't scheme try to evaluate square? Yes, it will. It will evaluate square. The value of square is this ugly thing, which is how STK prints out procedures. So it's perfectly okay to evaluate square. But I'm evaluating it is not the same thing as invoking it. Okay? What I'm evaluating is the the word S-Q-U-A-R-E. The value of that word is a function, a procedure. Okay? If I say parenthesis square such and such, then I'm invoking that procedure. I'm actually running it with arguments. Okay? Good question. Other questions? Okay, moving on. The version in the book, by the way, just backing up to the definition of sum here, they add another complexity, which is to allow you to use a function of your choice in going from one value of the variable to the next. So if you want to add up all the squares of odd numbers between 1 and 10, you could say sum square function 1 and then the function of x that's x plus 2. So add 2 function and then 10 and it would do 1 squared plus 3 squared plus 5 squared plus 7 squared plus 9 squared. And then we try to do 11, but that's bigger than 10. So that would be the base case. So that's just, you know, an added level of generality that they added. For lecture purposes, I try to make things as simple as possible. So this was the simplest way to get a function in as an argument. Okay. Once we have this idea of function as argument, we can use it to generalize many, many, many interesting patterns. So, so here I have a function evens up at the top of the screen. What it does, it takes a sentence of numbers as an argument and it returns the subset of that sentence that's even numbers. So I say evens. Okay, let's try this again. Evens, quote, 3, 5, 1, 4, 9, 8, 6, 7. And I get 4, 8, 6, which are the even numbers out of that sentence. So does everybody see what the function does? What value it can get? Good. Okay, some people aren't thumbing. If you don't thumb, then I'm assuming up. That's not what you want. Give me a thumbs down. Okay, how does nums, how does evens work? Well, it's a recursive procedure. So first we have a base case, which is if nums is empty, then we return the empty sentence. Why do we do that? Well, the range of evens. Oh, yeah. I think I forgot to say this last week. The two most important words in the English language are domain and range. The domain of a function is what kinds of things does it take as arguments? The range of a function is what kinds of things does it return as its result? So the domain and range of evens are both sentences. So in the case of an empty sentence, I still have to return a sentence, and the one that I'll return is the empty sentence, okay? As opposed to some other procedure that might have, say, numbers as its range, and then the base case I might return 0 or 1 or whatever makes sense. Okay, so if we get down here, then the sentence isn't empty, so therefore it has a first word, which in this case is going to be a number, and this complicated formula is just asking, is that number even? So I take the number, I take the remainder on dividing that number by 2, and I ask the question, is this equal to 0? As it turns out, I could have just said even, question mark, first of nums. But I like doing it this way because it's a good example of composition of functions to remind you that the way scheme evaluates an expression like this is not left to right, is not right to left, it's inside out. So the very first thing that's going to happen is we figure out first of nums, which is in this example 3. And then, surrounding that, we take the remainder on dividing 3 by 2, which is 1, and then I say, is that remainder equal to 0? And the result of that is false. Okay, so for 3 we're not going to do this thing which is the action, but for an even number like when we get to 4, we are going to do this. So what is it we want to do if first of nums is even? Well, we want our result to include first of nums because it's even. What else should it include? Well, it should include all the even numbers of but first of nums. So I had a few people show up in office hours this morning who had the same problem, they wanted to say first of nums, new line, evens of but first of nums, do those two things. That's it. You can't do that, a function has to return one value. So if you're accustomed to thinking about programming in terms of first do this, then do this, then do this, try not to think that way and think what is the one value that I want to return? And the answer is I want to combine first of nums and the recursive call into a sentence. Sentences, the primitive sentence constructor takes any number of arguments, they can be words or sentences mixed together and it sort of strings them out into a sentence. So our result sentence is going to include first of nums and it's also going to include any even numbers that might come after first of nums in the sentence. And then the third con clause is otherwise, namely we have an odd number, if we have an odd number then I don't want first of nums to be part of the result because we only want even numbers. So I'm just going to call evens on but first of nums and whatever that returns that's what I'm going to return to, throwing away first of nums. Questions about how that works? Yes, sentences as opposed to, oh we haven't invented lists yet, that's chapter two. Sorry, sentences, sentences. Basically, she's reading ahead or she took CS3 or something. Lists are a more complicated data structure. Sentences, okay so the structure of the book, the first part of the book, there's chapter one which is all about procedural abstraction. It's about controlling the control, flow of control of a program. So how do you say here's what I want you to do and what mechanisms do we have to help you do that? And that's what chapter one is about. Chapter two is about data. And we get into data structures and how to build them up and they did it that way because there's some complexities about both of them and they only wanted to throw one kind of complexity at you at a time. Now in order to make that work, the book uses in chapter one entirely mathematical examples. That's because the book comes from MIT where everybody speaks mathematics, even the business majors. There aren't any English majors. And for our purposes in Berkeley, I wanted to provide more interesting data from the beginning but without running into the complexities of lists. And sentences are kind of an idiot-proof data type. You can't build the wrong thing by accident with them very easily. So once you get flow of control under control, then we'll go on and talk more about data. And we'll revisit this question of sentences and what they are and why. Okay, other questions? Okay, moving on. E words get life. So E words takes a sentence and it returns the subset of that sentence consisting of words that include the letter E. So in got to get you into my life, got into and you and into and my don't have the letter E but get and life do. Okay? Fortunately the word A doesn't have an E in it so it doesn't say get a life. All right, how does E words work? Well, it says if my argument sentence is empty, return the empty sentence. Then it says, okay, the sentence is not empty so it has a first word. Does that first word contain the letter E? So a member question mark of a one letter word and a word will return true if that letter is in the word and false if that letter is not in the word. You can also do a member question mark of a word and a sentence and it does a similar thing. Returns true if the word is in that sentence, false otherwise. So is the letter E in that first word? If so, then I want to include the first word in my result and I also want to include a recursive call for the but first. If not, if there isn't an E in that word then I just want to return a recursive call for the but first. Is Essie the same thing as a pen? He's another list guy. Essie is the constructor for sentences. It is subtly different from a pen for lists because it accepts words as arguments. But a general rule for you guys who learned about lists last semester, try not, and actually this is a good piece of advice for everybody who learned anything about programming before this semester. As we're doing stuff, try not to do it by mentally converting what you're learning to what you already know. And it's for the same reason that teachers of foreign languages tell you this. When I was learning French in high school, they told me correctly, try not to translate what you're reading into English. Try to think in French. Because you never get any good at it until you can think in French. And the same is true here. If you convert into what you did last semester or convert into what you did in high school, you're going to miss the point, I think. Okay? So, you know, try and stay on the same page with the rest of us. All right. Other questions? Yeah. Okay. Pronouns takes a sentence as an argument. And it returns the words of that sentence that are pronouns. Which is to say, ones that are in this list, which, yes, is not really exhaustive. It leaves out all the possessives and so on. How does it work? Well, if the sentence is empty, then return the empty sentence. Otherwise, the sentence has a first word. So ask, is that first word one of these words? If so, return a sentence that contains first of scent and also contains the result of a recursive call for the but first. If not, if it's not a pronoun, then just return pronouns of but first of scent. Okay? So these three procedures have a very similar pattern, right? Yes. Why is a member instead of equal? Because first of scent is going to be something like I or only. It's not going to, let's say it's I. Is I equal to this sentence? No. I isn't a sentence at all. And it certainly isn't, you know, this sentence. I doesn't have a me or you or he or she or it in it. So it's not equal to that, but it is a member of that. They're two different relations. Does that answer the question? Okay. Oops. So, I want to generalize this pattern. So, I'm going to take a predicate function as an argument. Because that's what's different. It's what test do we make of first of scent? And it takes a sentence as argument just like the other ones. And I'm going to say, conned, empty scent. Return the empty sentence. That's the same in all of them. Now what? Pred of first of scent. If pred of first of scent is true, then sentence first of scent. And a recursive call with the extra argument pred, the same. And, but first of scent. Else, just the recursive call. Okay? So compare this with the ones on the top half of the screen. And see if you agree that it captures the pattern. Namely, the only thing that's different is this part. This part where we make some particular test about first of scent. Everything else is the same. So what we need is to capture that test. And again, a test is an action. It's a thing to do. So it has to be a procedure rather than a number or a character string or a sentence or any of those things. Okay? So now I can say, keep even a question mark. Ta-da. Okay? The other ones, again, I'd have to define e-word question mark of word to be, remember, a question mark, quote, e-word. So make a function just for words. And now I should be able to say, keep e-word question mark. Let me get the answer. Okay? Questions. Is there any advantage to con versus a stack of nested ifs? Just that your code is prettier. If you keep nesting ifs, everything marches further and further toward the right margin, you know? And your line space in the window gets smaller and smaller. That's all. And also, as soon as you see the word conned, you're thinking, okay, this is a multi-way choice. So it helps the reader understand the structure, I think, a little bit. But scheme doesn't care. Ah, are there any exceptions to the thing where we have to define a function and then stick a function to the function? I did not pay him to ask that question. Let's do something new. What's this? Well, glad you asked that question. Back in high school or middle school or whenever it was, your algebra teacher was very sloppy about notation and said things like the function AX plus B. Right? AX plus B isn't a function. AX plus B is a number. We don't know what number it is until we have values for A and X and B. But that expression represents a number. Real mathematicians would say the function X maps to, that's pronounced maps to, AX plus B. There are a lot of advantages to this notation. One of which is that unlike this one, it makes clear what it's supposed to be a function of. In high school, they use more or less explicitly this convention that letters near the beginning of the alphabet are constants and letters near the end of the alphabet are variables. But suppose we had an expression like X, Y plus Z. Well, there are a lot of functions this could be. Maybe it's the function Y maps to X, Y plus Z. And somebody's going to tell us the values for X and Z separately. Maybe it's the function X, Y, Z maps to X, Y plus Z where it's a function of three arguments. Maybe it's Y, X, Z maps to X, Y plus Z. Well, that's the same function. X, Z, Y there. Now it's a different function. So saying what your parameters are of your function, the formal parameters, helps you understand what function you're talking about. Sadly, this character does not appear on your computer keyboard. If you're printing it in a paper, you can say, if you're using tech, you can say backslash maps to and it'll print that. But that's not how we do it in our programming languages. The way we do it is based on the work of a guy named Alonzo Church, who was a, who is, I guess, a mathematician. Did he die yet? Church, I don't know. He was alive a few years ago. So he may still be. He invented a formalism for trying to talk about mathematical ideas based on the notion of functions. And there's a long story about this, which I'm not going to tell you right now, probably, unless we have time at the end. But in his representation of functions, he used the Greek letter lambda, which looks like that. It's Greek for L, basically, to represent the thing that makes a function. So instead of saying X maps to AX plus B, he would say lambda X dot AX plus B. That was his notation, except that he wouldn't say this because there was nothing but functions in his language. So it's a little messier, but this is the lambda part. So a function of X that has this body. And we don't have a lambda on our keyboards either. I got my introduction to computing at the MIT Artificial Intelligence Lab and then the Stanford Artificial Intelligence Lab. And their keyboards do have lambdas because they do all their, you know, not all, but a lot of their programming are did in LISP, which is a language developed for artificial intelligence. So they use lambda a lot, but we have to spell it out. I will write it on the board using lambda, the letter. But when you're typing it into the computer, you have to say L-A-M-B-D-I. So the notation is lambda and then a sentence, basically, of formal parameters. And then an expression, which is the body of the function. So this is the function W-D maps to. Remember question mark, quote E-W-D. Yes. I'm sorry. Say it again. Tell me when. Oh, the keep function. Yeah. Okay. So did you understand, for example, how E-words works? Yes. Okay. So keep works the same way. Compare it. Con. If the sentence is empty, return the empty sentence. Okay. Now, if the first word of the sentence contains the letter E, that's what this says. In keep, I'm generalizing that. The same as I generalize the area function back at the beginning. That was the point of that thing about the different kinds of areas. It's the idea of generalizing a pattern. This is the part that I'm generalizing. And you capture a generalization by having an argument for what's different between the instances of this generalization. So what's different between E-words and pronouns? It's this part, right? Versus this part. That's what's different. Those things are expressions whose value is true or false. Now, maybe I have to explain the word pred. Pred is short for predicate. A predicate is a function whose range is true and false. True and false are called booleans. So a predicate is a function whose range is booleans. So this expression produces a true or a false. So a function to compute this expression will be a predicate. And what I do in the generalized version is I take whatever predicate I'm given and call it with first of scent as the argument. Okay? And all the rest of this is exactly the same as before except that the recursive call has this extra argument pred in it. Does that answer your question? Okay. Any more? What's there a difference between using a lambda and defining a function with define? There's so much isn't a difference. I didn't pay him either. There's so much isn't a difference that in fact when you say define square x times xx, what this is is an abbreviation for define square m to x times xx. Okay? This is the same kind of define as if I said define foo plus 2,3. So we evaluate the plus 2,3 the value of foo is 5. Now what's the value of a lambda expression? Okay? Lambda x times xx. What do I get? What do I get? I get that. That's a procedure. Lambda returns a procedure. Lambda makes a procedure. Lambda is how you make a procedure. Okay? So the way you learned last week, this way to make a procedure, you now know actually it has hidden inside it a lambda because lambda is the only way to make a procedure. Okay? So we evaluate this lambda expression but that does not entail evaluating its body. Right? When we create the square procedure we don't evaluate times xx. That doesn't happen until we call this function. Right? So I could say open print, open print lambda x times xx of 6. I get 36. Okay? So lambda makes a procedure. Now you can call that procedure just like any other procedure. Usually you don't type stuff like this. This is a very roundabout way of saying 6 times 6. What you use lambdas for is to use them as an argument to a procedure like keep. A procedure like keep is called a higher order procedure. Higher order meaning that either it takes a procedure as an argument or it returns a procedure as its value. That's what higher order means. Now procedures represent functions. Remember we had that discussion last week. A higher order procedure represents a higher order function. A higher order function is one that takes functions as arguments and or returns a function like for example derivative and integral. And calculus are higher order functions. Keep is a higher order function. It doesn't return a function but it takes a function as one of its two arguments. Okay? And it does that in order to generalize a pattern. Generalizing patterns is how we keep our programs from getting really long. Remember I said last week programming is easy as long as you can see the whole program at once. You know, when the program gets too big to look at it all at once it gets hard. The way we make programming possible is to try to make our program smaller. And we do that by cramming more into the same amount of space. And one way to do that is generalizing patterns so that you don't write almost the same code over and over again. Instead you write it once generalized like keep. Okay, see you Wednesday.