week11: zipper metaphor
authorJim Pryor <profjim@jimpryor.net>
Tue, 30 Nov 2010 15:25:15 +0000 (10:25 -0500)
committerJim Pryor <profjim@jimpryor.net>
Tue, 30 Nov 2010 15:25:15 +0000 (10:25 -0500)
Signed-off-by: Jim Pryor <profjim@jimpryor.net>
week11.mdwn

index 1b38e49..be59f0e 100644 (file)
@@ -28,7 +28,9 @@ This searches for the `n`th element of a list that satisfies the predicate `test
                        | x :: xs -> helper x n xs
                in helper default n lst;;
 
                        | x :: xs -> helper x n xs
                in helper default n lst;;
 
-This duplicates a lot of the structure of `find_nth`; it just has enough different code to retrieve different information when the matching element is found. But now what if you wanted to retrieve yet a different kind of information...? Ideally, there should be some way to factor out the code to find the target element---the `n`th element of the list satisfying the predicate `test`---from the code that retrieves the information you want once the target is found. We might build upon the initial `find_nth` function, since that returns the *position* of the matching element. We could hand that result off to some other function that's designed to retrieve information of a specific sort surrounding that position. But suppose our list has millions of elements, and the target element is at position 600512. The search function will already have traversed 600512 elements of the list looking for the target, then the retrieval function would have to *start again from the beginning* and traverse those same 600512 elements again. It could go a bit faster, since it doesn't have to check each element against `test` as it traverses. It already knows how far it has to travel. But still, this should seem a bit wasteful.
+This duplicates a lot of the structure of `find_nth`; it just has enough different code to retrieve different information when the matching element is found. But now what if you wanted to retrieve yet a different kind of information...?
+
+Ideally, there should be some way to factor out the code to find the target element---the `n`th element of the list satisfying the predicate `test`---from the code that retrieves the information you want once the target is found. We might build upon the initial `find_nth` function, since that returns the *position* of the matching element. We could hand that result off to some other function that's designed to retrieve information of a specific sort surrounding that position. But suppose our list has millions of elements, and the target element is at position 600512. The search function will already have traversed 600512 elements of the list looking for the target, then the retrieval function would have to *start again from the beginning* and traverse those same 600512 elements again. It could go a bit faster, since it doesn't have to check each element against `test` as it traverses. It already knows how far it has to travel. But still, this should seem a bit wasteful.
 
 Here's an idea. What if we had some way of representing a list as "broken" at a specific point. For example, if our base list is:
 
 
 Here's an idea. What if we had some way of representing a list as "broken" at a specific point. For example, if our base list is:
 
@@ -64,7 +66,23 @@ The kind of data structure we're looking for here is called a **list zipper**. T
 
 would be represented as `([30; 20; 10], [40; 50; 60; 70; 80; 90])`. To move forward in the base list, we pop the head element `40` off of the head element of the second list in the zipper, and push it onto the first list, getting `([40; 30; 20; 10], [50; 60; 70; 80; 90])`. To move backwards again, we pop off of the first list, and push it onto the second. To reconstruct the base list, we just "move backwards" until the first list is empty. (This is supposed to evoke the image of zipping up a zipper; hence the data structure's name.)
 
 
 would be represented as `([30; 20; 10], [40; 50; 60; 70; 80; 90])`. To move forward in the base list, we pop the head element `40` off of the head element of the second list in the zipper, and push it onto the first list, getting `([40; 30; 20; 10], [50; 60; 70; 80; 90])`. To move backwards again, we pop off of the first list, and push it onto the second. To reconstruct the base list, we just "move backwards" until the first list is empty. (This is supposed to evoke the image of zipping up a zipper; hence the data structure's name.)
 
-This is a very handy datastructure, and it will become even more handy when we translate it over to more complicated base structures, like trees. To help get a good conceptual grip on how to do that, it's useful to introduce a kind of symbolism for talking about zippers. This is just a metalanguage notation, for us theorists; we don't need our programs to interpret the notation. We'll use a specification like this:
+We had some discussio in seminar of the right way to understand the "zipper" metaphor. In the case of the list zipper, I think it's best to think of the tab of the zipper being here:
+
+                t
+                 a
+                  b
+                   40;
+               30;     50;
+           20;             60;
+       [10;                    70;
+                                   80;
+                                       90]
+
+And imagine that you're just seeing the left half of a real-zipper, rotated 60 degrees counter-clockwise. When the list is all "zipped up", we've "move backwards" to the state where the first element is targetted:
+
+       ([], [10; 20; 30; 40; 50; 60; 70; 80; 90])
+
+However you understand the "zipper" metaphor, this is a very handy datastructure, and it will become even more handy when we translate it over to more complicated base structures, like trees. To help get a good conceptual grip on how to do that, it's useful to introduce a kind of symbolism for talking about zippers. This is just a metalanguage notation, for us theorists; we don't need our programs to interpret the notation. We'll use a specification like this:
 
        [10; 20; 30; *; 50; 60; 70; 80; 90], * filled by 40
 
 
        [10; 20; 30; *; 50; 60; 70; 80; 90], * filled by 40