1 00:00:18,550 --> 00:00:21,230 PROFESSOR: Well, last time Gerry really let the cat out 2 00:00:21,230 --> 00:00:22,230 of the bag. 3 00:00:22,230 --> 00:00:26,350 He introduced the idea of assignment. 4 00:00:26,350 --> 00:00:33,405 Assignment and state. 5 00:00:37,620 --> 00:00:41,500 And as we started to see, the implications of introducing 6 00:00:41,500 --> 00:00:43,860 assignment and state into the language are absolutely 7 00:00:43,860 --> 00:00:45,350 frightening. 8 00:00:45,350 --> 00:00:47,240 First of all, the substitution model of 9 00:00:47,240 --> 00:00:48,865 evaluation breaks down. 10 00:00:48,865 --> 00:00:52,210 And we have to use this much more complicated environment 11 00:00:52,210 --> 00:00:53,780 model and this very mechanistic thing with 12 00:00:53,780 --> 00:00:56,530 diagrams, even to say what statements in the programming 13 00:00:56,530 --> 00:00:58,130 language mean. 14 00:00:58,130 --> 00:01:00,260 And that's not a mere technical point. 15 00:01:00,260 --> 00:01:03,090 See, it's not that we had this particular substitution model 16 00:01:03,090 --> 00:01:05,200 and, well, it doesn't quite work, so we have to do 17 00:01:05,200 --> 00:01:05,870 something else. 18 00:01:05,870 --> 00:01:10,730 It's that nothing like the substitution model can work. 19 00:01:10,730 --> 00:01:15,950 Because suddenly, a variable is not just something that 20 00:01:15,950 --> 00:01:18,080 stands for a value. 21 00:01:18,080 --> 00:01:22,390 A variable now has to somehow specify a place 22 00:01:22,390 --> 00:01:23,630 that holds a value. 23 00:01:23,630 --> 00:01:25,885 And the value that's in that place can change. 24 00:01:30,280 --> 00:01:39,110 Or for instance, an expression like f of x might have a side 25 00:01:39,110 --> 00:01:40,410 effect in it. 26 00:01:40,410 --> 00:01:44,160 So if we say f of x and it has some value, and then later we 27 00:01:44,160 --> 00:01:48,890 say f of x again, we might get a different value 28 00:01:48,890 --> 00:01:49,730 depending on the order. 29 00:01:49,730 --> 00:01:52,780 So suddenly, we have to think not only about values but 30 00:01:52,780 --> 00:01:54,030 about time. 31 00:01:57,970 --> 00:02:02,070 And then things like pairs are no longer just their CARs and 32 00:02:02,070 --> 00:02:02,520 their CDRs. 33 00:02:02,520 --> 00:02:06,310 A pair now is not quite its CAR and its CDR. It's rather 34 00:02:06,310 --> 00:02:08,449 its identity. 35 00:02:08,449 --> 00:02:11,650 So a pair has identity. 36 00:02:11,650 --> 00:02:12,900 It's an object. 37 00:02:21,330 --> 00:02:26,280 And two pairs that have the same CAR and CDR might be the 38 00:02:26,280 --> 00:02:29,650 same or different, because suddenly we have to worry 39 00:02:29,650 --> 00:02:30,900 about sharing. 40 00:02:34,960 --> 00:02:38,910 So all of these things enter as soon as we introduce 41 00:02:38,910 --> 00:02:40,480 assignment. 42 00:02:40,480 --> 00:02:43,340 See, this is a really far cry from where we started with 43 00:02:43,340 --> 00:02:45,400 substitution. 44 00:02:45,400 --> 00:02:50,420 It's a technically harder way of looking at things because 45 00:02:50,420 --> 00:02:52,710 we have to think more mechanistically about our 46 00:02:52,710 --> 00:02:53,540 programming language. 47 00:02:53,540 --> 00:02:55,960 We can't just think about it as mathematics. 48 00:02:55,960 --> 00:02:59,860 It's philosophically harder, because suddenly there are all 49 00:02:59,860 --> 00:03:02,020 these funny issues about what does it mean that something 50 00:03:02,020 --> 00:03:04,050 changes or that two things are the same. 51 00:03:04,050 --> 00:03:07,910 And also, it's programming harder, because as Gerry 52 00:03:07,910 --> 00:03:10,070 showed last time, there are all these bugs having to do 53 00:03:10,070 --> 00:03:14,420 with bad sequencing and aliasing that just don't exist 54 00:03:14,420 --> 00:03:18,210 in a language where we don't worry about objects. 55 00:03:18,210 --> 00:03:23,635 Well, how'd we get into this mess? 56 00:03:23,635 --> 00:03:27,500 Remember what we did, the reason we got into this is 57 00:03:27,500 --> 00:03:35,750 because we were looking to build modular systems. We 58 00:03:35,750 --> 00:03:40,250 wanted to build systems that fall apart into chunks that 59 00:03:40,250 --> 00:03:42,760 seem natural. 60 00:03:42,760 --> 00:03:46,260 So for instance, we want to take a random number generator 61 00:03:46,260 --> 00:03:48,660 and package up the state of that random number generator 62 00:03:48,660 --> 00:03:52,840 inside of it so that we can separate the idea of picking 63 00:03:52,840 --> 00:03:56,640 random numbers from the general Monte Carlo strategy 64 00:03:56,640 --> 00:03:59,740 of estimating something and separate that from the 65 00:03:59,740 --> 00:04:03,060 particular way that you work with random numbers in that 66 00:04:03,060 --> 00:04:06,980 formula developed by Cesaro for pi. 67 00:04:06,980 --> 00:04:11,400 And similarly, when we go off and construct some models of 68 00:04:11,400 --> 00:04:15,440 things, if we go off and model a system that we see in the 69 00:04:15,440 --> 00:04:19,050 real world, we'd like our program to break into natural 70 00:04:19,050 --> 00:04:22,310 pieces, pieces that mirror the parts of the system that we 71 00:04:22,310 --> 00:04:24,900 see in the real world. 72 00:04:24,900 --> 00:04:28,780 So for example, if we look at a digital circuit, we say, 73 00:04:28,780 --> 00:04:33,910 gee, there's a circuit and it has a piece and 74 00:04:33,910 --> 00:04:35,160 it has another piece. 75 00:04:40,100 --> 00:04:43,580 And these different pieces sort of have identity. 76 00:04:43,580 --> 00:04:45,550 They have state. 77 00:04:45,550 --> 00:04:48,580 And the state sits on these wires. 78 00:04:48,580 --> 00:04:51,020 And we think of this piece as an object that's different 79 00:04:51,020 --> 00:04:52,610 from that as an object. 80 00:04:52,610 --> 00:04:54,400 And when we watch the system change, we think about a 81 00:04:54,400 --> 00:04:57,500 signal coming in here and changing a state that might be 82 00:04:57,500 --> 00:04:59,860 here and going here and interacting with a state that 83 00:04:59,860 --> 00:05:02,170 might be stored there, and so on and so on. 84 00:05:06,860 --> 00:05:12,760 So what we'd like is we'd like to build in the computer 85 00:05:12,760 --> 00:05:17,340 systems that fall into pieces that mirror our view of 86 00:05:17,340 --> 00:05:19,870 reality, of the way that the actual systems we're modeling 87 00:05:19,870 --> 00:05:23,365 seem to fall into pieces. 88 00:05:23,365 --> 00:05:28,970 Well, maybe the reason that building systems like this 89 00:05:28,970 --> 00:05:31,600 seems to introduce such technical complications has 90 00:05:31,600 --> 00:05:33,610 nothing to do with computers. 91 00:05:33,610 --> 00:05:37,960 See, maybe the real reason that we pay such a price to 92 00:05:37,960 --> 00:05:41,910 write programs that mirror our view of reality is that we 93 00:05:41,910 --> 00:05:44,550 have the wrong view of reality. 94 00:05:44,550 --> 00:05:47,460 See, maybe time is just an illusion, and 95 00:05:47,460 --> 00:05:50,150 nothing ever changes. 96 00:05:50,150 --> 00:05:52,910 See, for example, if I take this chalk, and we say, gee, 97 00:05:52,910 --> 00:05:55,820 this is an object and it has a state. 98 00:05:55,820 --> 00:05:59,710 At each moment it has a position and a velocity. 99 00:05:59,710 --> 00:06:01,240 And if we do something, that state can change. 100 00:06:04,340 --> 00:06:07,900 But if you studied any relativity, for instance, you 101 00:06:07,900 --> 00:06:09,760 know that you don't think of the path of that chalk as 102 00:06:09,760 --> 00:06:11,340 something that goes on instant by instant. 103 00:06:11,340 --> 00:06:13,870 It's more insightful to think of that whole chalk's 104 00:06:13,870 --> 00:06:16,020 existence as a path in space-time. 105 00:06:16,020 --> 00:06:18,040 that's all splayed out. 106 00:06:18,040 --> 00:06:19,840 There aren't individual positions and velocities. 107 00:06:19,840 --> 00:06:24,640 There's just its unchanging existence in space-time. 108 00:06:24,640 --> 00:06:28,080 Similarly, if we look at this electrical system, if we 109 00:06:28,080 --> 00:06:32,450 imagine this electrical system is implementing some sort of 110 00:06:32,450 --> 00:06:35,730 signal processing system, the signal processing engineer who 111 00:06:35,730 --> 00:06:39,010 put that thing together doesn't think of it as, well, 112 00:06:39,010 --> 00:06:41,490 at each instance there's a voltage coming in. 113 00:06:41,490 --> 00:06:43,340 And that translates into something. 114 00:06:43,340 --> 00:06:46,400 And that affects the state over here, which changes the 115 00:06:46,400 --> 00:06:46,810 state over here. 116 00:06:46,810 --> 00:06:49,060 Nobody putting together a signal processing system 117 00:06:49,060 --> 00:06:50,420 thinks about it like that. 118 00:06:50,420 --> 00:06:56,830 Instead, you say there's this signal that's 119 00:06:56,830 --> 00:06:58,060 splayed out over time. 120 00:06:58,060 --> 00:07:01,100 And if this is acting as a filter, this whole thing 121 00:07:01,100 --> 00:07:09,570 transforms this whole thing for some sort of other output. 122 00:07:09,570 --> 00:07:11,790 You don't think of it as what's happening instant by 123 00:07:11,790 --> 00:07:14,160 instant as the state of these things. 124 00:07:14,160 --> 00:07:17,990 And somehow you think of this box as a whole thing, not as 125 00:07:17,990 --> 00:07:20,980 little pieces sending messages of state to each other at 126 00:07:20,980 --> 00:07:22,230 particular instants. 127 00:07:28,250 --> 00:07:30,130 Well, today we're going to look at another way to 128 00:07:30,130 --> 00:07:34,260 decompose systems that's more like the signal processing 129 00:07:34,260 --> 00:07:37,050 engineer's view of the world than it is like thinking about 130 00:07:37,050 --> 00:07:41,130 objects that communicate sending messages. 131 00:07:41,130 --> 00:07:43,310 That's called stream processing. 132 00:07:54,570 --> 00:08:01,790 And we're going to start by showing how we can make our 133 00:08:01,790 --> 00:08:08,550 programs more uniform and see a lot more commonality if we 134 00:08:08,550 --> 00:08:12,490 throw out of these programs what you might say is an 135 00:08:12,490 --> 00:08:17,210 inordinate concern with worrying about time. 136 00:08:17,210 --> 00:08:19,910 Let me start by comparing two procedures. 137 00:08:23,260 --> 00:08:25,690 The first one does this. 138 00:08:25,690 --> 00:08:27,770 We imagine that there's a tree. 139 00:08:30,400 --> 00:08:33,179 Say there's a tree of integers. 140 00:08:33,179 --> 00:08:34,429 It's a binary tree. 141 00:08:39,100 --> 00:08:40,230 So it looks like this. 142 00:08:40,230 --> 00:08:44,990 And there's integers in each of the nodes. 143 00:08:44,990 --> 00:08:51,000 And what we would like to compute is for each odd number 144 00:08:51,000 --> 00:08:54,210 sitting here, we'd like to find the square and then sum 145 00:08:54,210 --> 00:08:57,210 up all those squares. 146 00:08:57,210 --> 00:08:59,480 Well, that should be a familiar kind of thing. 147 00:08:59,480 --> 00:09:02,930 There's a recursive strategy for doing it. 148 00:09:02,930 --> 00:09:04,880 We look at each leaf, and either it's going to 149 00:09:04,880 --> 00:09:06,690 contribute the square of the number if it's odd 150 00:09:06,690 --> 00:09:08,680 or 0 if it's even. 151 00:09:08,680 --> 00:09:13,280 And then recursively, we can say at each tree, the sum of 152 00:09:13,280 --> 00:09:15,330 all of them is the sum coming from the right branch and the 153 00:09:15,330 --> 00:09:17,640 left branch, and recursively down through the nodes. 154 00:09:17,640 --> 00:09:20,360 And that's a familiar way of thinking about programming. 155 00:09:20,360 --> 00:09:23,960 Let's actually look at that on the slide. 156 00:09:23,960 --> 00:09:27,960 We say to sum the odd squares in a tree, well, there's a 157 00:09:27,960 --> 00:09:30,520 test. Either it's a leaf node, and we're going to check to 158 00:09:30,520 --> 00:09:34,710 see if it's an integer, and then either it's odd, in which 159 00:09:34,710 --> 00:09:37,160 we take the square, or else it's 0. 160 00:09:37,160 --> 00:09:40,260 And then the sum of the whole thing is the sum coming from 161 00:09:40,260 --> 00:09:42,120 the left branch and the right branch. 162 00:09:46,340 --> 00:09:51,560 OK, well, let me contrast that with a second problem. 163 00:09:51,560 --> 00:09:55,810 Suppose I give you an integer n, and then some function to 164 00:09:55,810 --> 00:09:59,270 compute of the first of each integer in 1 through n. 165 00:09:59,270 --> 00:10:01,810 And then I want to collect together in a list all those 166 00:10:01,810 --> 00:10:05,600 function values that satisfy some property. 167 00:10:05,600 --> 00:10:06,880 That's a general kind of thing. 168 00:10:06,880 --> 00:10:09,750 Let's say to be specific, let's imagine that for each 169 00:10:09,750 --> 00:10:11,270 integer, k, we're going to compute 170 00:10:11,270 --> 00:10:14,210 the k Fibonacci number. 171 00:10:14,210 --> 00:10:17,550 And then we'll see which of those are odd and assemble 172 00:10:17,550 --> 00:10:19,050 those into a list. 173 00:10:19,050 --> 00:10:20,710 So here's a procedure that does that. 174 00:10:23,730 --> 00:10:26,240 Find the odd Fibonacci numbers among the first n. 175 00:10:26,240 --> 00:10:28,910 And here is a standard loop the way we've been writing it. 176 00:10:28,910 --> 00:10:30,800 This is a recursion. 177 00:10:30,800 --> 00:10:33,740 It's a loop on k, and says if k is bigger than n, it's the 178 00:10:33,740 --> 00:10:36,990 empty list. Otherwise we compute the k-th Fibonacci 179 00:10:36,990 --> 00:10:40,370 number, call that f. 180 00:10:40,370 --> 00:10:45,180 If it's odd, we CONS it on to the list starting 181 00:10:45,180 --> 00:10:47,690 with the next one. 182 00:10:47,690 --> 00:10:50,390 And otherwise, we just take the next one. 183 00:10:50,390 --> 00:10:52,000 And this is the standard way we've been 184 00:10:52,000 --> 00:10:53,000 writing iterative loops. 185 00:10:53,000 --> 00:10:57,600 And we start off calling that loop with 1. 186 00:10:57,600 --> 00:11:01,600 OK, so there are two procedures. 187 00:11:01,600 --> 00:11:02,900 Those procedures look very different. 188 00:11:02,900 --> 00:11:04,390 They have very different structures. 189 00:11:04,390 --> 00:11:07,740 Yet from a certain point of view, those procedures are 190 00:11:07,740 --> 00:11:11,330 really doing very much the same thing. 191 00:11:11,330 --> 00:11:14,930 So if I was talking like a signal processing engineer, 192 00:11:14,930 --> 00:11:25,730 what I might say is that the first procedure enumerates the 193 00:11:25,730 --> 00:11:26,980 leaves of a tree. 194 00:11:31,160 --> 00:11:33,510 And then we can think of a signal coming out of that, 195 00:11:33,510 --> 00:11:35,330 which is all the leaves. 196 00:11:35,330 --> 00:11:43,970 We'll filter them to see which ones are odd, put them through 197 00:11:43,970 --> 00:11:45,190 some kind of filter. 198 00:11:45,190 --> 00:11:49,000 We'll then put them through a kind of transducer. 199 00:11:49,000 --> 00:11:51,420 And for each one of those things, we'll take the square. 200 00:11:54,200 --> 00:11:58,290 And then we'll accumulate all of those. 201 00:11:58,290 --> 00:12:00,570 We'll accumulate them by sticking them together with 202 00:12:00,570 --> 00:12:03,340 addition starting from 0. 203 00:12:07,140 --> 00:12:08,210 That's the first program. 204 00:12:08,210 --> 00:12:10,620 The second program, I can describe in a very, very 205 00:12:10,620 --> 00:12:11,780 similar way. 206 00:12:11,780 --> 00:12:17,450 I'll say, we'll enumerate the numbers on this interval, for 207 00:12:17,450 --> 00:12:19,080 the interval 1 through n. 208 00:12:22,500 --> 00:12:28,080 We'll, for each one, compute the Fibonacci number, put them 209 00:12:28,080 --> 00:12:29,270 through a transducer. 210 00:12:29,270 --> 00:12:31,780 We'll then take the result of that, and we'll 211 00:12:31,780 --> 00:12:35,976 filter it for oddness. 212 00:12:35,976 --> 00:12:39,350 And then we'll take those and put them into an accumulator. 213 00:12:39,350 --> 00:12:41,730 This time we'll build up a list, so we'll accumulate with 214 00:12:41,730 --> 00:12:47,110 CONS starting from the empty list. 215 00:12:47,110 --> 00:12:50,940 So this way of looking at the program makes the two seem 216 00:12:50,940 --> 00:12:51,900 very, very similar. 217 00:12:51,900 --> 00:12:55,880 The problem is that that commonality is completely 218 00:12:55,880 --> 00:12:58,050 obscured when we look at the procedures we wrote. 219 00:12:58,050 --> 00:13:02,670 Let's go back and look at some odd squares again, and say 220 00:13:02,670 --> 00:13:06,300 things like, where's the enumerator? 221 00:13:06,300 --> 00:13:08,140 Where's the enumerator in this program? 222 00:13:08,140 --> 00:13:11,230 Well, it's not in one place. 223 00:13:11,230 --> 00:13:15,990 It's a little bit in this leaf-node test, 224 00:13:15,990 --> 00:13:17,160 which is going to stop. 225 00:13:17,160 --> 00:13:19,380 It's a little bit in the recursive structure of the 226 00:13:19,380 --> 00:13:20,630 thing itself. 227 00:13:23,150 --> 00:13:24,120 Where's the accumulator? 228 00:13:24,120 --> 00:13:25,680 The accumulator isn't in one place either. 229 00:13:25,680 --> 00:13:32,180 It's partly in this 0 and partly in this plus. 230 00:13:32,180 --> 00:13:34,510 It's not there as a thing that we can look at. 231 00:13:34,510 --> 00:13:40,550 Similarly, if we look at odd Fibs, that's also, in some 232 00:13:40,550 --> 00:13:42,940 sense, an enumerator and an accumulator, but 233 00:13:42,940 --> 00:13:44,470 it looks very different. 234 00:13:44,470 --> 00:13:49,260 Because partly, the enumerator is here in this greater than 235 00:13:49,260 --> 00:13:52,100 sign in the test. And partly it's in this whole recursive 236 00:13:52,100 --> 00:13:55,680 structure in the loop, and the way that we call it. 237 00:13:55,680 --> 00:13:58,100 And then similarly, that's also mixed up in there with 238 00:13:58,100 --> 00:14:01,010 the accumulator, which is partly over there and partly 239 00:14:01,010 --> 00:14:03,600 over there. 240 00:14:03,600 --> 00:14:09,790 So these very, very natural pieces, these very natural 241 00:14:09,790 --> 00:14:13,770 boxes here don't appear in our programs. Because they're kind 242 00:14:13,770 --> 00:14:14,360 of mixed up. 243 00:14:14,360 --> 00:14:16,290 The programs don't chop things up in the right way. 244 00:14:19,450 --> 00:14:22,240 Going back to this fundamental principle of computer science 245 00:14:22,240 --> 00:14:24,620 that in order to control something, you need the name 246 00:14:24,620 --> 00:14:27,820 of it, we don't really have control over thinking about 247 00:14:27,820 --> 00:14:30,500 things this way because we don't have our hands in them 248 00:14:30,500 --> 00:14:31,060 explicitly. 249 00:14:31,060 --> 00:14:35,510 We don't have a good language for talking about them. 250 00:14:35,510 --> 00:14:42,850 Well, let's invent an appropriate language in which 251 00:14:42,850 --> 00:14:44,515 we can build these pieces. 252 00:14:44,515 --> 00:14:48,650 The key to the language is these guys, is what is these 253 00:14:48,650 --> 00:14:50,480 things I called signals? 254 00:14:50,480 --> 00:14:52,070 What are these things that are flying on the 255 00:14:52,070 --> 00:14:53,320 arrows between the boxes? 256 00:14:56,880 --> 00:15:02,840 Well, those things are going to be data structures called 257 00:15:02,840 --> 00:15:04,770 streams. That's going to be the key to 258 00:15:04,770 --> 00:15:07,980 inventing this language. 259 00:15:07,980 --> 00:15:08,600 What's a stream? 260 00:15:08,600 --> 00:15:10,820 Well, a stream is, like anything else, a data 261 00:15:10,820 --> 00:15:12,220 abstraction. 262 00:15:12,220 --> 00:15:15,000 So I should tell you what its selectors and 263 00:15:15,000 --> 00:15:16,870 constructors are. 264 00:15:16,870 --> 00:15:20,185 For a stream, we're going to have one constructor that's 265 00:15:20,185 --> 00:15:21,435 called CONS-stream. 266 00:15:25,690 --> 00:15:29,060 CONS-stream is going to put two things together to form a 267 00:15:29,060 --> 00:15:32,040 thing called a stream. 268 00:15:32,040 --> 00:15:34,250 And then to extract things from the stream, we're going 269 00:15:34,250 --> 00:15:38,010 to have a selector called the head of the stream. 270 00:15:38,010 --> 00:15:41,340 So if I have a stream, I can take its head or I 271 00:15:41,340 --> 00:15:44,720 can take its tail. 272 00:15:44,720 --> 00:15:48,290 And remember, I have to tell you George's contract here to 273 00:15:48,290 --> 00:15:53,160 tell you what the axioms are that relate these. 274 00:15:53,160 --> 00:16:04,080 And it's going to be for any x and y, if I form the 275 00:16:04,080 --> 00:16:11,420 CONS-stream and take the head, the head of CONS-stream of x 276 00:16:11,420 --> 00:16:26,590 and y is going to be x and the tail of CONS-stream of x and y 277 00:16:26,590 --> 00:16:28,440 is going to be y. 278 00:16:28,440 --> 00:16:31,180 So those are the constructor, two selectors for 279 00:16:31,180 --> 00:16:34,750 streams, and an axiom. 280 00:16:34,750 --> 00:16:36,980 There's something fishy here. 281 00:16:36,980 --> 00:16:41,060 So you might notice that these are exactly the axioms for 282 00:16:41,060 --> 00:16:46,100 CONS, CAR, and CDR. If instead of writing CONS-stream I wrote 283 00:16:46,100 --> 00:16:50,810 CONS and I said head was the CAR and tail was the CDR, 284 00:16:50,810 --> 00:16:52,810 those are exactly the axioms for pairs. 285 00:16:52,810 --> 00:16:55,130 And in fact, there's another thing here. 286 00:16:55,130 --> 00:17:02,930 We're going to have a thing called the-empty-stream, which 287 00:17:02,930 --> 00:17:08,319 is like the-empty-list. 288 00:17:08,319 --> 00:17:10,030 So why am I introducing this terminology? 289 00:17:10,030 --> 00:17:12,780 Why don't I just keep talking about pairs and lists? 290 00:17:12,780 --> 00:17:15,510 Well, we'll see. 291 00:17:15,510 --> 00:17:18,440 For now, if you like, why don't you just pretend that 292 00:17:18,440 --> 00:17:21,560 streams really are just a terminology for lists. 293 00:17:21,560 --> 00:17:24,890 And we'll see in a little while why we want to keep this 294 00:17:24,890 --> 00:17:28,150 extra abstraction layer and not just call them lists. 295 00:17:32,300 --> 00:17:34,860 OK, now that we have streams, we can start constructing the 296 00:17:34,860 --> 00:17:38,990 pieces of the language to operate on streams. And there 297 00:17:38,990 --> 00:17:41,330 are a whole bunch of very useful things that we could 298 00:17:41,330 --> 00:17:42,120 start making. 299 00:17:42,120 --> 00:17:54,850 For instance, we'll make our map box to take a stream, s, 300 00:17:54,850 --> 00:18:00,400 and a procedure, and to generate a new stream which 301 00:18:00,400 --> 00:18:03,640 has as its elements the procedure applied to all the 302 00:18:03,640 --> 00:18:05,666 successive elements of s. 303 00:18:05,666 --> 00:18:07,400 In fact, we've seen this before. 304 00:18:07,400 --> 00:18:10,950 This is the procedure map that we did with lists. 305 00:18:10,950 --> 00:18:14,000 And you see it's exactly map, except we're testing for 306 00:18:14,000 --> 00:18:14,650 empty-stream. 307 00:18:14,650 --> 00:18:15,560 Oh, I forgot to mention that. 308 00:18:15,560 --> 00:18:19,420 Empty-stream is like the null test. So if it's empty, we 309 00:18:19,420 --> 00:18:20,510 generate the empty stream. 310 00:18:20,510 --> 00:18:24,700 Otherwise, we form a new stream whose first element is 311 00:18:24,700 --> 00:18:28,950 the procedure applied to the head of the stream, and whose 312 00:18:28,950 --> 00:18:31,570 rest is gotten by mapping along with the procedure down 313 00:18:31,570 --> 00:18:33,140 the tail of the stream. 314 00:18:33,140 --> 00:18:34,920 So that looks exactly like the map procedure 315 00:18:34,920 --> 00:18:37,030 we looked at before. 316 00:18:37,030 --> 00:18:38,350 Here's another useful thing. 317 00:18:38,350 --> 00:18:40,460 Filter, this is our filter box. 318 00:18:40,460 --> 00:18:43,890 We're going to have a predicate and a stream. 319 00:18:43,890 --> 00:18:46,720 We're going to make a new stream that consists of all 320 00:18:46,720 --> 00:18:48,310 the elements of the original one 321 00:18:48,310 --> 00:18:50,160 that satisfy the predicate. 322 00:18:50,160 --> 00:18:51,270 That's case analysis. 323 00:18:51,270 --> 00:18:53,140 When there's nothing in the stream, we 324 00:18:53,140 --> 00:18:56,280 return the empty stream. 325 00:18:56,280 --> 00:19:00,060 We test the predicate on the head of the stream. 326 00:19:00,060 --> 00:19:03,520 And if it's true, we add the head of the stream onto the 327 00:19:03,520 --> 00:19:08,220 result of filtering the tail of the stream. 328 00:19:08,220 --> 00:19:10,870 And otherwise, if that predicate was false, we just 329 00:19:10,870 --> 00:19:13,500 filter the tail of the stream. 330 00:19:13,500 --> 00:19:16,595 Right, so there's filter. 331 00:19:16,595 --> 00:19:18,560 Let me run through a couple more rather quickly. 332 00:19:18,560 --> 00:19:20,880 They're all in the book and you can look at them. 333 00:19:20,880 --> 00:19:22,110 Let me just flash through. 334 00:19:22,110 --> 00:19:23,260 Here's accumulate. 335 00:19:23,260 --> 00:19:27,690 Accumulate takes a way of combining things and an 336 00:19:27,690 --> 00:19:31,560 initial value in a stream and sticks them all together. 337 00:19:31,560 --> 00:19:33,970 If the stream's empty, it's just the initial value. 338 00:19:33,970 --> 00:19:36,930 Otherwise, we combine the head of the stream with the result 339 00:19:36,930 --> 00:19:39,550 of accumulating the tail of the stream starting from the 340 00:19:39,550 --> 00:19:40,900 initial value. 341 00:19:40,900 --> 00:19:42,830 So that's what I'd use to add up everything in the stream. 342 00:19:42,830 --> 00:19:45,830 I'd accumulate with plus. 343 00:19:45,830 --> 00:19:48,060 How would I enumerate the leaves of a tree? 344 00:19:48,060 --> 00:19:54,530 Well, if the tree is just a leaf itself, I make something 345 00:19:54,530 --> 00:19:56,640 which only has that node in it. 346 00:19:56,640 --> 00:20:01,100 Otherwise, I append together the stuff of enumerating the 347 00:20:01,100 --> 00:20:04,340 left branch and the right branch. 348 00:20:04,340 --> 00:20:08,130 And then append here is like the ordinary append on lists. 349 00:20:13,190 --> 00:20:13,850 You can look at that. 350 00:20:13,850 --> 00:20:16,410 That's analogous to the ordinary procedure for 351 00:20:16,410 --> 00:20:19,150 appending two lists. 352 00:20:19,150 --> 00:20:21,810 How would I enumerate an interval? 353 00:20:21,810 --> 00:20:24,500 This will take two integers, low and high, and generate a 354 00:20:24,500 --> 00:20:28,106 stream of the integers going from low to high. 355 00:20:28,106 --> 00:20:31,890 And we can make a whole bunch of pieces. 356 00:20:31,890 --> 00:20:34,860 So that's a little language of talking about streams. Once we 357 00:20:34,860 --> 00:20:37,670 have streams, we can build things for manipulating them. 358 00:20:37,670 --> 00:20:40,200 Again, we're making a language. 359 00:20:40,200 --> 00:20:41,270 And now we can start expressing 360 00:20:41,270 --> 00:20:43,060 things in this language. 361 00:20:43,060 --> 00:20:46,590 Here's our original procedure for summing the odd 362 00:20:46,590 --> 00:20:47,310 squares in a tree. 363 00:20:47,310 --> 00:20:52,210 And you'll notice it looks exactly now like the block 364 00:20:52,210 --> 00:20:54,590 diagram, like the signal processing block diagram. 365 00:20:54,590 --> 00:21:00,230 So to sum the odd squares in a tree, we enumerate the leaves 366 00:21:00,230 --> 00:21:01,320 of the tree. 367 00:21:01,320 --> 00:21:04,830 We filter that for oddness. 368 00:21:04,830 --> 00:21:06,220 We map that for squareness. 369 00:21:09,320 --> 00:21:12,460 And we accumulate the result of that using addition, 370 00:21:12,460 --> 00:21:14,760 starting from 0. 371 00:21:14,760 --> 00:21:17,290 So we can see the pieces that we wanted. 372 00:21:17,290 --> 00:21:22,050 Similarly, the Fibonacci one, how do we get the odd Fibs? 373 00:21:22,050 --> 00:21:27,900 Well, we enumerate the interval from 1 to n, we map 374 00:21:27,900 --> 00:21:30,920 along that, computing the Fibonacci of each one. 375 00:21:30,920 --> 00:21:34,810 We filter the result of those for oddness. 376 00:21:34,810 --> 00:21:38,460 And we accumulate all of that stuff using CONS starting from 377 00:21:38,460 --> 00:21:43,650 the empty-list. 378 00:21:43,650 --> 00:21:47,680 OK, what's the advantage of this? 379 00:21:47,680 --> 00:21:50,260 Well, for one thing, we now have pieces that we can start 380 00:21:50,260 --> 00:21:51,880 mixing and matching. 381 00:21:51,880 --> 00:21:58,230 So for instance, if I wanted to change this, if I wanted to 382 00:21:58,230 --> 00:22:00,400 compute the squares of the integers and then filter them, 383 00:22:00,400 --> 00:22:03,810 all I need to do is pick up a standard piece like this in 384 00:22:03,810 --> 00:22:06,210 that square and put it in. 385 00:22:06,210 --> 00:22:10,150 Or if we wanted to do this whole Fibonacci computation on 386 00:22:10,150 --> 00:22:12,980 the leaves of a tree rather than a sequence, all I need to 387 00:22:12,980 --> 00:22:18,030 do is replace this enumerator with that one. 388 00:22:18,030 --> 00:22:20,650 See, the advantage of this stream processing is that 389 00:22:20,650 --> 00:22:21,995 we're establishing-- 390 00:22:21,995 --> 00:22:25,330 this is one of the big themes of the course-- 391 00:22:25,330 --> 00:22:35,570 we're establishing conventional interfaces that 392 00:22:35,570 --> 00:22:38,130 allow us to glue things together. 393 00:22:38,130 --> 00:22:41,730 Things like map and filter are a standard set of components 394 00:22:41,730 --> 00:22:43,900 that we can start using for pasting together programs in 395 00:22:43,900 --> 00:22:45,750 all sorts of ways. 396 00:22:45,750 --> 00:22:50,090 It allows us to see the commonality of programs. 397 00:22:50,090 --> 00:22:52,390 I just ought to mention, I've only showed you two 398 00:22:52,390 --> 00:22:53,860 procedures. 399 00:22:53,860 --> 00:22:57,800 But let me emphasize that this way of putting things together 400 00:22:57,800 --> 00:22:59,780 with maps, filters, and accumulators 401 00:22:59,780 --> 00:23:01,410 is very, very general. 402 00:23:01,410 --> 00:23:08,010 It's the generate and test paradigm for programs. And as 403 00:23:08,010 --> 00:23:11,970 an example of that, Richard Waters, who was at MIT when he 404 00:23:11,970 --> 00:23:14,060 was a graduate student, as part of his thesis research 405 00:23:14,060 --> 00:23:17,700 went and analyzed a large chunk of the IBM scientific 406 00:23:17,700 --> 00:23:22,340 subroutine library, and discovered that about 60% of 407 00:23:22,340 --> 00:23:26,830 the programs in it could be expressed exactly in terms 408 00:23:26,830 --> 00:23:28,940 using no more than what we've put here-- 409 00:23:28,940 --> 00:23:30,710 map, filter, and accumulate. 410 00:23:30,710 --> 00:23:31,960 All right, let's take a break. 411 00:23:36,620 --> 00:23:37,870 Questions? 412 00:23:40,470 --> 00:23:43,033 AUDIENCE: It seems like the essence of this whole thing is 413 00:23:43,033 --> 00:23:45,980 just that you have a very uniform, simple data structure 414 00:23:45,980 --> 00:23:48,380 to work with, the stream. 415 00:23:48,380 --> 00:23:48,920 PROFESSOR: Right. 416 00:23:48,920 --> 00:23:51,670 The essence is that you, again, it's this sense of 417 00:23:51,670 --> 00:23:53,710 conventional interfaces. 418 00:23:53,710 --> 00:23:55,610 So you can start putting a lot of things together. 419 00:23:55,610 --> 00:23:59,830 And the stream is as you say, the uniform data structure 420 00:23:59,830 --> 00:24:00,890 that supports that. 421 00:24:00,890 --> 00:24:03,600 This is very much like APL, by the way. 422 00:24:03,600 --> 00:24:06,330 APL is very much the same idea, except in APL, instead 423 00:24:06,330 --> 00:24:09,560 of this stream, you have arrays and vectors. 424 00:24:09,560 --> 00:24:13,565 And a lot of the power of APL is exactly the same reason of 425 00:24:13,565 --> 00:24:14,815 the power of this. 426 00:24:19,910 --> 00:24:20,910 OK, thank you. 427 00:24:20,910 --> 00:24:22,160 Let's take a break. 428 00:24:57,470 --> 00:24:57,610 All right. 429 00:24:57,610 --> 00:25:02,830 We've been looking at ways of organizing computations using 430 00:25:02,830 --> 00:25:07,560 streams. What I want to do now is just show you two somewhat 431 00:25:07,560 --> 00:25:10,810 more complicated examples of that. 432 00:25:10,810 --> 00:25:15,000 Let's start by thinking about the following kind of utility 433 00:25:15,000 --> 00:25:16,810 procedure that will come in useful. 434 00:25:16,810 --> 00:25:19,960 Suppose I've got a stream. 435 00:25:19,960 --> 00:25:23,730 And the elements of this stream are themselves streams. 436 00:25:23,730 --> 00:25:26,530 So the first thing might be 1, 2, 3. 437 00:25:32,600 --> 00:25:33,880 So I've got a stream. 438 00:25:33,880 --> 00:25:40,100 And each element of the stream is itself a stream. 439 00:25:40,100 --> 00:25:45,580 And what I'd like to do is build a stream that collects 440 00:25:45,580 --> 00:25:47,870 together all of the elements, pulls all of the elements out 441 00:25:47,870 --> 00:25:50,840 of these sub-streams and strings them all 442 00:25:50,840 --> 00:25:52,080 together in one thing. 443 00:25:52,080 --> 00:25:56,220 So just to show you the use of this language, how easy it is, 444 00:25:56,220 --> 00:25:56,960 call that flatten. 445 00:25:56,960 --> 00:26:13,020 And I can define to flatten this stream of streams. Well, 446 00:26:13,020 --> 00:26:13,960 what is that? 447 00:26:13,960 --> 00:26:16,240 That's just an accumulation. 448 00:26:16,240 --> 00:26:25,240 I want to accumulate using append, by 449 00:26:25,240 --> 00:26:26,450 successively appending. 450 00:26:26,450 --> 00:26:36,590 So I accumulate using append streams, starting with 451 00:26:36,590 --> 00:26:54,370 the-empty-stream down that stream of streams. 452 00:26:54,370 --> 00:26:58,290 OK, so there's an example of how you can start using these 453 00:26:58,290 --> 00:27:00,830 higher order things to do some interesting operations. 454 00:27:00,830 --> 00:27:04,230 In fact, there's another useful thing 455 00:27:04,230 --> 00:27:05,100 that I want to do. 456 00:27:05,100 --> 00:27:18,700 I want to define a procedure called flat-map, flat map of 457 00:27:18,700 --> 00:27:21,840 some function and a stream. 458 00:27:21,840 --> 00:27:23,920 And what this is going to do is f will 459 00:27:23,920 --> 00:27:25,720 be a stream of elements. 460 00:27:25,720 --> 00:27:28,930 f is going to be a function that for each element in the 461 00:27:28,930 --> 00:27:31,950 stream produces another stream. 462 00:27:31,950 --> 00:27:33,950 And what I want to do is take all of the elements and all of 463 00:27:33,950 --> 00:27:36,000 those streams and combine them together. 464 00:27:36,000 --> 00:27:51,350 So that's just going to be the flatten of map f down s. 465 00:27:51,350 --> 00:27:54,290 Each time I apply f to an element of s, I get a stream. 466 00:27:54,290 --> 00:27:56,690 If I map it all the way down, I get a stream of streams, and 467 00:27:56,690 --> 00:27:58,385 I'll flatten that. 468 00:27:58,385 --> 00:28:04,670 Well, I want to use that to show you a new way to do a 469 00:28:04,670 --> 00:28:06,360 familiar kind of problem. 470 00:28:06,360 --> 00:28:12,310 The problem's going to be like a lot of problems you've seen, 471 00:28:12,310 --> 00:28:14,190 although maybe not this particular one. 472 00:28:14,190 --> 00:28:15,490 I'm going to give you an integer, n. 473 00:28:18,480 --> 00:28:31,020 And the problem is going to be find all pairs and integers i 474 00:28:31,020 --> 00:28:42,740 and j, between 0 and i, with j less than i, up to n, such 475 00:28:42,740 --> 00:28:51,910 that i plus j is prime. 476 00:28:55,740 --> 00:29:00,520 So for example, if n equals 6, let's make a little table 477 00:29:00,520 --> 00:29:06,640 here, i and j and i plus j. 478 00:29:09,700 --> 00:29:15,520 So for, say, i equals 2 and j equals 1, I'd get 3. 479 00:29:15,520 --> 00:29:18,940 And for i equals 3, I could have j equals 2, and that 480 00:29:18,940 --> 00:29:21,210 would be 5. 481 00:29:21,210 --> 00:29:28,400 And 4 and 1 would be 5 and so on, up until i goes to 6. 482 00:29:28,400 --> 00:29:33,640 And what I'd like to return is to produce a stream of all the 483 00:29:33,640 --> 00:29:37,350 triples like this, let's say i, j, and i plus j. 484 00:29:37,350 --> 00:29:41,530 So for each n, I want to generate this stream. 485 00:29:41,530 --> 00:29:43,680 OK, well, that's easy. 486 00:29:43,680 --> 00:29:47,230 Let's build it up. 487 00:29:47,230 --> 00:29:50,150 We start like this. 488 00:29:50,150 --> 00:29:55,510 We're going to say for each i, we're going 489 00:29:55,510 --> 00:29:56,440 to generate a stream. 490 00:29:56,440 --> 00:29:58,830 For each i in the interval 1 through n, we're going to 491 00:29:58,830 --> 00:30:00,660 generate a stream. 492 00:30:00,660 --> 00:30:02,230 What's that stream going to be? 493 00:30:02,230 --> 00:30:04,180 We're going to start by generating all the pairs. 494 00:30:04,180 --> 00:30:11,840 So for each i, we're going to generate, for each j in the 495 00:30:11,840 --> 00:30:19,450 interval 1 to i minus 1, we'll generate the pair, or the list 496 00:30:19,450 --> 00:30:20,710 with two elements i and j. 497 00:30:23,780 --> 00:30:30,712 So we map along the interval, generating the pairs. 498 00:30:30,712 --> 00:30:33,170 And for each i, that generates a stream of pairs. 499 00:30:33,170 --> 00:30:34,590 And we flatmap it. 500 00:30:34,590 --> 00:30:37,390 Now we have all the pairs i and j, such that i 501 00:30:37,390 --> 00:30:38,730 is less than j. 502 00:30:38,730 --> 00:30:39,850 So that builds that. 503 00:30:39,850 --> 00:30:42,990 Now we're got to test them. 504 00:30:42,990 --> 00:30:47,160 Well, we take that thing we just built, the flatmap, and 505 00:30:47,160 --> 00:30:50,090 we filter it to see whether the i-- 506 00:30:50,090 --> 00:30:51,660 see, we had an i and a j. 507 00:30:51,660 --> 00:30:55,180 i was the first thing in the list, j was the second thing 508 00:30:55,180 --> 00:30:59,030 in the list. So we have a predicate which says in that 509 00:30:59,030 --> 00:31:00,870 list of two elements is the sum of the 510 00:31:00,870 --> 00:31:02,070 CAR and the CDR prime. 511 00:31:02,070 --> 00:31:06,540 And we filter that collection of pairs we just built. 512 00:31:06,540 --> 00:31:09,420 So those are the pairs we want. 513 00:31:09,420 --> 00:31:13,340 Now we go ahead and we take the result of that filter and 514 00:31:13,340 --> 00:31:19,610 we map along it, generating the list i and j and i plus j. 515 00:31:19,610 --> 00:31:22,910 And that's our procedure prime-sum-pairs. 516 00:31:22,910 --> 00:31:24,480 And then just to flash it up, here's the whole procedure. 517 00:31:27,945 --> 00:31:30,750 A map, a filter, a flatmap. 518 00:31:34,850 --> 00:31:36,350 There's the whole thing, even though this isn't 519 00:31:36,350 --> 00:31:37,120 particularly readable. 520 00:31:37,120 --> 00:31:40,000 It's just expanding that flatmap. 521 00:31:40,000 --> 00:31:45,090 So there's an example which illustrates the general point 522 00:31:45,090 --> 00:31:49,350 that nested loops in this procedure start looking like 523 00:31:49,350 --> 00:31:52,370 compositions of flatmaps of flatmaps of flatmaps of maps 524 00:31:52,370 --> 00:31:54,200 and things. 525 00:31:54,200 --> 00:31:57,900 So not only can we enumerate individual things, but by 526 00:31:57,900 --> 00:32:00,890 using flatmaps, we can do what would correspond to nested 527 00:32:00,890 --> 00:32:03,230 loops in most other languages. 528 00:32:03,230 --> 00:32:06,870 Of course, it's pretty awful to keep writing these flatmaps 529 00:32:06,870 --> 00:32:08,410 of flatmaps of flatmaps. 530 00:32:08,410 --> 00:32:13,830 Prime-sum-pairs you saw looked fairly complicated, even 531 00:32:13,830 --> 00:32:15,480 though the individual pieces were easy. 532 00:32:15,480 --> 00:32:17,800 So what you can do, if you like, is introduced some 533 00:32:17,800 --> 00:32:21,040 syntactic sugar that's called collect. 534 00:32:21,040 --> 00:32:23,570 And collect is just an abbreviation for that nest of 535 00:32:23,570 --> 00:32:26,160 flatmaps and filters arranged in that particular way. 536 00:32:26,160 --> 00:32:29,620 Here's prime-sum-pairs again, written using collect. 537 00:32:29,620 --> 00:32:32,670 It says to find all those pairs, I'm going to collect 538 00:32:32,670 --> 00:32:40,910 together a result, which is the list i, j, and i plus j, 539 00:32:40,910 --> 00:32:44,510 that's going to be generated as i runs through the interval 540 00:32:44,510 --> 00:32:51,440 from 1 to n and as j runs through the interval from 1 to 541 00:32:51,440 --> 00:32:58,040 i minus 1, such that i plus j is prime. 542 00:32:58,040 --> 00:33:00,690 So I'm not going to say what collect does in general. 543 00:33:00,690 --> 00:33:03,420 You can look at that by looking at it in the book. 544 00:33:03,420 --> 00:33:06,010 But pretty much, you can see that the pieces of this are 545 00:33:06,010 --> 00:33:08,820 the pieces of that original procedure I wrote. 546 00:33:08,820 --> 00:33:11,550 And this collect is just some syntactic sugar for 547 00:33:11,550 --> 00:33:16,310 automatically generating that nest of flatmaps and flatmaps. 548 00:33:16,310 --> 00:33:21,120 OK, well, let me do one more example that shows you the 549 00:33:21,120 --> 00:33:22,120 same kind of thing. 550 00:33:22,120 --> 00:33:25,740 Here's a very famous problem that's used to illustrate a 551 00:33:25,740 --> 00:33:28,980 lot of so-called backtracking computer algorithms. This is 552 00:33:28,980 --> 00:33:30,200 the eight queens problem. 553 00:33:30,200 --> 00:33:32,370 This is a chess board. 554 00:33:32,370 --> 00:33:34,570 And the eight queens problem says, find a way to put down 555 00:33:34,570 --> 00:33:37,660 eight queens on a chess board so that no two are attacking 556 00:33:37,660 --> 00:33:38,000 each other. 557 00:33:38,000 --> 00:33:39,685 And here's a particular solution to the 558 00:33:39,685 --> 00:33:41,430 eight queens problem. 559 00:33:41,430 --> 00:33:44,450 So I have to make sure to put down queens so that no two are 560 00:33:44,450 --> 00:33:48,570 in the same row or the same column or sit 561 00:33:48,570 --> 00:33:51,410 along the same diagonal. 562 00:33:51,410 --> 00:33:56,400 Now, there's sort of a standard way of doing that. 563 00:33:59,740 --> 00:34:03,200 Well, first we need to do is below the 564 00:34:03,200 --> 00:34:04,940 surface, at George's level. 565 00:34:04,940 --> 00:34:07,340 We have to find some way to represent a board, and 566 00:34:07,340 --> 00:34:08,095 represent positions. 567 00:34:08,095 --> 00:34:09,800 And we'll not worry about that. 568 00:34:09,800 --> 00:34:12,540 But let's assume that there's a predicate called safe. 569 00:34:16,040 --> 00:34:19,090 And what safe is going to do is going to say given that I 570 00:34:19,090 --> 00:34:22,520 have a bunch of queens down on the chess board, is it OK to 571 00:34:22,520 --> 00:34:25,400 put a queen in this particular spot? 572 00:34:25,400 --> 00:34:32,889 So safe is going to take a row and a column. 573 00:34:32,889 --> 00:34:34,510 That's going to be a place where I'm going to try and put 574 00:34:34,510 --> 00:34:42,370 down the next queen, and the rest of positions. 575 00:34:45,420 --> 00:34:48,679 And what safe will say is given that I already have 576 00:34:48,679 --> 00:34:53,920 queens down in these positions, is it safe to put 577 00:34:53,920 --> 00:34:58,300 another queen down in that row and that column? 578 00:34:58,300 --> 00:34:59,360 And let's not worry about that. 579 00:34:59,360 --> 00:35:01,380 That's George's problem. and it's not hard to write. 580 00:35:01,380 --> 00:35:06,350 You just have to check whether this thing contains any things 581 00:35:06,350 --> 00:35:10,530 on that row or that column or in that diagonal. 582 00:35:10,530 --> 00:35:13,590 Now, how would you organize the program given that? 583 00:35:13,590 --> 00:35:18,010 And there's sort of a traditional way to organize it 584 00:35:18,010 --> 00:35:20,116 called backtracking. 585 00:35:20,116 --> 00:35:27,570 And it says, well, let's think about all the ways of putting 586 00:35:27,570 --> 00:35:31,290 the first queen down in the first column. 587 00:35:31,290 --> 00:35:32,580 There are eight ways. 588 00:35:32,580 --> 00:35:35,880 Well, let's say try the first column. 589 00:35:35,880 --> 00:35:37,300 Try column 1, row 1. 590 00:35:37,300 --> 00:35:41,300 These branches are going to represent the possibilities at 591 00:35:41,300 --> 00:35:43,360 each level. 592 00:35:43,360 --> 00:35:45,875 So I'll try and put a queen down in the first column. 593 00:35:45,875 --> 00:35:48,360 And now given that it's in the first column, I'll try and put 594 00:35:48,360 --> 00:35:49,980 the next queen down in the first column. 595 00:35:53,035 --> 00:35:55,470 I'll try and put the first queen, the one in the first 596 00:35:55,470 --> 00:35:56,920 column, down in the first row. 597 00:35:56,920 --> 00:35:59,050 I'm sorry. 598 00:35:59,050 --> 00:36:00,780 And then given that, we'll put the next queen down 599 00:36:00,780 --> 00:36:01,390 in the first row. 600 00:36:01,390 --> 00:36:02,090 And that's no good. 601 00:36:02,090 --> 00:36:04,200 So I'll back up to here. 602 00:36:04,200 --> 00:36:06,280 And I'll say, oh, can I put the first queen down in the 603 00:36:06,280 --> 00:36:07,510 second row? 604 00:36:07,510 --> 00:36:08,550 Well, that's no good. 605 00:36:08,550 --> 00:36:09,760 Oh, can I put it down in the third row? 606 00:36:09,760 --> 00:36:12,790 Well, that's good. 607 00:36:12,790 --> 00:36:14,290 Well, now can I put the next queen down 608 00:36:14,290 --> 00:36:15,380 in the first column? 609 00:36:15,380 --> 00:36:18,030 Well, I can't visualize this chess board anymore, but I 610 00:36:18,030 --> 00:36:19,195 think that's right. 611 00:36:19,195 --> 00:36:20,450 And I try the next one. 612 00:36:20,450 --> 00:36:24,170 And at each place, I go as far down this tree as I can. 613 00:36:24,170 --> 00:36:25,640 And I back up. 614 00:36:25,640 --> 00:36:28,970 If I get down to here and find no possibilities below there, 615 00:36:28,970 --> 00:36:31,740 I back all the way up to here, and now start again generating 616 00:36:31,740 --> 00:36:33,260 this sub-tree. 617 00:36:33,260 --> 00:36:35,050 And I sort of walk around. 618 00:36:35,050 --> 00:36:37,870 And finally, if I ever manage to get all the way down, I've 619 00:36:37,870 --> 00:36:40,090 found a solution. 620 00:36:40,090 --> 00:36:45,020 So that's a typical sort of paradigm that's used a lot in 621 00:36:45,020 --> 00:36:45,930 AI programming. 622 00:36:45,930 --> 00:36:47,300 It's called backtracking search. 623 00:36:57,470 --> 00:37:03,860 And it's really unnecessary. 624 00:37:03,860 --> 00:37:06,550 You saw me get confused when I was visualizing this thing. 625 00:37:06,550 --> 00:37:08,550 And you see the complication. 626 00:37:08,550 --> 00:37:10,760 This is a complicated thing to say. 627 00:37:10,760 --> 00:37:12,390 Why is it complicated? 628 00:37:12,390 --> 00:37:16,190 Its because somehow this program is too inordinately 629 00:37:16,190 --> 00:37:18,580 concerned with time. 630 00:37:18,580 --> 00:37:19,200 It's too much-- 631 00:37:19,200 --> 00:37:21,670 I try this one, and I try this one, and I go back to the last 632 00:37:21,670 --> 00:37:22,320 possibility. 633 00:37:22,320 --> 00:37:24,340 And that's a complicated thing. 634 00:37:24,340 --> 00:37:28,590 If I stop worrying about time so much, then there's a much 635 00:37:28,590 --> 00:37:31,200 simpler way to describe this. 636 00:37:31,200 --> 00:37:40,320 It says, let's imagine that I have in my hands the tree down 637 00:37:40,320 --> 00:37:43,400 to k minus 1 levels. 638 00:37:43,400 --> 00:37:50,670 See, suppose I had in my hands all possible ways to put down 639 00:37:50,670 --> 00:37:53,560 queens in the first k columns. 640 00:37:53,560 --> 00:37:54,610 Suppose I just had that. 641 00:37:54,610 --> 00:37:57,070 Let's not worry about how we get it. 642 00:37:57,070 --> 00:37:59,200 Well, then, how do I extend that? 643 00:37:59,200 --> 00:38:01,420 How do I find all possible ways to put down queens in the 644 00:38:01,420 --> 00:38:02,480 next column? 645 00:38:02,480 --> 00:38:03,620 It's really easy. 646 00:38:03,620 --> 00:38:12,210 For each of these positions I have, I think about putting 647 00:38:12,210 --> 00:38:16,160 down a queen in each row to make the next thing. 648 00:38:16,160 --> 00:38:18,930 And then for each one I put down, I filter those by the 649 00:38:18,930 --> 00:38:22,080 ones that are safe. 650 00:38:22,080 --> 00:38:24,190 So instead of thinking about this tree as generated step by 651 00:38:24,190 --> 00:38:26,860 step, suppose I had it all there. 652 00:38:29,680 --> 00:38:32,990 And to extend it from level k minus 1 to level k, I just 653 00:38:32,990 --> 00:38:36,840 need to extend each thing in all possible ways and only 654 00:38:36,840 --> 00:38:37,800 keep the ones that are safe. 655 00:38:37,800 --> 00:38:39,300 And that will give me the tree to level k. 656 00:38:39,300 --> 00:38:41,675 And that's a recursive strategy for solving the eight 657 00:38:41,675 --> 00:38:44,530 queens problem. 658 00:38:44,530 --> 00:38:45,780 All right, well, let's look at it. 659 00:38:50,280 --> 00:38:54,360 To solve the eight queens problem on a board of some 660 00:38:54,360 --> 00:39:00,390 specified size, we write a sub-procedure called 661 00:39:00,390 --> 00:39:01,030 fill-columns. 662 00:39:01,030 --> 00:39:04,050 Fill-columns is going to put down queens up 663 00:39:04,050 --> 00:39:06,086 through column k. 664 00:39:06,086 --> 00:39:07,700 And here's the pattern of the recursion. 665 00:39:07,700 --> 00:39:12,990 I'm going to call fill-columns with the size eventually. 666 00:39:12,990 --> 00:39:15,630 So fill-columns says how to put down queens safely in the 667 00:39:15,630 --> 00:39:19,255 first k columns of this chess board with a size number of 668 00:39:19,255 --> 00:39:20,360 rows in it. 669 00:39:20,360 --> 00:39:22,946 If k is equal to 0, well, then I don't have to 670 00:39:22,946 --> 00:39:23,940 put anything down. 671 00:39:23,940 --> 00:39:26,710 So my solution is just an empty chess board. 672 00:39:26,710 --> 00:39:28,070 Otherwise, I'm going to do some stuff. 673 00:39:28,070 --> 00:39:30,522 And I'm going to use collect. 674 00:39:30,522 --> 00:39:31,772 And here's the collect. 675 00:39:34,530 --> 00:39:40,590 I find all ways to put down queens in the 676 00:39:40,590 --> 00:39:41,910 first k minus 1 columns. 677 00:39:41,910 --> 00:39:43,320 And this was just what I set for. 678 00:39:43,320 --> 00:39:48,880 Imagine I have this tree down to k minus 1 levels. 679 00:39:48,880 --> 00:39:53,230 And then I find all ways of trying a row, that's just each 680 00:39:53,230 --> 00:39:54,130 of the possible rows. 681 00:39:54,130 --> 00:39:58,040 They're size rows, so that's enumerate interval. 682 00:39:58,040 --> 00:40:03,950 And now what I do is I collect together the new row I'm going 683 00:40:03,950 --> 00:40:08,950 to try and column k with the rest of the queens. 684 00:40:08,950 --> 00:40:10,200 I adjoin a position. 685 00:40:10,200 --> 00:40:11,290 This is George's problem. 686 00:40:11,290 --> 00:40:13,640 An adjoined position is like safe. 687 00:40:13,640 --> 00:40:16,530 It's a thing that takes a row and a column and the rest of 688 00:40:16,530 --> 00:40:19,660 the positions and makes a new position collection. 689 00:40:19,660 --> 00:40:26,230 So I adjoin a position of a new row and a new column to 690 00:40:26,230 --> 00:40:30,310 the rest of the queens, where the rest of the queens runs 691 00:40:30,310 --> 00:40:32,870 through all possible ways of solving the problem 692 00:40:32,870 --> 00:40:34,620 in k minus 1 columns. 693 00:40:34,620 --> 00:40:39,730 And the new row runs through all possible rows such that it 694 00:40:39,730 --> 00:40:43,240 was safe to put one there. 695 00:40:43,240 --> 00:40:46,500 And that's the whole program. 696 00:40:46,500 --> 00:40:49,840 There's the whole procedure. 697 00:40:49,840 --> 00:40:51,990 Not only that, that doesn't just solve the eight queens 698 00:40:51,990 --> 00:40:56,010 problem, it gives you all solutions to the 699 00:40:56,010 --> 00:40:56,680 eight queens problem. 700 00:40:56,680 --> 00:40:58,480 When you're done, you have a stream. 701 00:40:58,480 --> 00:41:00,650 And the elements of that stream are all possible ways 702 00:41:00,650 --> 00:41:01,900 of solving that problem. 703 00:41:05,310 --> 00:41:06,260 Why is that simpler? 704 00:41:06,260 --> 00:41:10,170 Well, we threw away the whole idea that this is some process 705 00:41:10,170 --> 00:41:12,720 that happens in time with state. 706 00:41:12,720 --> 00:41:14,420 And we just said it's a whole collection of stuff. 707 00:41:14,420 --> 00:41:18,260 And that's why it's simpler. 708 00:41:18,260 --> 00:41:20,110 We've changed our view. 709 00:41:20,110 --> 00:41:22,820 Remember, that's where we started today. 710 00:41:22,820 --> 00:41:26,230 We've changed our view of what it is we're trying to model. 711 00:41:26,230 --> 00:41:30,570 we stop modeling things that evolve in time and have steps 712 00:41:30,570 --> 00:41:31,750 and have state. 713 00:41:31,750 --> 00:41:33,990 And instead, we're trying to model this global thing like 714 00:41:33,990 --> 00:41:37,950 the whole flight of the chalk, rather than its 715 00:41:37,950 --> 00:41:40,750 state at each instant. 716 00:41:40,750 --> 00:41:42,000 Any questions? 717 00:41:43,810 --> 00:41:46,190 AUDIENCE: It looks to me like backtracking would be 718 00:41:46,190 --> 00:41:49,970 searching for the first solution it can find, whereas 719 00:41:49,970 --> 00:41:54,030 this recursive search would be looking for all solutions. 720 00:41:54,030 --> 00:41:58,090 And it seems that if you have a large enough area to search, 721 00:41:58,090 --> 00:42:01,360 that the second is going to become impossible. 722 00:42:01,360 --> 00:42:07,610 PROFESSOR: OK, the answer to that question is the whole 723 00:42:07,610 --> 00:42:08,570 rest of this lecture. 724 00:42:08,570 --> 00:42:10,540 It's exactly the right question. 725 00:42:13,522 --> 00:42:15,540 And without trying to anticipate the lecture too 726 00:42:15,540 --> 00:42:19,910 much, you should start being suspicious at this point, and 727 00:42:19,910 --> 00:42:22,220 exactly those kinds of suspicions. 728 00:42:22,220 --> 00:42:24,830 It's wonderful, but isn't it so terribly inefficient? 729 00:42:24,830 --> 00:42:28,100 That's where we're going. 730 00:42:28,100 --> 00:42:30,020 So I won't answer now, but I'll answer later. 731 00:42:33,350 --> 00:42:34,600 OK, let's take a break. 732 00:43:29,650 --> 00:43:35,600 Well, by now you should be starting to get suspicious. 733 00:43:35,600 --> 00:43:41,450 See, I've showed your this simple, elegant way of putting 734 00:43:41,450 --> 00:43:46,440 programs together, very unlike these other traditional 735 00:43:46,440 --> 00:43:50,490 programs that sum the odd squares or compute the odd 736 00:43:50,490 --> 00:43:53,740 Fibonacci numbers. 737 00:43:53,740 --> 00:43:57,080 Very unlike these programs that mix up the enumerator and 738 00:43:57,080 --> 00:44:00,440 the filter and the accumulator. 739 00:44:00,440 --> 00:44:04,770 And by mixing it up, we don't have all of these wonderful 740 00:44:04,770 --> 00:44:07,990 conceptual advantages of these streams pieces, these 741 00:44:07,990 --> 00:44:09,840 wonderful mix and match components for putting 742 00:44:09,840 --> 00:44:13,800 together lots and lots of programs. 743 00:44:13,800 --> 00:44:15,810 On the other hand, most of the programs you've seen look like 744 00:44:15,810 --> 00:44:18,340 these ugly ones. 745 00:44:18,340 --> 00:44:19,460 Why's that? 746 00:44:19,460 --> 00:44:23,705 Can it possibly be that computer scientists are so 747 00:44:23,705 --> 00:44:28,370 obtuse that they don't notice that if you'd merely did this 748 00:44:28,370 --> 00:44:33,620 thing, then you can get this great programming elegance? 749 00:44:33,620 --> 00:44:36,760 There's got to be a catch. 750 00:44:36,760 --> 00:44:39,510 And it's actually pretty easy to see what the catch is. 751 00:44:39,510 --> 00:44:42,030 Let's think about the following problem. 752 00:44:42,030 --> 00:44:47,510 Suppose I tell you to find the second prime between 10,000 753 00:44:47,510 --> 00:44:51,020 and 1 million, or if your computer's larger, say between 754 00:44:51,020 --> 00:44:54,105 10,000 and 100 billion, or something. 755 00:44:54,105 --> 00:44:55,550 And you say, oh, that's easy. 756 00:44:55,550 --> 00:44:57,080 I can do that with a stream. 757 00:44:57,080 --> 00:45:01,530 All I do is I enumerate the interval 758 00:45:01,530 --> 00:45:04,160 from 10,000 to 1 million. 759 00:45:04,160 --> 00:45:06,800 So I get all those integers from 10,000 to 1 million. 760 00:45:06,800 --> 00:45:10,520 I filter them for prime-ness, so test all of them and see if 761 00:45:10,520 --> 00:45:11,762 they're prime. 762 00:45:11,762 --> 00:45:13,170 And I take the second element. 763 00:45:13,170 --> 00:45:16,130 That's the head of the tail. 764 00:45:16,130 --> 00:45:17,380 Well, that's clearly pretty ridiculous. 765 00:45:21,660 --> 00:45:24,620 We'd not even have room in the machine to store the integers 766 00:45:24,620 --> 00:45:27,040 in the first place, much less to test them. 767 00:45:27,040 --> 00:45:29,810 And then I only want the second one. 768 00:45:29,810 --> 00:45:36,500 See, the power of this traditional programming style 769 00:45:36,500 --> 00:45:39,860 is exactly its weakness, that we're mixing up the 770 00:45:39,860 --> 00:45:45,090 enumerating and the testing and the accumulating. 771 00:45:45,090 --> 00:45:46,670 So we don't do it all. 772 00:45:46,670 --> 00:45:52,580 So the very thing that makes it conceptually ugly is the 773 00:45:52,580 --> 00:45:55,210 very thing that makes it efficient. 774 00:45:55,210 --> 00:45:57,800 It's this mixing up. 775 00:45:57,800 --> 00:45:59,840 So it seems that all I've done this morning so far is just 776 00:45:59,840 --> 00:46:00,420 confuse you. 777 00:46:00,420 --> 00:46:02,930 I showed you this wonderful way that programming might 778 00:46:02,930 --> 00:46:05,840 work, except that it doesn't. 779 00:46:05,840 --> 00:46:09,040 Well, here's where the wonderful thing happens. 780 00:46:09,040 --> 00:46:13,210 It turns out in this game that we really can have our cake 781 00:46:13,210 --> 00:46:14,870 and eat it too. 782 00:46:14,870 --> 00:46:20,280 And what I mean by that is that we really can write 783 00:46:20,280 --> 00:46:24,210 stream programs exactly like the ones I wrote and arrange 784 00:46:24,210 --> 00:46:28,830 things so that when the machine actually runs, it's as 785 00:46:28,830 --> 00:46:31,690 efficient as running this traditional programming style 786 00:46:31,690 --> 00:46:36,310 that mixes up the generation and the test. 787 00:46:36,310 --> 00:46:40,770 Well, that sounds pretty magic. 788 00:46:40,770 --> 00:46:43,690 The key to this is that streams are not lists. 789 00:46:48,090 --> 00:46:50,070 We'll see this carefully in a second, but for now, let's 790 00:46:50,070 --> 00:46:52,115 take a look at that slide again. 791 00:46:52,115 --> 00:46:55,060 The image you should have here of this signal processing 792 00:46:55,060 --> 00:47:00,940 system is that what's going to happen is there's this box 793 00:47:00,940 --> 00:47:05,360 that has the integers sitting in it. 794 00:47:05,360 --> 00:47:08,680 And there's this filter that's connected to it and it's 795 00:47:08,680 --> 00:47:10,940 tugging on them. 796 00:47:10,940 --> 00:47:13,680 And then there's someone who's tugging on this stuff saying 797 00:47:13,680 --> 00:47:16,790 what comes out of the filter. 798 00:47:16,790 --> 00:47:19,630 And the image you should have is that someone says, well, 799 00:47:19,630 --> 00:47:24,590 what's the first prime, and tugs on this filter. 800 00:47:24,590 --> 00:47:28,020 And the filter tugs on the integers. 801 00:47:28,020 --> 00:47:29,830 And you look only at that much, and then say, oh, I 802 00:47:29,830 --> 00:47:30,930 really wanted the second one. 803 00:47:30,930 --> 00:47:33,710 What's the second prime? 804 00:47:33,710 --> 00:47:37,730 And that no computation gets done except when you tug on 805 00:47:37,730 --> 00:47:40,500 these things. 806 00:47:40,500 --> 00:47:41,410 Let me try that again. 807 00:47:41,410 --> 00:47:43,815 This is a little device. 808 00:47:43,815 --> 00:47:46,400 This is a little stream machine invented by Eric 809 00:47:46,400 --> 00:47:49,830 Grimson who's been teaching this course at MIT. 810 00:47:49,830 --> 00:47:52,940 And the image is here's a stream of stuff, like a whole 811 00:47:52,940 --> 00:47:54,780 bunch of the integers. 812 00:47:54,780 --> 00:47:58,700 And here's some processing elements. 813 00:47:58,700 --> 00:48:02,600 And if, say, it's filter of filter of map, or something. 814 00:48:05,570 --> 00:48:08,760 And if I really tried to implement that with streams as 815 00:48:08,760 --> 00:48:11,520 lists, what I'd say is, well, I've got this list of things, 816 00:48:11,520 --> 00:48:12,670 and now I do the first filter. 817 00:48:12,670 --> 00:48:14,070 So do all this processing. 818 00:48:14,070 --> 00:48:18,570 And I take this and I process and I process and I process 819 00:48:18,570 --> 00:48:19,610 and I process. 820 00:48:19,610 --> 00:48:21,910 And now I'm got this new stream. 821 00:48:21,910 --> 00:48:24,070 Now I take that result in my hand someplace. 822 00:48:24,070 --> 00:48:25,260 And I put that through the second one. 823 00:48:25,260 --> 00:48:28,110 And I process the whole thing. 824 00:48:28,110 --> 00:48:29,510 And there's this new stream. 825 00:48:32,130 --> 00:48:35,230 And then I take the result and I put it all the way through 826 00:48:35,230 --> 00:48:36,360 this one the same way. 827 00:48:36,360 --> 00:48:41,760 That's what would happen to these stream programs if 828 00:48:41,760 --> 00:48:43,860 streams were just lists. 829 00:48:43,860 --> 00:48:46,065 But in fact, streams aren't lists, they're streams. And 830 00:48:46,065 --> 00:48:47,240 the image you should have is something a little 831 00:48:47,240 --> 00:48:50,230 bit more like this. 832 00:48:50,230 --> 00:48:55,880 I've got these gadgets connected up by this data 833 00:48:55,880 --> 00:48:57,130 that's flowing out of them. 834 00:48:59,960 --> 00:49:04,190 And here's my original source of the streams. It might be 835 00:49:04,190 --> 00:49:05,980 starting to generate the integers. 836 00:49:05,980 --> 00:49:07,580 And now, what happens if I want a result? 837 00:49:07,580 --> 00:49:10,200 I tug on the end here. 838 00:49:10,200 --> 00:49:13,090 And this element says, gee, I need some more data. 839 00:49:13,090 --> 00:49:15,830 So this one comes here and tugs on that one. 840 00:49:15,830 --> 00:49:17,890 And it says, gee, I need some more data. 841 00:49:17,890 --> 00:49:19,960 And this one tugs on this thing, which might be a 842 00:49:19,960 --> 00:49:21,640 filter, and says, gee, I need some more data. 843 00:49:21,640 --> 00:49:24,755 And only as much of this thing at the end here gets generated 844 00:49:24,755 --> 00:49:25,780 as I tugged. 845 00:49:25,780 --> 00:49:28,030 And only as much of this stuff goes through the processing 846 00:49:28,030 --> 00:49:30,760 units as I'm pulling on the end I need. 847 00:49:30,760 --> 00:49:33,720 That's the image you should have of the difference between 848 00:49:33,720 --> 00:49:36,580 implementing what we're actually going to do and if 849 00:49:36,580 --> 00:49:37,830 streams were lists. 850 00:49:40,600 --> 00:49:42,430 Well, how do we make this thing? 851 00:49:42,430 --> 00:49:43,400 I hope you have the image. 852 00:49:43,400 --> 00:49:44,947 The trick is how to make it. 853 00:49:47,930 --> 00:49:52,080 We want to arrange for a stream to be a data structure 854 00:49:52,080 --> 00:49:55,670 that computes itself incrementally, an on-demand 855 00:49:55,670 --> 00:49:56,920 data structure. 856 00:49:59,220 --> 00:50:02,700 And the basic idea is, again, one of the very basic ideas 857 00:50:02,700 --> 00:50:04,490 that we're seeing throughout the whole course. 858 00:50:04,490 --> 00:50:07,440 And that is that there's not a firm distinction between 859 00:50:07,440 --> 00:50:09,240 programs and data. 860 00:50:09,240 --> 00:50:12,260 So what a stream is going to be is simultaneously this data 861 00:50:12,260 --> 00:50:15,270 structure that you think of, like the stream of the leaves 862 00:50:15,270 --> 00:50:16,810 of this tree. 863 00:50:16,810 --> 00:50:18,880 But at the same time, it's going to be a very clever 864 00:50:18,880 --> 00:50:23,550 procedure that has the method of computing in it. 865 00:50:23,550 --> 00:50:25,930 Well, let me try this. 866 00:50:25,930 --> 00:50:28,460 It's going to turn out that we don't need any more mechanism. 867 00:50:28,460 --> 00:50:31,150 We already have everything we need simply from the fact that 868 00:50:31,150 --> 00:50:32,770 we know how to handle procedures 869 00:50:32,770 --> 00:50:35,460 as first-class objects. 870 00:50:35,460 --> 00:50:36,880 Well, let's go back to the key. 871 00:50:36,880 --> 00:50:39,030 The key is, remember, we had these operations. 872 00:50:39,030 --> 00:50:48,080 CONS-stream and head and tail. 873 00:50:48,080 --> 00:50:51,580 When I started, I said you can think about this as CONS and 874 00:50:51,580 --> 00:50:53,340 think about this as CAR and think about that as 875 00:50:53,340 --> 00:50:55,080 CDR, but it's not. 876 00:50:55,080 --> 00:50:57,550 Now, let's look at what they really are. 877 00:50:57,550 --> 00:51:09,360 Well, CONS-stream of x and y is going to be an abbreviation 878 00:51:09,360 --> 00:51:19,540 for the following thing. 879 00:51:19,540 --> 00:51:24,470 CONS form a pair, ordinary CONS, of x to a thing called 880 00:51:24,470 --> 00:51:28,000 delay of y. 881 00:51:31,188 --> 00:51:34,670 And before I explain that, let me go and write the rest. The 882 00:51:34,670 --> 00:51:39,790 head of a stream is going to be just the CAR. 883 00:51:42,380 --> 00:51:47,610 And the tail of a stream is going to be a thing called 884 00:51:47,610 --> 00:51:56,120 force the CDR of the stream. 885 00:51:56,120 --> 00:51:58,060 Now let me explain this. 886 00:51:58,060 --> 00:52:01,420 Delay is going to be a special magic thing. 887 00:52:01,420 --> 00:52:06,240 What delay does is take an expression and produce a 888 00:52:06,240 --> 00:52:08,380 promise to compute that expression 889 00:52:08,380 --> 00:52:10,600 when you ask for it. 890 00:52:10,600 --> 00:52:11,980 It doesn't do any computation here. 891 00:52:11,980 --> 00:52:14,820 It just gives you a rain check. 892 00:52:14,820 --> 00:52:17,110 It produces a promise. 893 00:52:17,110 --> 00:52:23,280 And CONS-stream says I'm going to put together in a pair x 894 00:52:23,280 --> 00:52:25,360 and a promise to compute y. 895 00:52:28,230 --> 00:52:30,200 Now, if I want the head, that's just the CAR that I put 896 00:52:30,200 --> 00:52:31,840 in the pair. 897 00:52:31,840 --> 00:52:34,350 And the key is that the tail is going to be-- 898 00:52:34,350 --> 00:52:39,110 force calls in that promise. 899 00:52:39,110 --> 00:52:43,690 Tail says, well, take that promise and now 900 00:52:43,690 --> 00:52:44,610 call in that promise. 901 00:52:44,610 --> 00:52:47,430 And then we compute that thing. 902 00:52:47,430 --> 00:52:48,740 That's how this is going to work. 903 00:52:48,740 --> 00:52:51,550 That's what CONS-stream, head, and tail really are. 904 00:52:54,196 --> 00:52:55,570 Now, let's see how this works. 905 00:52:55,570 --> 00:52:58,410 And we'll go through this fairly carefully. 906 00:52:58,410 --> 00:53:01,990 We're going to see how this works in this example of 907 00:53:01,990 --> 00:53:08,650 computing the second prime between 10,000 and a million. 908 00:53:08,650 --> 00:53:11,610 OK, so we start off and we have this expression. 909 00:53:15,820 --> 00:53:20,380 The second prime-- the head of the tail of the result of 910 00:53:20,380 --> 00:53:24,060 filtering for primality the integers between 911 00:53:24,060 --> 00:53:26,710 10,000 and 1 million. 912 00:53:26,710 --> 00:53:28,400 Now, what is that? 913 00:53:28,400 --> 00:53:35,790 What that is, that interval between 10,000 and 1 million, 914 00:53:35,790 --> 00:53:37,480 well, if you trace through enumerate interval, there 915 00:53:37,480 --> 00:53:40,250 builds a CONS-stream. 916 00:53:40,250 --> 00:53:45,880 And the CONS-stream is the CONS of 10,000 to a promise to 917 00:53:45,880 --> 00:53:54,480 compute the integers between 10,001 and 1 million. 918 00:53:54,480 --> 00:53:55,750 So that's what this expression is. 919 00:53:55,750 --> 00:53:57,640 Here I'm using the substitution model. 920 00:53:57,640 --> 00:53:59,690 And we can use the substitution model because we 921 00:53:59,690 --> 00:54:01,010 don't have side effects and state. 922 00:54:04,270 --> 00:54:07,860 So I have CONS of 10,000 to a promise to compute the rest of 923 00:54:07,860 --> 00:54:08,380 the integers. 924 00:54:08,380 --> 00:54:09,850 So only one integer, so far, got enumerated. 925 00:54:14,380 --> 00:54:16,580 Well, I'm going to filter that thing for primality. 926 00:54:19,900 --> 00:54:22,360 Again, you go back and look at the filter code. 927 00:54:22,360 --> 00:54:25,460 What the filter will first do is test the head. 928 00:54:25,460 --> 00:54:31,580 So in this case, the filter will test 10,000 and say, oh, 929 00:54:31,580 --> 00:54:33,500 10,000's not prime. 930 00:54:33,500 --> 00:54:36,260 Therefore, what I have to do recursively 931 00:54:36,260 --> 00:54:39,220 is filter the tail. 932 00:54:39,220 --> 00:54:42,550 And what's the tail of it, well, that's the tail of this 933 00:54:42,550 --> 00:54:46,340 pair with a promise in it. 934 00:54:46,340 --> 00:54:49,680 Tail now comes in and says, well, I'm going to force that. 935 00:54:49,680 --> 00:54:53,790 I'm going to force that promise, which means now I'm 936 00:54:53,790 --> 00:55:00,880 going to compute the integers between 10,001 and 1 million. 937 00:55:00,880 --> 00:55:02,970 OK, so this filter now is looking at that. 938 00:55:07,810 --> 00:55:10,100 That enumerate itself, well, now we're back in the original 939 00:55:10,100 --> 00:55:11,960 enumerate situation. 940 00:55:11,960 --> 00:55:16,920 The enumerate is the CONS of the first thing, 10,001, onto 941 00:55:16,920 --> 00:55:19,740 a promise to compute the rest. 942 00:55:19,740 --> 00:55:23,060 So now the primality filter is going to go look at 10,001. 943 00:55:23,060 --> 00:55:25,120 It's going to decide if it likes that or not. 944 00:55:25,120 --> 00:55:27,550 It turns out 10,001 isn't prime. 945 00:55:27,550 --> 00:55:29,610 So it'll force it again and again and again. 946 00:55:32,920 --> 00:55:37,100 And finally, I think the first prime it hits is 10,009. 947 00:55:37,100 --> 00:55:40,465 And at that point, it'll stop. 948 00:55:40,465 --> 00:55:42,500 And that will be the first prime, and then eventually, 949 00:55:42,500 --> 00:55:45,240 it'll need the second prime. 950 00:55:45,240 --> 00:55:47,030 So at that point, it will go again. 951 00:55:47,030 --> 00:55:51,880 So you see what happens is that no more gets generated 952 00:55:51,880 --> 00:55:53,130 than you actually need. 953 00:55:56,690 --> 00:56:00,060 That enumerator is not going to generate any more integers 954 00:56:00,060 --> 00:56:02,410 than the filter asks it for as it's pulling in things to 955 00:56:02,410 --> 00:56:04,930 check for primality. 956 00:56:04,930 --> 00:56:07,290 And the filter is not going to generate any more stuff than 957 00:56:07,290 --> 00:56:11,255 you ask it for, which is the head of the tail. 958 00:56:11,255 --> 00:56:17,180 You see, what's happened is we've put that mixing of 959 00:56:17,180 --> 00:56:20,130 generation and test into what actually happens in the 960 00:56:20,130 --> 00:56:24,250 computer, even though that's not apparently what's 961 00:56:24,250 --> 00:56:28,160 happening from looking at our programs. 962 00:56:28,160 --> 00:56:30,230 OK, well, that seemed easy. 963 00:56:30,230 --> 00:56:33,326 All of this mechanism got put into this magic delay. 964 00:56:33,326 --> 00:56:36,900 So you're saying, gee, that must be where the magic is. 965 00:56:36,900 --> 00:56:39,070 But see there's no magic there either. 966 00:56:39,070 --> 00:56:40,610 You know what delay is. 967 00:56:40,610 --> 00:56:50,040 Delay on some expression is just an abbreviation for-- 968 00:56:53,400 --> 00:56:56,490 well, what's a promise to compute an expression? 969 00:56:56,490 --> 00:57:00,700 Lambda of nil, procedure of no arguments, which is that 970 00:57:00,700 --> 00:57:03,000 expression. 971 00:57:03,000 --> 00:57:03,930 That's what a procedure is. 972 00:57:03,930 --> 00:57:06,050 It says I'm going to compute an expression. 973 00:57:06,050 --> 00:57:07,460 What's force? 974 00:57:07,460 --> 00:57:10,800 How do I take up a promise? 975 00:57:10,800 --> 00:57:15,890 Well, force of some procedure, a promise, is just run it. 976 00:57:18,710 --> 00:57:20,120 Done. 977 00:57:20,120 --> 00:57:23,580 So there's no magic there at all. 978 00:57:23,580 --> 00:57:26,440 Well, what have we done? 979 00:57:26,440 --> 00:57:29,510 We said the old style, traditional style of 980 00:57:29,510 --> 00:57:30,960 programming is more efficient. 981 00:57:30,960 --> 00:57:35,260 And the stream thing is more perspicuous. 982 00:57:35,260 --> 00:57:40,070 And we managed to make the stream procedures run like the 983 00:57:40,070 --> 00:57:43,350 other procedures by using delay. 984 00:57:43,350 --> 00:57:46,880 And the thing that delay did for us was to de-couple the 985 00:57:46,880 --> 00:57:52,150 apparent order of events in our programs from the actual 986 00:57:52,150 --> 00:57:54,440 order of events that happened in the machine. 987 00:57:54,440 --> 00:57:56,540 That's really what delay is doing. 988 00:57:56,540 --> 00:57:58,290 That's exactly the whole point. 989 00:57:58,290 --> 00:58:04,720 We've given up the idea that our procedures, as they run, 990 00:58:04,720 --> 00:58:09,182 or as we look at them, mirror some clear notion of time. 991 00:58:09,182 --> 00:58:12,960 And by giving that up, we give delay the freedom to arrange 992 00:58:12,960 --> 00:58:16,690 the order of events in the computation the way it likes. 993 00:58:16,690 --> 00:58:17,610 That's the whole idea. 994 00:58:17,610 --> 00:58:20,640 We de-couple the apparent order of events in our 995 00:58:20,640 --> 00:58:24,200 programs from the actual order of events in the computer. 996 00:58:24,200 --> 00:58:25,770 OK, well there's one more detail. 997 00:58:25,770 --> 00:58:27,750 It's just a technical detail, but it's actually 998 00:58:27,750 --> 00:58:29,730 an important one. 999 00:58:29,730 --> 00:58:32,190 As you run through these recursive programs unwinding, 1000 00:58:32,190 --> 00:58:35,360 you'll see a lot of things that look like tail of the 1001 00:58:35,360 --> 00:58:39,320 tail of the tail. 1002 00:58:39,320 --> 00:58:41,840 That's the kind of thing that would happen as I go CONSing 1003 00:58:41,840 --> 00:58:43,860 down a stream all the way. 1004 00:58:43,860 --> 00:58:47,170 And if each time I'm doing that, each time to compute a 1005 00:58:47,170 --> 00:58:51,830 tail, I evaluate a procedure which then has to go 1006 00:58:51,830 --> 00:58:54,270 re-compute its tail, and re-compute its tail and 1007 00:58:54,270 --> 00:58:56,380 recompute its tail each time, you can see that's very 1008 00:58:56,380 --> 00:58:59,610 inefficient compared to just having a list where the 1009 00:58:59,610 --> 00:59:02,510 elements are all there, and I don't have to re-compute each 1010 00:59:02,510 --> 00:59:05,290 tail every time I get the next tail. 1011 00:59:05,290 --> 00:59:15,030 So there's one little hack to slightly change what delay is, 1012 00:59:15,030 --> 00:59:17,380 and make it a thing which is-- 1013 00:59:17,380 --> 00:59:20,390 I'll write it this way. 1014 00:59:20,390 --> 00:59:27,360 The actual implementation, delay is an abbreviation for 1015 00:59:27,360 --> 00:59:31,000 this thing, memo-proc of a procedure. 1016 00:59:31,000 --> 00:59:35,150 Memo-proc is a special thing that transforms a procedure. 1017 00:59:35,150 --> 00:59:39,250 What it does is it takes a procedure of no arguments and 1018 00:59:39,250 --> 00:59:42,190 it transforms it into a procedure that'll only have to 1019 00:59:42,190 --> 00:59:44,806 do its computation once. 1020 00:59:44,806 --> 00:59:48,700 And what I mean by that is, you give it a procedure. 1021 00:59:48,700 --> 00:59:51,950 The result of memo-proc will be a new procedure, which the 1022 00:59:51,950 --> 00:59:55,370 first time you call it, will run the original procedure, 1023 00:59:55,370 --> 01:00:00,040 remember what result it got, and then from ever on after, 1024 01:00:00,040 --> 01:00:01,610 when you call it, it just won't have to do the 1025 01:00:01,610 --> 01:00:02,360 computation. 1026 01:00:02,360 --> 01:00:05,200 It will have cached that result someplace. 1027 01:00:05,200 --> 01:00:06,550 And here's an implementation of memo-proc. 1028 01:00:11,210 --> 01:00:12,710 Once you have the idea, it's easy to implement. 1029 01:00:12,710 --> 01:00:15,830 Memo-proc is this little thing that has two 1030 01:00:15,830 --> 01:00:17,390 little flags in there. 1031 01:00:17,390 --> 01:00:20,320 It says, have I already been run? 1032 01:00:20,320 --> 01:00:23,620 And initially it says, no, I haven't already been run. 1033 01:00:23,620 --> 01:00:29,070 And what was the result I got the last time I was run? 1034 01:00:29,070 --> 01:00:32,200 So memo-proc takes a procedure called proc, and it returns a 1035 01:00:32,200 --> 01:00:34,360 new procedure of no arguments. 1036 01:00:34,360 --> 01:00:38,610 Proc is supposed to be a procedure of no arguments. 1037 01:00:38,610 --> 01:00:42,970 And it says, oh, if I'm not already run, then I'm going to 1038 01:00:42,970 --> 01:00:44,430 do a sequence of things. 1039 01:00:44,430 --> 01:00:48,450 I'm going to compute proc, I'm going to save that. 1040 01:00:48,450 --> 01:00:51,140 I'm going to stash that in the variable result. 1041 01:00:51,140 --> 01:00:53,510 I'm going to make a note to myself that I've already been 1042 01:00:53,510 --> 01:00:56,610 run, and then I'll return the result. 1043 01:00:56,610 --> 01:00:59,010 So that's if you compute it if it's not already run. 1044 01:00:59,010 --> 01:01:01,040 If you call it and it's already been run, it just 1045 01:01:01,040 --> 01:01:03,420 returns the result. 1046 01:01:03,420 --> 01:01:08,400 So that's a little clever hack called memoization. 1047 01:01:08,400 --> 01:01:12,100 And in this case, it short circuits having to re-compute 1048 01:01:12,100 --> 01:01:15,270 the tail of the tail of the tail of the tail of the tail. 1049 01:01:15,270 --> 01:01:17,810 So there isn't even that kind of inefficiency. 1050 01:01:17,810 --> 01:01:20,590 And in fact, the streams will run with pretty much the same 1051 01:01:20,590 --> 01:01:24,210 efficiency as the other programs precisely. 1052 01:01:24,210 --> 01:01:28,110 And remember, again, the whole idea of this is that we've 1053 01:01:28,110 --> 01:01:32,390 used the fact that there's no really good dividing line 1054 01:01:32,390 --> 01:01:33,610 between procedures and data. 1055 01:01:33,610 --> 01:01:36,510 We've written data structures that, in fact, are sort of 1056 01:01:36,510 --> 01:01:38,760 like procedures. 1057 01:01:38,760 --> 01:01:45,280 And what that's allowed us to do is take an example of a 1058 01:01:45,280 --> 01:01:49,620 common control structure, in this place iteration. 1059 01:01:49,620 --> 01:01:52,460 And we've built a data structure which, since itself 1060 01:01:52,460 --> 01:01:54,530 is a procedure, kind of has this iteration control 1061 01:01:54,530 --> 01:01:55,496 structure in it. 1062 01:01:55,496 --> 01:01:58,650 And that's really what streams are. 1063 01:01:58,650 --> 01:01:59,900 OK, questions? 1064 01:02:03,950 --> 01:02:06,110 AUDIENCE: Your description of tail-tail-tail, if I 1065 01:02:06,110 --> 01:02:10,050 understand it correctly, force is actually execution of a 1066 01:02:10,050 --> 01:02:13,052 procedure, if it's done without this memo-proc thing. 1067 01:02:13,052 --> 01:02:16,380 And you implied that memo-proc gets around that problem. 1068 01:02:16,380 --> 01:02:20,580 Doesn't it only get around it if tail-tail-tail is always 1069 01:02:20,580 --> 01:02:22,550 executing exactly the same-- 1070 01:02:22,550 --> 01:02:23,500 PROFESSOR: Oh, that's-- 1071 01:02:23,500 --> 01:02:23,910 sure. 1072 01:02:23,910 --> 01:02:26,050 AUDIENCE: I guess I missed that point. 1073 01:02:26,050 --> 01:02:26,540 PROFESSOR: Oh, sure. 1074 01:02:26,540 --> 01:02:27,790 I mean the point is-- 1075 01:02:31,160 --> 01:02:31,290 yeah. 1076 01:02:31,290 --> 01:02:34,160 I mean I have to do a computation to get the answer. 1077 01:02:34,160 --> 01:02:37,590 But the point is, once I've found the tail of the stream, 1078 01:02:37,590 --> 01:02:39,530 to get the tail of the tail, I shouldn't have had to 1079 01:02:39,530 --> 01:02:42,980 re-compute the first tail. 1080 01:02:42,980 --> 01:02:45,370 See, and if I didn't use memo-proc, that re-computation 1081 01:02:45,370 --> 01:02:46,460 would have been done. 1082 01:02:46,460 --> 01:02:47,710 AUDIENCE: I understand now. 1083 01:02:50,830 --> 01:02:52,550 AUDIENCE: In one of your examples, you mentioned that 1084 01:02:52,550 --> 01:02:55,010 we were able to use the substitution model because 1085 01:02:55,010 --> 01:02:56,830 there are no side effects. 1086 01:02:56,830 --> 01:03:01,040 What if we had a single processing unit-- 1087 01:03:01,040 --> 01:03:03,620 if we had a side effect, if we had a state? 1088 01:03:03,620 --> 01:03:09,120 Could we still practically build the stream model? 1089 01:03:09,120 --> 01:03:09,530 PROFESSOR: Maybe. 1090 01:03:09,530 --> 01:03:10,540 That's a hard question. 1091 01:03:10,540 --> 01:03:15,540 I'm going to talk a little bit later about the places where 1092 01:03:15,540 --> 01:03:18,960 substitution and side effects don't really mix very well. 1093 01:03:18,960 --> 01:03:21,170 But in general, I think the answer is unless you're very 1094 01:03:21,170 --> 01:03:23,920 careful, any amount of side effect is going to mess up 1095 01:03:23,920 --> 01:03:25,170 everything. 1096 01:03:35,490 --> 01:03:36,150 AUDIENCE: Sorry, I didn't quite understand 1097 01:03:36,150 --> 01:03:39,410 the memo-proc operation. 1098 01:03:39,410 --> 01:03:41,990 When do you execute the lambda? 1099 01:03:41,990 --> 01:03:46,270 In other words, when memo-proc is executed, just this lambda 1100 01:03:46,270 --> 01:03:47,600 expression is being generated. 1101 01:03:47,600 --> 01:03:50,390 But it's not clear to me when it's executed. 1102 01:03:50,390 --> 01:03:51,350 PROFESSOR: Right. 1103 01:03:51,350 --> 01:03:53,890 What memo-proc does-- remember, the thing that's 1104 01:03:53,890 --> 01:03:57,290 going into memo-proc, the thing proc, is a procedure of 1105 01:03:57,290 --> 01:03:57,930 no arguments. 1106 01:03:57,930 --> 01:04:00,390 And someday, you're going to call it. 1107 01:04:00,390 --> 01:04:03,350 Memo-proc translates that procedure into another 1108 01:04:03,350 --> 01:04:05,110 procedure of no arguments, which someday 1109 01:04:05,110 --> 01:04:06,620 you're going to call. 1110 01:04:06,620 --> 01:04:09,890 That's that lambda. 1111 01:04:09,890 --> 01:04:17,370 So here, where I initially built as my tail of the 1112 01:04:17,370 --> 01:04:20,680 stream, say, this procedure of no arguments, which 1113 01:04:20,680 --> 01:04:24,100 someday I'll call. 1114 01:04:24,100 --> 01:04:27,130 Instead, I'm going to have the tail of the stream be 1115 01:04:27,130 --> 01:04:30,650 memo-proc of it, which someday I'll call. 1116 01:04:30,650 --> 01:04:35,340 So that lambda of nil, that gets called when you call the 1117 01:04:35,340 --> 01:04:40,990 memo-proc, when you call the result of that memo-proc, 1118 01:04:40,990 --> 01:04:44,400 which would be ordinarily when you would have called the 1119 01:04:44,400 --> 01:04:47,642 original thing that you set it. 1120 01:04:47,642 --> 01:04:49,690 AUDIENCE: OK, the reason I ask is I had a feeling that when 1121 01:04:49,690 --> 01:04:52,610 you call memo-proc, you just return this lambda. 1122 01:04:52,610 --> 01:04:53,770 PROFESSOR: That's right. 1123 01:04:53,770 --> 01:04:58,100 When you call memo-proc, you return the lambda. 1124 01:04:58,100 --> 01:05:00,090 You never evaluate the expression at all, until the 1125 01:05:00,090 --> 01:05:02,270 first time that you would have evaluated it. 1126 01:05:07,590 --> 01:05:10,000 AUDIENCE: Do I understand it right that you actually have 1127 01:05:10,000 --> 01:05:12,980 to build the list up, but the elements of the 1128 01:05:12,980 --> 01:05:14,240 list don't get evaluated? 1129 01:05:14,240 --> 01:05:15,630 The expressions don't get evaluated? 1130 01:05:15,630 --> 01:05:18,540 But at each stage, you actually are building a list. 1131 01:05:18,540 --> 01:05:19,750 PROFESSOR: That's-- 1132 01:05:19,750 --> 01:05:20,700 I really should have said this. 1133 01:05:20,700 --> 01:05:22,270 That's a really good point. 1134 01:05:22,270 --> 01:05:23,660 No, it's not quite right. 1135 01:05:23,660 --> 01:05:25,080 Because what happens is this. 1136 01:05:25,080 --> 01:05:26,890 Let me draw this as pairs. 1137 01:05:26,890 --> 01:05:29,710 Suppose I'm going to make a big stream, like enumerate 1138 01:05:29,710 --> 01:05:32,740 interval, 1 through 1 billion. 1139 01:05:32,740 --> 01:05:43,045 What that is, is a pair with a 1 and a promise. 1140 01:05:46,520 --> 01:05:47,890 That's exactly what it is. 1141 01:05:47,890 --> 01:05:49,140 Nothing got built up. 1142 01:05:51,600 --> 01:05:56,370 When I go and force this, and say, what happens? 1143 01:05:56,370 --> 01:06:00,530 Well, this thing is now also recursively a CONS. 1144 01:06:00,530 --> 01:06:07,770 So that this promise now is the next thing, which is a 2 1145 01:06:07,770 --> 01:06:11,350 and a promise to do more. 1146 01:06:11,350 --> 01:06:14,470 And so on and so on and so on. 1147 01:06:14,470 --> 01:06:18,200 So nothing gets built up until you walk down the stream. 1148 01:06:18,200 --> 01:06:20,790 Because what's sitting here is not the list, but a promise to 1149 01:06:20,790 --> 01:06:24,250 generate the list. And by promise, 1150 01:06:24,250 --> 01:06:25,500 technically I mean procedure. 1151 01:06:28,050 --> 01:06:30,485 So it doesn't get built up. 1152 01:06:30,485 --> 01:06:34,280 Yeah, I should have said that before this point. 1153 01:06:34,280 --> 01:06:34,490 OK. 1154 01:06:34,490 --> 01:06:34,790 Thank you. 1155 01:06:34,790 --> 01:06:36,340 Let's take a break.