Page 2: Generalizing the Map Pattern

Unit 8, Lab 4, Page 2

On this page, you will generalize the code pattern you’ve been exploring and get to know some other problems where this code pattern can be used.

Here’s a version of the code for each block from the previous page.
plurals (words){if(empty?(words)){report(list{})}else{report(join(item(1) of (words) (s) in front of (plurals(all but first of(words))))}}

Your exaggerate word block might look a lot different, but here is one version:
exaggerate word (word){if(is (word) a (number)?){report(2*word)}; if(word=good){report(great)}; if(word=bad){report(terrible)}; if(word=like){report(love)}; if(word=dislike){report(hate)}; report(word)}

squares(numbers){if(empty?(numbers)){report(list{})}else{(item(1)of(numbers)*item(1)of(numbers)) in front of(squares(all but first of(numbers))))}}
exaggerate wordlist(words){if(empty? (words)){report(empty list)}else{report((exaggerate word(item(1) of (words))) in front of (exaggerate wordlist(all but first of (words))))}}

Plurals, squares, and exaggerate wordlist look almost identical. The only difference is the particular function that’s applied to item 1 of the input list. Here’s a generalization of the pattern:
map definition without function call: map (function) over (data){if (empty? (data)){report(empty list)} else{report(() in front of (map(function) over(all but first of (data))))}}

But what do we put in the first input slot of in front of?

There are two little details you have to learn to finish this definition. The first is that the function input has to be a reporter. You already know how to set an input to be of a specific type. Setting this as a reporter is no different:
reporter type declaration

The Greek letter λ that appears in the Block Editor next to the word function in the orange oval is a type reminder, just like the︙for lists.

Now you have to know how to apply the function to item 1 of data. Find the call block in the Control palette and click on its right arrowhead to give it a second input slot. Then fill it in like this:
map definition: map (function lambda) over (data){if (empty? (data)){report(empty list)} else{report((call(function) with inputs (item(1) of (data))) in front of (map(function) over(all but first of (data))))}}

That’s it! You’ve written map, a higher-order function.

The call block finds empty input slots in its function input and fills them in with the given input values.

Higher-order functions aren’t difficult, once you understand recursion and how to generalize a procedure by adding an input. They’re no big deal to write! And they are very powerful to use.

  1. Rebuild the exaggerate block using a call to map. What happens to the helper function?

  1. Occasionally a problem comes up that doesn’t exactlymatch the map pattern, but comes close. In that case, you can’t use map, but your understanding of the pattern still helps. Here’s an example:
    odd numbered items of (data){if(empty? (data)){report(empty list)} else{if(empty? (all but first of (data))){report(data)} else{report((item(1) of (data)) in front of (odd numbered items of(all but first of (all but first of (data)))))}}}
    Fill in the blanks: This script is just like the map pattern except for ______ (base case) and _____ (in the recursive case).

  1. Build the pairup block:
    pairup(list{now, here, after, math}), reporting {nowhere, hereafter, aftermath}