Modeling Language: Plurals, Part 2

Need to redo KEEP images. --MF, 6/27/20

You can improve plural to work correctly with more words. If the project is not already open, please reload it. (The suggested file name was U2-Plural.)

  1. Abstraction: Make a plural-h () block that "specializes" on just one category, words that end with the letter "h".
    • Start with a list of the words you want it to work for.
    • This specialist block should work correctly for words that fit its specialty plural-h (crutch) reporting 'crutches' plural-h (moth) reporting 'moths', plural-h (bush) reporting 'bushes'. It can be completely wrong about words that don't fit its specialty plural-h (mouse) reporting 'mousees' because the plural block should never give words to plural-h unless those words end with "h".
    • Test plural-h () with a variety of words to make sure it works the way you want. Then use map to test it on the entire list you made earlier.
      Click for a hint about creating a test list of words that begin with "h".
      Because you know what keep-items-such-that-(last-letter-of-(input)-equals-h)-from-test-list does, you can use its output as input to a test of plural or plural-h:
      map-plural(with-input)-over-(keep-items-such-that-(last-letter-of-(input)-equals-h)-from-test-list)
  2. Abstraction: Language often has special cases. In English, the plurals of some nouns add "s"; some add "es"; nouns like "calf" and "fly" become "calves" and "flies", changing their final letters before adding "es". And more. For a programming task this complex, it's (generally) best to break it into parts, handle each part separately with its own procedure (its own block), and then have the "top-level" block—in this case, plural, itself—use those specialists. That is, instead of coding every little detail directly in plural, it is cleaner and clearer to make plural look something like this.
    abstract definition of plural: if h = last letter of word, report h-case plural; if y = last letter of word, report y-case plural; if some other case, use a block that specializes in that case; etc.; else report join word s

    Showing the structure of the method—just the overall strategy—in the "top-level" block and leaving the details to separate blocks is one part of an important computer science idea called abstraction. Abstraction keeps your code clear, readable, and more easily debugged. It will also help your code be more flexible.

  3. When you trust your new specialist block, edit plural to use the specialist. Test (you can use map) to make sure plural still works for all the words it used to work for, as well as the new ones.
  4. Create plural-y to handle words like plural-y (tray) reporting 'trays' and plural-y (sky) reporting 'skies'
  5. Remember, any specialist can make mistakes if it's asked to do a job that isn't its specialty. For example:
    plural-y (mouse) reporting 'mousies'
    Make sure plural gives plural-y only words it knows how to handle correctly.
    Save your work
I'm not thrilled with the way this pushes a particular implementation for a reason that's hidden until they get there, and doesn't make sense on its own. bh

Any better now? BTW, I moved what was TIF A up into an endnote in 1. --MF, 1/11/21

I cleaned this up a bit and added a "tough stuff" icon on part B. I think part B is very hard. More could be done to make it clearer, but I don't have the time for that now. One concern that this we should address now is not using "..." where what we need is jaggies. Can you please replace those two images with the complete images, and I'll jaggy them?

Also, can you make sure that the TG and the solutions match this version after you review it? Thanks! --MF, 1/11/21

  1. Extend plural to correctly handle a input word that has a space at the end.

    Right now, if plural is given a word with a space at the end, it leaves that space in the plural:
    plural (bulldog ) reporting 'bulldog s'. There is a space after 'bulldog' in the input and a space between 'bulldog' and 's' in the reported value.
    Figure out how to handle this special case and edit plural so that the result is
    plural (bulldog ) reporting 'bulldogs'. There is a space after 'bulldog' in the input but no space in the reported value.
    You already have a block that specializes in making plurals of words that don't have a space at the end. Use it.

    Surprise! Once plural works for a single space at the end of a word, try giving it plural (bulldog      ). There iare six spaces after 'bulldog' in the input.. That works too! But why?

  2. Tough Stuff Does your plurals block still feel cluttered, even though the details about how to handle each possible last letter are abstracted into specialist blocks? One way to improve on this situation is to use another kind of abstraction called data-directed programming.
    1. Start by making a list of key-value pairs, with last letters as the keys and specialist procedures as the values:
      set (plural specialists) to (list (list (h) (ringed plural-h ( ) block)) (list (y) (ringed plural-y ( ) block)) (list (f) (ringed plural-f ( ) block)) (list (x) (ringed (join ( ) (es)) block)) (list (...) (...)))
      To make the second item of each small list, find the block you want, put it in the second input to list, then right-click on the block and choose "ringify." As the "x" example shows, it doesn't have to be a block named plural-something. It just has to have an empty input slot where you want the word to go.
    2. FYI, this breaks the rule of not teaching more than one thing at a time, we are teaching key-value pairing, FIND FIRST, and CALL. As a result, you've defaulted to hand-holding here. --MF, 1/11/21
      Then in your plural procedure, you can replace most of the if blocks with
      script variables (pair)
set (pair) to (find first item (ringed ((item (1) of ( )) = (last letter of (word)))) in (plural specialists))
if (is (pair) a (list)?) {
    report (call (item (2) of (pair)) with inputs (word))
}
      We haven't taught FIND FIRST yet. So I looked at 3.3.3 where we first use it, and we don't teach it there either. :/ We actually teach it in 5.3.4. So I dragged some of that content back into U3 and also here. --MF, 1/11/21

      Find first is a higher-order function works similarly to keep, but it reports only the first item that's found. It is equivalent to item (1) of (keep).

      Ringifying is unclear here. What does it mean to have the ring? What does it mean not to have it? If they don't know, they are "just doing what the book says to." --MF, 1/11/21

      The call block in the palette doesn't include the "with inputs" in the picture; when you click on its right arrowhead, those words will appear along with an input slot. The call block's first input slot is the empty gray ring, which means that call wants a block in that input slot. When you drag item 2 of pair into that slot, the ring remains around it. That's usually what we want when using call, but not this time. Right-click on the item of block and choose "unringify" from the menu.

      You can experiment with call to see what else you can do with it. What happens if you don't unringify the item block? What happens if you leave out the "with inputs" and the second input?
    3. I think a hint here about testing all trailing substrings would be helpful. Also, a definition of the term would be nice. --MF, 1/11/21
      There's no reason the keys have to be single letters. Modify the code in plural so that it tests all trailing substrings of the word against the specialist list. That is, if the word is "ditch," you'd look in the specialist list for "ditch," then for "itch," then "tch," "ch," and finally, if nothing else is found, "h." You'd put
      list (ch) (ringed block (join ( ) (es)))
      in the list, and then words ending "ch" would have "es" added. This mechanism is very general; it works even for special cases such as
      list (child) (ringified (word (children)))
      (You'll have to define the word function that just reports its input; it's needed because you can't type letters directly into a ring.) Now you have a really uncluttered plural block. And it'll even work for a language other than English, if you make a different specialist list.
    4. Here is where you really start to lose me. I'm not actually building this in Snap!now, as I've spent far too long on this already, but the text doesn't explain this adequately for me. --MF, 1/11/21
      If you modify the trailing-substring test so that instead of giving up if it can't find the last letter of the word as a key, it goes one step further and looks for an empty key in the specialist list, and you add
      (item ( ) (ringed (join ( ) (s))))
      to the list, then you can be sure that every possible word will match that, if nothing else, so you don't need the if any more. Your plural block can now be just one-line: report (call (item 2 of (longest trailing substring ....
    5. For an extra challenge, modify your trailing-substring test so that a plus sign in the key matches any vowel (it will serve as a wild-card character), so you can do this:
      list (...) (list (+y) (ringed (join ( ) (s)))) (list (y) (ringed (join (all but last letter of ( )) (ies)))) (...)
Save your work