Code Like a Pirate! =================== In this chapter, we have learned how to define a dictionary, add or modify the key/value pairs, search through the keys and values, and loop through the collection. However, we still need to understand what dictionaries allow us to *do*. Lists vs. Dictionaries ---------------------- First, neither collection is better than the other. They are just different. There are some problems that lists solve more easily, while dictionaries are the better choice for other tasks. The question isn't which collection is *better* than the other. The proper question is which data structure best *fits the job at hand*. Let's take a look at one task where using a dictionary really helps. Translation App --------------- Google provides an easy-to-use algorithm that translates simple words and phrases between languages. .. figure:: figures/translation.gif :alt: Gif showing Google translate the word "Hello" into different languages. `Google translate `__ While we cannot build something as complete as this app (yet), we CAN understand how to make something similar with Python. At its core, the program is built around a dictionary! Take a look at this small English-to-Spanish dictionary. The keys are English words, and the values are the same words in Spanish: .. sourcecode:: Python :linenos: eng_to_span = { 'hello' : 'hola', 'blue' : 'azul', 'apple' : 'manzana', 'python' : 'pitón', 'library' : 'biblioteca' } Now imagine that a user inputs an English word. We can easily search the keys in the ``eng_to_span`` dictionary for that word. If we find it, we can print the value for that key, which is the Spanish translation. That's it! Our word translator is limited only by the size and accuracy of the dictionary. .. admonition:: Try It! Add key/value pairs to the ``translate`` dictionary! Feel free to use words from any two languages you want, but you must be able to type in the starting words with the letters on your keyboard. #. The keys in the dictionary should be in the language that the user enters. #. The values will be in the other language. #. Use Google translate if you need help finding word pairs. .. raw:: html Walking through the code: #. ``keep_going = True`` keeps the ``while`` loop running until the user tells the program to stop. #. The ``word_to_change`` input statement prompts the user for the word to translate. #. ``if word_to_change in translate`` checks to see if the entered word is one of the keys in the dictionary. a. If ``True``, then ``print(translate[word_to_change])`` displays the value for the key. b. If ``False``, then the user sees a message telling them that their word is missing from the dictionary. #. The ``repeat`` input statement asks the user if they would like to keep going. Any entry that does NOT start with ``'y'`` (``repeat[0].lower() != 'y'``) sets ``keep_going`` to ``False``, and the program ends. .. admonition:: Note We *could* build the translation app with lists, but that code would be less efficient and more likely to have bugs. Explain why. Talk Like A Pirate ------------------ Once we complete a language-to-language dictionary, we can use it to translate more than just single words. Earlier in this chapter, we created a dictionary that linked words in pirate-speak with their English meanings. Let's use this to have a little fun! Our next task will take a set of text and replace some of the English words with pirate-speak. Follow the instructions listed below the editor to complete the program. You can also see the steps by clicking the *Instructions* tab in the editor. .. raw:: html **Instructions:** #. Note that the English-to-pirate dictionary is defined in the ``translate.py`` module. a. Placing it here is an excellent idea, because we can keep multiple dictionaries in ``translate.py`` and then import only the ones we want into different programs. b. Changes made to a dictionary in ``translate.py`` get applied to all programs that import it. c. Feel free to add, remove, or edit the key/value pairs in ``eng_to_pirate``! #. Line 3 assigns the string we want to translate to the ``text`` variable, and line 4 splits ``text`` into a list of separate words. Line 6 defines an empty list to hold the words that go into the translated string. #. On line 7, set up a ``for`` loop to iterate through the list of words: .. sourcecode:: python :lineno-start: 7 for word in eng_words: #. Now set up a conditional to check if ``word`` is a key in the ``eng_to_pirate`` dictionary. If ``True``, append the *value* for the key to the ``new_words`` list. If ``False``, append the original word. .. sourcecode:: python :lineno-start: 8 if word in eng_to_pirate: new_words.append(eng_to_pirate[word]) else: new_words.append(word) #. After the loop is done, join the elements from the ``new_words`` list into a new string, then print the result. .. sourcecode:: python :lineno-start: 13 new_text = ' '.join(new_words) print(new_text) #. Now run the program, fixing any syntax, runtime, or logic errors. Properly done, the output should look like: :: If ye be interested in tales with happy endings, ye would be better off reading some other book. Nice! Feel free to change the value of ``text`` to try out different translations. .. admonition:: Note Right now, our translation program does NOT consider case or punctuation. We will fix this in the sections below. Consider Case ^^^^^^^^^^^^^ Change ``you`` to ``You`` in the text and run the program again. Notice that it leaves the capitalized word alone. All of the keys in ``eng_to_pirate`` are lowercase strings, so ``You`` isn't found. Let's add some logic to deal with capitalized words: #. Change the ``if`` statement to search for ``word`` in lowercase. Note that we also need to update the first ``append`` statement: .. sourcecode:: python :lineno-start: 8 if word.lower() in eng_to_pirate: new_words.append(eng_to_pirate[word.lower()]) #. Run the program again, and note that ``You`` gets changed to ``ye``. This is better, but not quite complete. The next fix is to replace capitalized words with capitalized translations. #. Change the ``if`` statement one more time to also check if ``word`` starts with a capital letter. If ``True`` we can retrieve the translated word and then apply another string method to capitalize it. .. sourcecode:: python :lineno-start: 8 if word.lower() in eng_to_pirate and word[0].isupper(): new_word = eng_to_pirate[word.lower()] new_words.append(new_word.capitalize()) #. Run the program again. Woo, hoo! ``You`` translates to ``Ye``! But wait, now nothing else in ``text`` gets converted! The way the ``if`` statement works now, only capitalized English words get changed. #. To fix this, add an ``elif`` statement to bring back the original check and translation statements: .. sourcecode:: python :lineno-start: 11 elif word in eng_to_pirate: new_words.append(eng_to_pirate[word]) Our program now replaces both lowercase words and capitalized words. Final Touch ^^^^^^^^^^^ When we split ``text`` into a list of separate strings, punctuation marks remain attached to the words. Since the keys in the ``eng_to_pirate`` dictionary don't include punctuation, strings like ``'Stop!'`` remain the same. We won't step through building the code like we did above. Instead, here is one way to deal with periods, commas, and exclamation points. Feel free to paste this into your code above: .. sourcecode:: python :lineno-start: 7 for word in eng_words: punctuation = '' # Start 'punctuation' as the empty string. # Check if 'word' ends with a period, comma, or ! if word[-1] in '.,!': # Add extra symbols as needed! punctuation = word[-1] # Save the punctuation mark. word = word[:-1] # Reassign 'word' WITHOUT the mark. if word[0].isupper() and word.lower() in eng_to_pirate: new_word = eng_to_pirate[word.lower()] # Translate 'word'. # Append capitalized 'new_word' combined with any saved punctuation mark. new_words.append(new_word.capitalize() + punctuation) elif word in eng_to_pirate: # Append translated word plus any saved punctuation mark. new_words.append(eng_to_pirate[word] + punctuation) else: # Append original word plus any saved punctuation mark. new_words.append(word + punctuation) Try entering different phrases for ``text`` to check that the punctuation and capitalization code works. .. admonition:: Examples #. **Text:** ``"Stop! Go Meet The Principal in the cafeteria."`` **Translation:** ``"Avast! Go Meet Th' Scallywag in th' swill dungeon."`` #. **Text:** ``"Put away your phone and focus on the computer!"`` **Translation:** ``"Put away yer cursed device and focus on th' magic box!"`` #. **Text:** ``"Listen to your teacher."`` **Translation:** ``"Listen ta yer wise sage."``