Intro Ths mornng I d lke to talk a lttle bt about s and s. We wll start out wth smlartes and dfferences, then we wll see how to draw them n envronment dagrams, and we wll fnsh wth some examples. Happy learnng! Iterators 1. Access Frst thngs frst: how do we get an? Ths s a bt confusng, so we ll start at the top wth terables. By now you are famlar wth terables thngs lke lsts, tuples, strngs, etc. We can ndex them. (Yes, all of them, not just lsts!) But, a lot of the tme we just fnd ourselves wrtng lst[0], lst[1], lst[2], and so on. We just want to get the elements of the sequence n order. It would be a lot more convenent f there was some that we could tell, Okay, gve me the next element. Then we would not have to keep track of the ndces. Ths s where s come n. We provde an terable, and then the wll feed us the elements one by one, n order, just lke we asked. To provde an terable, we just use the ter method. In summary: Iterables * Lsts * Tuples * Strngs * etc. ter( ) Iterators * ter(lst) * ter(tuple) * ter(strng) * range(nteger) access va ndexng access usng next( ) Also note that, techncally, the word terable s also a blanket term for terables, s, and s. That s because all of these thngs are terable, meanng that we are able to terate over them, usng a for loop. 2. Envronment Dagrams Envronment dagrams are the sngle best tool I know of for learnng CS. (Yes, better than computers. Actually.) They are already worth a sgnfcant fracton of your grade, thanks to WWPD and smlar questons whch appear consstently on exams. But even more mportantly, envronment dagrams are how you work your way through confusng bugs or edge cases you don t understand. Ths s why t s so vtal that you know how to represent everythng n envronment dagrams. So, let s talk about drawng s n envronment dagrams, yay!
Example 1: Iterators and Mutablty Iterators have one job: to keep track of an ndex n an terable. = ter(lst) global lst In ths envronment dagram, lst s a lst, and s an over that lst. In the envronment dagram, we smply represent as an holdng a ponter to the very begnnng of lst. = ter(lst) global lst When we call, the number 0 gets returned and we move the ponter n so that t ponts to the next element n lst. Now that we see how to draw s n envronment dagrams, let s take a look at a confusng edge case: = ter(lst) lst.append(3) Wll the last call to next cause an error, or wll t return 3? Let s draw the envronment dagram. Remember that append s a mutable functon, so lst.append(3) does not make a new lst. Rather, t changes lst so that 3 s at the end. So the frst 4 lnes should look lke ths: = ter(lst) lst.append(3) global lst 3
Now the has no way to know that 3 was not orgnally n lst. So f we call a few more tmes, we get 3 and not an error. At the very end, the envronment dagram looks lke ths: = ter(lst) lst.append(3) global lst 3 Check that ths makes sense to you, before you keep readng. All rght, now let s consder a smlar but dfferent pece of code. What wll happen the same thng, or somethng dfferent? = ter(lst) lst = lst + [3] The dfference here s that we have lst = lst + [3] nstead of lst.append(3). Sometmes envronment dagrams can get trcky, especally when we are assgnng or reassgnng varables. Even though t may be tedous, please use the followng process whenever you see an equals sgn. I have taught t to several students and they all got 100% or very near to 100% on the mdterm 2 WWPD. 1. Do not look at the left sde of the equals sgn. 2. Evaluate the rght sde of the equals sgn. Draw the result somewhere wth enough space. 3. Now look at the left sde of the equals sgn, and bnd that value to what you just drew. Contnue to the next page to see what t looks lke when we apply the process above to the lne lst = lst + [3].
Ignore the left hand sde of the equals sgn for now. Instead let s just evaluate lst + [3]. Ths s drawn below the envronment dagram, and nothng s bound to t yet. global lst 3 Ths s mportant. Note that the varable lst s not bound to the lst [0,1,2]. Rather, lst s just a ponter to the lst [0,1,2]. So, when we reassgn lst, ths only overwrtes that ponter. It does not overwrte the actual lst [0,1,2]. The result s the envronment dagram to the rght. global lst lst 3 Note that nothng has changed the lst [0,1,2], so the s stll the same as before we reassgned the varable lst. When we call t wll return 1, and then 2, and f we call agan we wll get an error. wll never return 3. Generators 1. Access Sometmes we want the elements of a sequence, one by one, but we don t already have that sequence n a lst, tuple, etc. That means we can t use an to get the elements we want. Instead, we use somethng called a. Generators only compute a value once we request t. For example, magne I have a over Fbonacc numbers called fb. When I call next(fb) the frst tme, t yelds 0. When I call next(fb) agan, t yelds 1. The nth tme I call next(fb), I wll get the nth Fbonacc number. So, how do I get the I want? I have to specfy the sequence t should gve me. Ths makes sense, snce t couldn t gve me the next number n the sequence f t ddn t know what the pattern was. To specfy a pattern, I have to wrte a functon.
The functon that specfes the pattern I want s called a functon. When I call the functon, t does not gve me the frst number n the pattern. Instead, t gves me a. The s what yelds the pattern I want. To summarze: Generator Functon specfes the desred pattern call the functon Generator Object yelds elements from the sequence next( ) Element the next element accordng to ths Ths means that I can call a functon multple tmes, and then I can call next on each of the resultng s multple tmes. 1 2 3 5 functon 2 3 2 3 5 2 3 5 Make sure you understand everythng above, before proceedng. 2. Envronment Dagrams All rght, so now we know what s are. How do we draw them? We wll explore ths queston n the followng examples. Example 2: Fbonacc Generator Let s say I want to get all the Fbonacc numbers, one by one. I can t use an snce there are nfnte Fbonacc numbers, and s are only fnte. Instead we can use a. Remember how s just have to keep track of an ndex n an terable? Well, s are a really smlar dea. The dfference s ths: nstead of keepng track of an ndex n an terable, we wll be keepng tack of a lne of code n a functon. See the next page to see what t looks lke.
Take a look at ths code. Frst we wrte a Fbonacc functon, and then we assgn the varable g to a partcular Fbonacc. Don t worry about how to wrte ths functon yet for now we wll focus on beng able to understand t. def fb(): cur, next = 0, 1 yeld cur cur, next = next, cur + next g = fb() Ths corresponds to the envronment dagram below: global g def fb(): cur, next = 0, 1 yeld cur cur, next = next, cur + next At the begnnng, the ponts rght under the functon sgnature. When we call next(g), the functon executes untl rght after the frst yeld. The call to next wll yeld 0 and the envronment dagram wll look lke ths: global g f1:fb cur 0 def fb(): cur, next = 0, 1 yeld cur cur, next = next, cur + next next 1 If we call next(g) agan, then we wll resume from where we left off n fb. The next lne to execute s the very bottom one, where cur and next both get reassgned to 1. Then we reenter the whle loop and yeld cur. The s ponter leaves off at ths yeld, whch s only concdentally the same one we left off on before. Afterwards, the envronment dagram looks lke ths: global g f1:fb cur 1 def fb(): cur, next = 0, 1 yeld cur cur, next = next, cur + next next 1 Make sure you understand ths dagram before movng to the next page.
Example 3: Generator of Generators Now that we know how to draw s n envronment dagrams, let s talk a lttle bt about how to wrte functons. The very frst thng you want to do s thnk about how many thngs you should be yeldng. Typcally we want to yeld a lot of thngs maybe hundreds or even nfnte but we don t want to have to wrte that many yeld statements. So let s thnk about loopng. We have seen 3 methods n ths class: recurson, whle, and for. The latter two are mostly nterchangeable, except for the behavor of whle True, so we wll consder these 3 methods nstead: 1. recurson 2. whle True 3. for Let s consder them one by one. Thnk about mplementng a fb functon recursvely. We mght try somethng lke ths: def fb(n): f n <= 0: yeld n yeld fb(n-1) + fb(n-2) There s a problem, though. Recall that a functon does not return any elements of the sequence we want. Instead t returns a, and that yelds the elements we want. Look at the recursve calls above. Snce fb s a functon, these recursve calls wll not return numbers. Instead they wll return new s, and we can t add s! So ths wll error. In general, ths problem makes recurson a bad dea for wrtng functons. The two remanng optons are whle True and for. They are both handy, and suted to dfferent tasks: * If you want to yeld nfntely many elements, then use whle True. * If you want to yeld fntely many elements, then use for. Ths mght be somethng you want on your cheat sheet ;-) Let s put t to use. In the extra lab questons, there was a functon called remanders_(m). Instead of yeldng numbers or strngs lke most functons we wll see n 61A, ths functon yelds s. (Meta, rght?) Go on to the next page to see some doctests.
x = remanders_(3) g1 = next(x) next(g1) > 0 next(g1) > 3 next(g1) > 6 next(g1) > 9... g2 = next(x) next(g2) > 1 next(g2) > 4 next(g2) > 7 next(g2) > 10... How do we even started on somethng lke ths? As always, our very frst step should be to decde between usng whle True or usng for. Refer back to the prevous page f you need to. Ask yourself, should remanders_(m) yeld nfntely many thngs, or fntely many thngs? Lookng at the above doctests, we see t should yeld fntely many thngs specfcally, m thngs. It wll yeld m s. That means we want a for loop. We start out lke ths: def remanders_(m): Okay, next step. What are we yeldng n the for loop? Generator s. That means we need a functon, so that we can call t to get the s we need. We wll have to defne a functon. def remanders_(m): def remanders(): yeld remanders() Ths s the trcky part. Does remanders need any parameters? Look back at the doctests. g1, g2, and g3 all yeld dfferent thngs. That means we can t use the same exact functon to make them. We need a parameter so that g1, g2, and g3 don t all use the same pattern. In the above functon, r makes a good parameter snce we know t s dfferent each tme we call remanders. The result s somethng lke ths: def remanders_(m): def remanders(r): yeld remanders(r) (Example 3 contnued on the next page.) g3 = next(x) next(g3) > 2 next(g3) > 5 next(g3) > 8 next(g3) > 11... next(x) > StopIteraton
Now we have fnshed wrtng remanders_, and we only have to wrte the functon remanders. Agan recall the frst step n wrtng a functon: choose between whle True or for. Look at the doctests. g1, g2, and g3 are all s returned from a call to remanders. They yeld nfntely many thngs, so use whle True. Now our functon looks lke ths: def remanders_(m): def remanders(r): yeld remanders(r) Here s a tp that apples to functons, recurson, lnked lst problems, and pretty much every code-wrtng queston n 61A. (Maybe make a note of t.) The smplest lne n the doctest often tells you what your base case s. For reference, here are the smplest doctests for g1, g2, and g3 from the prevous page: next(g1) > 0 next(g2) > 1 next(g3) > 2 It looks lke the frst call to each just yelds the value of r that the was created from. Now we have ths: def remanders_(m): def remanders(r): yeld r yeld remanders(r) Of course, ths s not complete. Now g1 just yeld 0 forever, g2 wll yeld 1 forever, and g3 wll yeld 2 forever. Snce we are yeldng r each tme, thnk about how we have to modfy r to get the correct value at every yeld after the frst one. In other words, what s the pattern? Recall from earler, functons are just about wrtng down patterns. Here, we just ncrement by m to get each successve call: g1: 0, 3, 6, 9 g2: 1, 4, 7, 10 g3: 2, 5, 8, 11 The complete functon s on the next page.
def remanders_(m): def remanders(r): yeld r r += m yeld remanders(r) Check to see that ths makes sense to you, before movng on. Now let s recap what we learned from that example: * Check whether you want nfntely many thngs (whle True), or fntely many thngs (for). * Look at the smplest doctest to determne your base case. * Frequently check your understandng wth one of the doctests, as you wrte code. Blurrng the Lne (bonus secton) There s nothng nherently specal about s and s n Python. We learn about them because they make a lot of tasks easer, but they are not necessary to solve those tasks. It s very mportant to recognze that the bult n functons and data structures are not magc. In fact, you could mplement them yourself. Whenever I come across a new concept lke s, I always thnk about how I would mplement t myself. Ths makes t clear that the bult-ns are really nothng more than useful code the desgners of Python courteously wrote so you wouldn t have to. For nstance, consder the Fbonacc we wrote n Example 2. We can acheve the same functonalty usng nothng but a lst and a hgher order functon. Example 4: Fbonacc Generator Ths task s more suted toward a lesson on hgher order functons, so I wll not go over the soluton process here. However, I encourage you to gve t a go before peekng at the answer. If you aren t able to mmedately solve t, don t worry. Just make sure you understand why t works. def fb(): lst = [0,1] def yeld_next(): lst.append(lst[0] + lst[1]) return lst.pop(0) return yeld_next