diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f494b1b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.tool-versions diff --git a/lessons/00 index.ipynb b/lessons/00 index.ipynb index 212e18b..24aa28d 100644 --- a/lessons/00 index.ipynb +++ b/lessons/00 index.ipynb @@ -1 +1,70 @@ -{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"provenance":[]},"kernelspec":{"name":"python3","display_name":"Python 3"},"language_info":{"name":"python"}},"cells":[{"cell_type":"markdown","source":["# CDH course \"Programming in Python\"\n","\n","**April 2023 edition**\n","\n","[Teams channel](https://teams.microsoft.com/l/channel/19%3aAlr7pcE2v9p1eaKT1BtNxJAB9mgCFT1olZvAMswJlEc1%40thread.tacv2/Algemeen?groupId=7fd15384-6748-4852-8646-d96963fb3524&tenantId=d72758a0-a446-4e0f-a0aa-4bf95a4a10e7)\n","\n","## Course modules\n","\n","1. [Introduction](https://colab.research.google.com/drive/1i4wJpUIr77Fh1renWNjt00Bd51NbC1nB)\n","2. [Values and expressions](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp)\n","3. [Conditionals](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw)\n","4. [Datastructures](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF)\n","5. [Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM)\n","6. [Loops](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV)\n","7. [Functions](https://colab.research.google.com/drive/1APhegD_KpLRFn5bC6Ater2wFpT8_Opfr) \n","8. [Debugging](https://colab.research.google.com/drive/1yQskT6SyKvXtXewx5kCla2NOmasgP8Vi)\n","9. [String manipulation](https://colab.research.google.com/drive/1Z7NNMxqHTSMoUadH3pVL6Bg6oii4qUoQ)\n","10. [Dictionaries](https://colab.research.google.com/drive/16mecEPkVGUBIFoHIALO7dE8qv7PynHL9)\n","11. [Working with files](https://colab.research.google.com/drive/17G-gA5-GNKX-mQglrPVNtndV2YhBafBr)\n","\n","## Extra material\n","\n","- [Functions exercises](https://colab.research.google.com/drive/13__hh18xcBeZ5xjeby21KXUBYjRpb_Vy)\n","- [Tips](https://colab.research.google.com/drive/1qKVaI3Ct1hOsM_06mZdXpx0sLSR_O-6R)\n","\n","## Projects\n","- [Text analysis](https://colab.research.google.com/drive/1wUgaVE70dzjIlIEuZtgQalqYI2UcfiXK)\n","\n","## Exercise solutions\n","\n","1. [Introduction](https://colab.research.google.com/drive/1RIwG7hA-Ymjcm1dmSW6wA_hu1clDNrAd)\n","2. [Values and expressions](https://colab.research.google.com/drive/1JKeNehBZ9hhHXPdVunQ_r9cv-lLR9Byy)\n","3. [Conditionals](https://colab.research.google.com/drive/1Nvvjc3fGnMg2tWvfw2W1gVn2oKKbWzGI)\n","4. [Datastructures](https://colab.research.google.com/drive/1juOQFMlRmeVUfbKWy0wtZ9lWkSZmr2Gn)\n","5. [Assertions](https://colab.research.google.com/drive/1tqjXsHnnJeEAivrxqRoOSW9eNESCwLIK)\n","6. [Loops](https://colab.research.google.com/drive/1vy2gKHtBUMk60u2bmCYlcqmhIFpI2PI-)\n","7. [Functions](https://colab.research.google.com/drive/13M4t34msJvudQTN-Im24w4iRfctOuNCv)\n","8. Debugging - no solutions\n","9. [String manipulation](https://colab.research.google.com/drive/1Z7NNMxqHTSMoUadH3pVL6Bg6oii4qUoQ)\n","10. [Dictionaries](https://colab.research.google.com/drive/1Qw7vgiyRVg1E-BkUEvCxX0e6QZQ9y_k6)\n","11. [Working with files](https://colab.research.google.com/drive/1Nz3lhsntgw4yAI6257drrp-EtEQEVogF)"],"metadata":{"id":"RQ73d6XVysi7"}}]} \ No newline at end of file +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "RQ73d6XVysi7" + }, + "source": [ + "# CDH course \"Programming in Python\"\n", + "\n", + "**April 2023 edition**\n", + "\n", + "[Teams channel](https://teams.microsoft.com/l/channel/19%3aAlr7pcE2v9p1eaKT1BtNxJAB9mgCFT1olZvAMswJlEc1%40thread.tacv2/Algemeen?groupId=7fd15384-6748-4852-8646-d96963fb3524&tenantId=d72758a0-a446-4e0f-a0aa-4bf95a4a10e7)\n", + "\n", + "## Course modules\n", + "\n", + "1. [Introduction](https://colab.research.google.com/drive/1i4wJpUIr77Fh1renWNjt00Bd51NbC1nB)\n", + "2. [Values and expressions](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp)\n", + "3. [Conditionals](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw)\n", + "4. [Datastructures](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF)\n", + "5. [Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM)\n", + "6. [Loops](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV)\n", + "7. [Functions](https://colab.research.google.com/drive/1APhegD_KpLRFn5bC6Ater2wFpT8_Opfr) \n", + "8. [Debugging](https://colab.research.google.com/drive/1yQskT6SyKvXtXewx5kCla2NOmasgP8Vi)\n", + "9. [String manipulation](https://colab.research.google.com/drive/1Z7NNMxqHTSMoUadH3pVL6Bg6oii4qUoQ)\n", + "10. [Dictionaries](https://colab.research.google.com/drive/16mecEPkVGUBIFoHIALO7dE8qv7PynHL9)\n", + "11. [Working with files](https://colab.research.google.com/drive/17G-gA5-GNKX-mQglrPVNtndV2YhBafBr)\n", + "\n", + "## Extra material\n", + "\n", + "- [Functions exercises](https://colab.research.google.com/drive/13__hh18xcBeZ5xjeby21KXUBYjRpb_Vy)\n", + "- [Tips](https://colab.research.google.com/drive/1qKVaI3Ct1hOsM_06mZdXpx0sLSR_O-6R)\n", + "\n", + "## Projects\n", + "- [Text analysis](https://colab.research.google.com/drive/1wUgaVE70dzjIlIEuZtgQalqYI2UcfiXK)\n", + "\n", + "## Exercise solutions\n", + "\n", + "1. [Introduction](https://colab.research.google.com/drive/1RIwG7hA-Ymjcm1dmSW6wA_hu1clDNrAd)\n", + "2. [Values and expressions](https://colab.research.google.com/drive/1JKeNehBZ9hhHXPdVunQ_r9cv-lLR9Byy)\n", + "3. [Conditionals](https://colab.research.google.com/drive/1Nvvjc3fGnMg2tWvfw2W1gVn2oKKbWzGI)\n", + "4. [Datastructures](https://colab.research.google.com/drive/1juOQFMlRmeVUfbKWy0wtZ9lWkSZmr2Gn)\n", + "5. [Assertions](https://colab.research.google.com/drive/1tqjXsHnnJeEAivrxqRoOSW9eNESCwLIK)\n", + "6. [Loops](https://colab.research.google.com/drive/1vy2gKHtBUMk60u2bmCYlcqmhIFpI2PI-)\n", + "7. [Functions](https://colab.research.google.com/drive/13M4t34msJvudQTN-Im24w4iRfctOuNCv)\n", + "8. Debugging - no solutions\n", + "9. [String manipulation](https://colab.research.google.com/drive/1Z7NNMxqHTSMoUadH3pVL6Bg6oii4qUoQ)\n", + "10. [Dictionaries](https://colab.research.google.com/drive/1Qw7vgiyRVg1E-BkUEvCxX0e6QZQ9y_k6)\n", + "11. [Working with files](https://colab.research.google.com/drive/1Nz3lhsntgw4yAI6257drrp-EtEQEVogF)" + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "jupytext": { + "main_language": "python" + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/lessons/00 index.py b/lessons/00 index.py new file mode 100644 index 0000000..d06c8bd --- /dev/null +++ b/lessons/00 index.py @@ -0,0 +1,55 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# name: python3 +# --- + +# %% [markdown] id="RQ73d6XVysi7" +# # CDH course "Programming in Python" +# +# **April 2023 edition** +# +# [Teams channel](https://teams.microsoft.com/l/channel/19%3aAlr7pcE2v9p1eaKT1BtNxJAB9mgCFT1olZvAMswJlEc1%40thread.tacv2/Algemeen?groupId=7fd15384-6748-4852-8646-d96963fb3524&tenantId=d72758a0-a446-4e0f-a0aa-4bf95a4a10e7) +# +# ## Course modules +# +# 1. [Introduction](https://colab.research.google.com/drive/1i4wJpUIr77Fh1renWNjt00Bd51NbC1nB) +# 2. [Values and expressions](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp) +# 3. [Conditionals](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw) +# 4. [Datastructures](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF) +# 5. [Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM) +# 6. [Loops](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV) +# 7. [Functions](https://colab.research.google.com/drive/1APhegD_KpLRFn5bC6Ater2wFpT8_Opfr) +# 8. [Debugging](https://colab.research.google.com/drive/1yQskT6SyKvXtXewx5kCla2NOmasgP8Vi) +# 9. [String manipulation](https://colab.research.google.com/drive/1Z7NNMxqHTSMoUadH3pVL6Bg6oii4qUoQ) +# 10. [Dictionaries](https://colab.research.google.com/drive/16mecEPkVGUBIFoHIALO7dE8qv7PynHL9) +# 11. [Working with files](https://colab.research.google.com/drive/17G-gA5-GNKX-mQglrPVNtndV2YhBafBr) +# +# ## Extra material +# +# - [Functions exercises](https://colab.research.google.com/drive/13__hh18xcBeZ5xjeby21KXUBYjRpb_Vy) +# - [Tips](https://colab.research.google.com/drive/1qKVaI3Ct1hOsM_06mZdXpx0sLSR_O-6R) +# +# ## Projects +# - [Text analysis](https://colab.research.google.com/drive/1wUgaVE70dzjIlIEuZtgQalqYI2UcfiXK) +# +# ## Exercise solutions +# +# 1. [Introduction](https://colab.research.google.com/drive/1RIwG7hA-Ymjcm1dmSW6wA_hu1clDNrAd) +# 2. [Values and expressions](https://colab.research.google.com/drive/1JKeNehBZ9hhHXPdVunQ_r9cv-lLR9Byy) +# 3. [Conditionals](https://colab.research.google.com/drive/1Nvvjc3fGnMg2tWvfw2W1gVn2oKKbWzGI) +# 4. [Datastructures](https://colab.research.google.com/drive/1juOQFMlRmeVUfbKWy0wtZ9lWkSZmr2Gn) +# 5. [Assertions](https://colab.research.google.com/drive/1tqjXsHnnJeEAivrxqRoOSW9eNESCwLIK) +# 6. [Loops](https://colab.research.google.com/drive/1vy2gKHtBUMk60u2bmCYlcqmhIFpI2PI-) +# 7. [Functions](https://colab.research.google.com/drive/13M4t34msJvudQTN-Im24w4iRfctOuNCv) +# 8. Debugging - no solutions +# 9. [String manipulation](https://colab.research.google.com/drive/1Z7NNMxqHTSMoUadH3pVL6Bg6oii4qUoQ) +# 10. [Dictionaries](https://colab.research.google.com/drive/1Qw7vgiyRVg1E-BkUEvCxX0e6QZQ9y_k6) +# 11. [Working with files](https://colab.research.google.com/drive/1Nz3lhsntgw4yAI6257drrp-EtEQEVogF) diff --git a/lessons/01 introduction.ipynb b/lessons/01 introduction.ipynb index 559ef56..05268de 100644 --- a/lessons/01 introduction.ipynb +++ b/lessons/01 introduction.ipynb @@ -1 +1,489 @@ -{"cells":[{"cell_type":"markdown","metadata":{"id":"Buvh9v-iYeOO"},"source":["# Module 1: Introduction\n","## CDH course \"Programming in Python\"\n","\n","[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n","\n","## Welcome!\n","\n","## Who are we?\n","\n","- Julian Gonggrijp, Sheean Spoel, Jelte van Boheemen, Luka van der Plas, Mees van Stiphout\n","- Developers at the Digital Humanities Lab\n","\n","## Who are you?\n","\n","- Entry level course, no previous experience required\n","- If you have some experience, parts of the course may be familiar. Challenge yourself with some difficult exercises!\n","\n","## Goals\n","\n","- Introduce the basics of the Python programming language\n","- Write simple computer programs\n","- Simple data analysis on your own data\n","- Teach good practices that you can apply to all your future programming, and make you a self-reliant programmer\n","\n","## Colab Notebooks\n","\n","- Runs Python code in your web browser, no installing required\n","- If you do not have a Google account, please make one\n","- Presenting straight from Colab, so you can follow our example\n","- Copy the notebooks to edit them, instructions in exercise 1"]},{"cell_type":"markdown","metadata":{"id":"STQf1EROV-4I"},"source":["## Python\n","\n","- Programming language\n","- Invented by Guido van Rossum (former Benevolent Dictator For Life) in 1989\n","- *High level*\n"," - Far removed from machine language\n"," - Written in C (slightly lower level)\n","- Designed to be readable\n","- Many versions, currently on major version 3."]},{"cell_type":"markdown","metadata":{"id":"rC4AwjL06k0q"},"source":["### Why learn Python?\n","\n","- (Relatively) easy to learn\n","- Widely used\n"," - Easy to install and run on a variety of platforms\n"," - Many resources available\n","- Many good packages and libraries available"]},{"cell_type":"markdown","metadata":{"id":"xyNb4mPMcQCd"},"source":["## Notebooks 101\n","\n","Let's get started!"]},{"cell_type":"markdown","metadata":{"id":"4lc8nYpP9mtf"},"source":["### Copy a notebook to your own Google Drive\n","\n","You can only run code in this notebook, not edit it. To edit it, you need to copy it to [your own Google Drive](https://drive.google.com) first.\n","\n","To do so, click on the *File* menu and choose the ninth option from the top: *Save a copy in Drive*. You can now find the copy in your drive, in the directory *Colab Notebooks*.\n","\n","Tip: give it a recognizable name (e.g. `Module 1 - my solutions`).\n"]},{"cell_type":"markdown","metadata":{"id":"8zoSNQtC_6_v"},"source":["### Adjust some settings\n","\n","Open the *Settings*, in the *Tools* menu or by clicking the cogwheel icon in the top right. Open the *Editor* section.\n","\n","- Set *Indentation width in spaces* to **4**.\n","- Switch *Show line numbers* **on**.\n","\n","The other settings are up to you."]},{"cell_type":"markdown","metadata":{"id":"qtRYKOWnB5Wl"},"source":["### Execute Python code\n","\n","Press the triangle icon in the top left corner of a code block to run it, *OR* type **ctrl-enter** when the text cursor is in the code block. Type **ctrl-shift-enter** to run only the current line.\n","\n","If you open a notebook for the first time, or if you have not run any code for a while, it takes a while before you see a result."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"MdA4_KyrDAsf"},"outputs":[],"source":["print('Hello')\n","print('Goodbye')"]},{"cell_type":"markdown","metadata":{"id":"AoBoZqIeFEru"},"source":["## Python 101"]},{"cell_type":"markdown","metadata":{"id":"ZOqgk7VAFKnJ"},"source":["### `print`\n","\n","In the previous code block, we saw the `print` function in action. It causes Python to \"talk\" to us."]},{"cell_type":"markdown","metadata":{"id":"NOtudpGlFe78"},"source":["### Values\n","\n","Between the parentheses `( )` of the `print` function, we put the *value* that we want Python to show. We can also write the value without passing it to `print`."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"in8WoUafGEyK"},"outputs":[],"source":["'Hello'"]},{"cell_type":"markdown","metadata":{"id":"BHQPB7LuQ1mf"},"source":["A bare value does not always cause output. We will explore this in the first exercise."]},{"cell_type":"markdown","metadata":{"id":"Src0ZdRKGHtl"},"source":["### Comments\n","\n","A comment starts with a hash `#` and runs until the end of the line. Python ignores comments. You can use them as notes for yourself and for other programmers."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"ssP4GWUOIxWS"},"outputs":[],"source":["# Python will not look here!\n","\n","print('Hello') # comments can start after code"]},{"cell_type":"markdown","metadata":{"id":"YxXeF4u5I2c_"},"source":["Use comments to explain *why* your code was written the way it is. You will be grateful to yourself when you read your own code after a few weeks!"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"nMTlvlbbJUWu"},"outputs":[],"source":["# Useless comment:\n","\n","# Print \"Hello\".\n","print('Hello')\n","\n","# Useful comment:\n","\n","# Everyone writes \"Hello\" in this exercise, but\n","# I prefer something a little bit more British.\n","print('How do you do?')"]},{"cell_type":"markdown","metadata":{"id":"ZhgCPUNTLIZX"},"source":["### `pass`\n","\n","You can write `pass` to do nothing. It behaves almost like a comment, but isn't. It is sometimes useful as a placeholder, as we will see later on in the course."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"oS7kfP3OLtJn"},"outputs":[],"source":["pass"]},{"cell_type":"markdown","metadata":{"id":"pKIEfocbMaIR"},"source":["## Exercise 1.1: Try it out\n","\n","Read each of the code blocks below. Try to predict what it will do, then run the block to check your prediction.\n","\n","Do you see any (subtle or not-so subtle) surprises? Can you explain them?"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"6hlNxRNNM1fV"},"outputs":[],"source":["print(1)\n","1"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"VIm6FdRIM6OE"},"outputs":[],"source":["print('oops')\n","'oops'"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"_MN48xz5NAya"},"outputs":[],"source":["print()"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"g9WKeIA2NVIA"},"outputs":[],"source":["print('apricot')\n","print('banana')\n","print('cherry')"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"NCbCfbHaNafJ"},"outputs":[],"source":["'apricot'\n","'banana'\n","'cherry'"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"-ZuiJ92yNqpi"},"outputs":[],"source":["# apricot\n","# banana\n","# cherry"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"b9bTbrNSNwwn"},"outputs":[],"source":["print('apricot')\n","'banana'\n","# cherry"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"GYmcSm6iOAiA"},"outputs":[],"source":["# apricot\n","'banana'\n","print('cherry')"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"rbjHe9KbOzFb"},"outputs":[],"source":["print('apricot')\n","'banana'\n","pass"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"V1GiIP_ZNK8H"},"outputs":[],"source":["print(pass)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"sNNoSfndOSiw"},"outputs":[],"source":["print(#oops)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"6IZS1NUuTdOX"},"outputs":[],"source":["print('apricot', 'banana')"]},{"cell_type":"markdown","metadata":{"id":"Tr3wPlHMeBCI"},"source":["## Exercise 1.2: Hello, world!\n","\n","The classic way to demonstrate a programming language's syntax is the hello world-program. This is the minimal amount of code required to output the text \"Hello, world!\" to the user. \n","\n","Write a hello world-program for Python."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"jN7WTVOOSq2C"},"outputs":[],"source":["# Your solution here"]},{"cell_type":"markdown","metadata":{"id":"zI0ohEpPUwpC"},"source":["## Next module\n","\n","[2. Values and expressions](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp)"]}],"metadata":{"colab":{"authorship_tag":"ABX9TyOqiugh/2SmC5zeIHsVl+q8","provenance":[],"toc_visible":true},"kernelspec":{"display_name":"Python 3","name":"python3"},"language_info":{"name":"python"}},"nbformat":4,"nbformat_minor":0} +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "Buvh9v-iYeOO" + }, + "source": [ + "# Module 1: Introduction\n", + "## CDH course \"Programming in Python\"\n", + "\n", + "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "\n", + "## Welcome!\n", + "\n", + "## Who are we?\n", + "\n", + "- Julian Gonggrijp, Sheean Spoel, Jelte van Boheemen, Luka van der Plas, Mees van Stiphout\n", + "- Developers at the Digital Humanities Lab\n", + "\n", + "## Who are you?\n", + "\n", + "- Entry level course, no previous experience required\n", + "- If you have some experience, parts of the course may be familiar. Challenge yourself with some difficult exercises!\n", + "\n", + "## Goals\n", + "\n", + "- Introduce the basics of the Python programming language\n", + "- Write simple computer programs\n", + "- Simple data analysis on your own data\n", + "- Teach good practices that you can apply to all your future programming, and make you a self-reliant programmer\n", + "\n", + "## Colab Notebooks\n", + "\n", + "- Runs Python code in your web browser, no installing required\n", + "- If you do not have a Google account, please make one\n", + "- Presenting straight from Colab, so you can follow our example\n", + "- Copy the notebooks to edit them, instructions in exercise 1" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "STQf1EROV-4I" + }, + "source": [ + "## Python\n", + "\n", + "- Programming language\n", + "- Invented by Guido van Rossum (former Benevolent Dictator For Life) in 1989\n", + "- *High level*\n", + " - Far removed from machine language\n", + " - Written in C (slightly lower level)\n", + "- Designed to be readable\n", + "- Many versions, currently on major version 3." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rC4AwjL06k0q" + }, + "source": [ + "### Why learn Python?\n", + "\n", + "- (Relatively) easy to learn\n", + "- Widely used\n", + " - Easy to install and run on a variety of platforms\n", + " - Many resources available\n", + "- Many good packages and libraries available" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xyNb4mPMcQCd" + }, + "source": [ + "## Notebooks 101\n", + "\n", + "Let's get started!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "4lc8nYpP9mtf" + }, + "source": [ + "### Copy a notebook to your own Google Drive\n", + "\n", + "You can only run code in this notebook, not edit it. To edit it, you need to copy it to [your own Google Drive](https://drive.google.com) first.\n", + "\n", + "To do so, click on the *File* menu and choose the ninth option from the top: *Save a copy in Drive*. You can now find the copy in your drive, in the directory *Colab Notebooks*.\n", + "\n", + "Tip: give it a recognizable name (e.g. `Module 1 - my solutions`).\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8zoSNQtC_6_v" + }, + "source": [ + "### Adjust some settings\n", + "\n", + "Open the *Settings*, in the *Tools* menu or by clicking the cogwheel icon in the top right. Open the *Editor* section.\n", + "\n", + "- Set *Indentation width in spaces* to **4**.\n", + "- Switch *Show line numbers* **on**.\n", + "\n", + "The other settings are up to you." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qtRYKOWnB5Wl" + }, + "source": [ + "### Execute Python code\n", + "\n", + "Press the triangle icon in the top left corner of a code block to run it, *OR* type **ctrl-enter** when the text cursor is in the code block. Type **ctrl-shift-enter** to run only the current line.\n", + "\n", + "If you open a notebook for the first time, or if you have not run any code for a while, it takes a while before you see a result." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "MdA4_KyrDAsf" + }, + "outputs": [], + "source": [ + "print('Hello')\n", + "print('Goodbye')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "AoBoZqIeFEru" + }, + "source": [ + "## Python 101" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ZOqgk7VAFKnJ" + }, + "source": [ + "### `print`\n", + "\n", + "In the previous code block, we saw the `print` function in action. It causes Python to \"talk\" to us." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "NOtudpGlFe78" + }, + "source": [ + "### Values\n", + "\n", + "Between the parentheses `( )` of the `print` function, we put the *value* that we want Python to show. We can also write the value without passing it to `print`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "in8WoUafGEyK" + }, + "outputs": [], + "source": [ + "'Hello'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BHQPB7LuQ1mf" + }, + "source": [ + "A bare value does not always cause output. We will explore this in the first exercise." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Src0ZdRKGHtl" + }, + "source": [ + "### Comments\n", + "\n", + "A comment starts with a hash `#` and runs until the end of the line. Python ignores comments. You can use them as notes for yourself and for other programmers." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ssP4GWUOIxWS" + }, + "outputs": [], + "source": [ + "# Python will not look here!\n", + "\n", + "print('Hello') # comments can start after code" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "YxXeF4u5I2c_" + }, + "source": [ + "Use comments to explain *why* your code was written the way it is. You will be grateful to yourself when you read your own code after a few weeks!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "nMTlvlbbJUWu" + }, + "outputs": [], + "source": [ + "# Useless comment:\n", + "\n", + "# Print \"Hello\".\n", + "print('Hello')\n", + "\n", + "# Useful comment:\n", + "\n", + "# Everyone writes \"Hello\" in this exercise, but\n", + "# I prefer something a little bit more British.\n", + "print('How do you do?')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ZhgCPUNTLIZX" + }, + "source": [ + "### `pass`\n", + "\n", + "You can write `pass` to do nothing. It behaves almost like a comment, but isn't. It is sometimes useful as a placeholder, as we will see later on in the course." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "id": "oS7kfP3OLtJn" + }, + "outputs": [], + "source": [ + "pass" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pKIEfocbMaIR" + }, + "source": [ + "## Exercise 1.1: Try it out\n", + "\n", + "Read each of the code blocks below. Try to predict what it will do, then run the block to check your prediction.\n", + "\n", + "Do you see any (subtle or not-so subtle) surprises? Can you explain them?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "6hlNxRNNM1fV" + }, + "outputs": [], + "source": [ + "print(1)\n", + "1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "VIm6FdRIM6OE" + }, + "outputs": [], + "source": [ + "print('oops')\n", + "'oops'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "_MN48xz5NAya" + }, + "outputs": [], + "source": [ + "print()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "g9WKeIA2NVIA" + }, + "outputs": [], + "source": [ + "print('apricot')\n", + "print('banana')\n", + "print('cherry')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "NCbCfbHaNafJ" + }, + "outputs": [], + "source": [ + "'apricot'\n", + "'banana'\n", + "'cherry'" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "id": "-ZuiJ92yNqpi" + }, + "outputs": [], + "source": [ + "# apricot\n", + "# banana\n", + "# cherry" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "b9bTbrNSNwwn" + }, + "outputs": [], + "source": [ + "print('apricot')\n", + "'banana'\n", + "# cherry" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "GYmcSm6iOAiA" + }, + "outputs": [], + "source": [ + "# apricot\n", + "'banana'\n", + "print('cherry')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "rbjHe9KbOzFb" + }, + "outputs": [], + "source": [ + "print('apricot')\n", + "'banana'\n", + "pass" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "V1GiIP_ZNK8H" + }, + "outputs": [], + "source": [ + "print(pass)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "sNNoSfndOSiw" + }, + "outputs": [], + "source": [ + "print(#oops)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "6IZS1NUuTdOX" + }, + "outputs": [], + "source": [ + "print('apricot', 'banana')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Tr3wPlHMeBCI" + }, + "source": [ + "## Exercise 1.2: Hello, world!\n", + "\n", + "The classic way to demonstrate a programming language's syntax is the hello world-program. This is the minimal amount of code required to output the text \"Hello, world!\" to the user. \n", + "\n", + "Write a hello world-program for Python." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "jN7WTVOOSq2C" + }, + "outputs": [], + "source": [ + "# Your solution here" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zI0ohEpPUwpC" + }, + "source": [ + "## Next module\n", + "\n", + "[2. Values and expressions](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp)" + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyOqiugh/2SmC5zeIHsVl+q8", + "provenance": [], + "toc_visible": true + }, + "jupytext": { + "main_language": "python" + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/lessons/01 introduction.py b/lessons/01 introduction.py new file mode 100644 index 0000000..e1a1b99 --- /dev/null +++ b/lessons/01 introduction.py @@ -0,0 +1,224 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# name: python3 +# --- + +# %% [markdown] id="Buvh9v-iYeOO" +# # Module 1: Introduction +# ## CDH course "Programming in Python" +# +# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# +# ## Welcome! +# +# ## Who are we? +# +# - Julian Gonggrijp, Sheean Spoel, Jelte van Boheemen, Luka van der Plas, Mees van Stiphout +# - Developers at the Digital Humanities Lab +# +# ## Who are you? +# +# - Entry level course, no previous experience required +# - If you have some experience, parts of the course may be familiar. Challenge yourself with some difficult exercises! +# +# ## Goals +# +# - Introduce the basics of the Python programming language +# - Write simple computer programs +# - Simple data analysis on your own data +# - Teach good practices that you can apply to all your future programming, and make you a self-reliant programmer +# +# ## Colab Notebooks +# +# - Runs Python code in your web browser, no installing required +# - If you do not have a Google account, please make one +# - Presenting straight from Colab, so you can follow our example +# - Copy the notebooks to edit them, instructions in exercise 1 + +# %% [markdown] id="STQf1EROV-4I" +# ## Python +# +# - Programming language +# - Invented by Guido van Rossum (former Benevolent Dictator For Life) in 1989 +# - *High level* +# - Far removed from machine language +# - Written in C (slightly lower level) +# - Designed to be readable +# - Many versions, currently on major version 3. + +# %% [markdown] id="rC4AwjL06k0q" +# ### Why learn Python? +# +# - (Relatively) easy to learn +# - Widely used +# - Easy to install and run on a variety of platforms +# - Many resources available +# - Many good packages and libraries available + +# %% [markdown] id="xyNb4mPMcQCd" +# ## Notebooks 101 +# +# Let's get started! + +# %% [markdown] id="4lc8nYpP9mtf" +# ### Copy a notebook to your own Google Drive +# +# You can only run code in this notebook, not edit it. To edit it, you need to copy it to [your own Google Drive](https://drive.google.com) first. +# +# To do so, click on the *File* menu and choose the ninth option from the top: *Save a copy in Drive*. You can now find the copy in your drive, in the directory *Colab Notebooks*. +# +# Tip: give it a recognizable name (e.g. `Module 1 - my solutions`). +# + +# %% [markdown] id="8zoSNQtC_6_v" +# ### Adjust some settings +# +# Open the *Settings*, in the *Tools* menu or by clicking the cogwheel icon in the top right. Open the *Editor* section. +# +# - Set *Indentation width in spaces* to **4**. +# - Switch *Show line numbers* **on**. +# +# The other settings are up to you. + +# %% [markdown] id="qtRYKOWnB5Wl" +# ### Execute Python code +# +# Press the triangle icon in the top left corner of a code block to run it, *OR* type **ctrl-enter** when the text cursor is in the code block. Type **ctrl-shift-enter** to run only the current line. +# +# If you open a notebook for the first time, or if you have not run any code for a while, it takes a while before you see a result. + +# %% id="MdA4_KyrDAsf" +print('Hello') +print('Goodbye') + +# %% [markdown] id="AoBoZqIeFEru" +# ## Python 101 + +# %% [markdown] id="ZOqgk7VAFKnJ" +# ### `print` +# +# In the previous code block, we saw the `print` function in action. It causes Python to "talk" to us. + +# %% [markdown] id="NOtudpGlFe78" +# ### Values +# +# Between the parentheses `( )` of the `print` function, we put the *value* that we want Python to show. We can also write the value without passing it to `print`. + +# %% id="in8WoUafGEyK" +'Hello' + +# %% [markdown] id="BHQPB7LuQ1mf" +# A bare value does not always cause output. We will explore this in the first exercise. + +# %% [markdown] id="Src0ZdRKGHtl" +# ### Comments +# +# A comment starts with a hash `#` and runs until the end of the line. Python ignores comments. You can use them as notes for yourself and for other programmers. + +# %% id="ssP4GWUOIxWS" +# Python will not look here! + +print('Hello') # comments can start after code + +# %% [markdown] id="YxXeF4u5I2c_" +# Use comments to explain *why* your code was written the way it is. You will be grateful to yourself when you read your own code after a few weeks! + +# %% id="nMTlvlbbJUWu" +# Useless comment: + +# Print "Hello". +print('Hello') + +# Useful comment: + +# Everyone writes "Hello" in this exercise, but +# I prefer something a little bit more British. +print('How do you do?') + +# %% [markdown] id="ZhgCPUNTLIZX" +# ### `pass` +# +# You can write `pass` to do nothing. It behaves almost like a comment, but isn't. It is sometimes useful as a placeholder, as we will see later on in the course. + +# %% id="oS7kfP3OLtJn" +pass + +# %% [markdown] id="pKIEfocbMaIR" +# ## Exercise 1.1: Try it out +# +# Read each of the code blocks below. Try to predict what it will do, then run the block to check your prediction. +# +# Do you see any (subtle or not-so subtle) surprises? Can you explain them? + +# %% id="6hlNxRNNM1fV" +print(1) +1 + +# %% id="VIm6FdRIM6OE" +print('oops') +'oops' + +# %% id="_MN48xz5NAya" +print() + +# %% id="g9WKeIA2NVIA" +print('apricot') +print('banana') +print('cherry') + +# %% id="NCbCfbHaNafJ" +'apricot' +'banana' +'cherry' + +# %% id="-ZuiJ92yNqpi" +# apricot +# banana +# cherry + +# %% id="b9bTbrNSNwwn" +print('apricot') +'banana' +# cherry + +# %% id="GYmcSm6iOAiA" +# apricot +'banana' +print('cherry') + +# %% id="rbjHe9KbOzFb" +print('apricot') +'banana' +pass + +# %% id="V1GiIP_ZNK8H" +print(pass) + +# %% id="sNNoSfndOSiw" +print(#oops) + +# %% id="6IZS1NUuTdOX" +print('apricot', 'banana') + +# %% [markdown] id="Tr3wPlHMeBCI" +# ## Exercise 1.2: Hello, world! +# +# The classic way to demonstrate a programming language's syntax is the hello world-program. This is the minimal amount of code required to output the text "Hello, world!" to the user. +# +# Write a hello world-program for Python. + +# %% id="jN7WTVOOSq2C" +# Your solution here + +# %% [markdown] id="zI0ohEpPUwpC" +# ## Next module +# +# [2. Values and expressions](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp) diff --git a/lessons/02 values and expressions.ipynb b/lessons/02 values and expressions.ipynb index fb82463..9f6dfa1 100644 --- a/lessons/02 values and expressions.ipynb +++ b/lessons/02 values and expressions.ipynb @@ -1 +1,1277 @@ -{"cells":[{"cell_type":"markdown","metadata":{"id":"fqMJHzNk5yXQ"},"source":["# Module 2: Values and Expressions\n","\n","### CDH course \"Programming in Python\"\n","\n","[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n","\n","Previous module: [1. Introduction](https://colab.research.google.com/drive/1i4wJpUIr77Fh1renWNjt00Bd51NbC1nB)\n","\n","### This module\n","\n","- The basic types of values that exist in Python.\n","- How to store values for later use.\n","- How to create values from other values."]},{"cell_type":"markdown","metadata":{"id":"AqGUR1POYVNL"},"source":["## Primitive types and values\n","\n","> In computer science, primitive data types are a set of basic data types from which all other data types are constructed. [wikipedia](https://en.wikipedia.org/wiki/Primitive_data_type)\n","\n","In Python:\n","\n","- integer - `int`\n","- floating point - `float`\n","- string - `str`\n","- boolean - `bool`\n","- none - `None`\n"]},{"cell_type":"markdown","metadata":{"id":"8sSOrG7FdMT5"},"source":["### integer\n","'whole number'"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"wTlfkN-Sa_MG"},"outputs":[],"source":["1\n","2\n","-12\n","289883891009329819081202\n","0"]},{"cell_type":"markdown","metadata":{"id":"iBCZZWfDdS7o"},"source":["### floating point\n","'decimal number'"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"2JbaI2-pbTF5"},"outputs":[],"source":["2.1\n","-3.2\n","12.8\n","0.0\n",".8"]},{"cell_type":"markdown","metadata":{"id":"om0zZYCBdd8F"},"source":["### string\n","'text'"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"DB6KZC5Sblwy"},"outputs":[],"source":["'hello'\n","'we can choose between single'\n","\"or double quotes!\"\n","\n","\"I don't want to do this\"\n","\n","# escaping difficult characters with \\ \n","\"I won't say \\\"banana\\\"\\\\\"\n","\n","# line breaks are preserved\n","'''a long string\n","\n","that can go over multiple lines'''\n","\n","''\n","\"\""]},{"cell_type":"markdown","metadata":{"id":"dQ6u3Syk4fS4"},"source":["*escape characters* only show when printing"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"7QCMtj3S4d6E"},"outputs":[],"source":["# escape character: \\n (newline)\n","print('hello \\n world')\n","'hello \\n world'"]},{"cell_type":"markdown","metadata":{"id":"ouf6r2zmdjY9"},"source":["### boolean\n","true or false"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"8n81rHXFb9cl"},"outputs":[],"source":["True\n","False\n","false\n","true\n","\"True\""]},{"cell_type":"markdown","metadata":{"id":"xS0efw6fdoW9"},"source":["### None\n","'nothing here'"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"fOZdR0YUcFRb"},"outputs":[],"source":["None\n","none\n","\"none\""]},{"cell_type":"markdown","metadata":{"id":"jockLUXXd2Ad"},"source":["## Difference between types\n","\n","Use `type(value)` to find the type of a value."]},{"cell_type":"code","execution_count":1,"metadata":{"id":"lHtfczHxd89N"},"outputs":[],"source":["type(10)\n","type(3.14159)\n","type('hello')\n","type(True)\n","type('True')\n","type(None)"]},{"cell_type":"markdown","metadata":{"id":"kZffa20rXLOQ"},"source":["Careful! A number in quotes is not actually a number, but a string. A string that looks like a number will **NOT** behave like a number."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"-Zv10HJFXUW8"},"outputs":[],"source":["type('10')\n","type('3.14159')"]},{"cell_type":"markdown","metadata":{"id":"rJ_mNXXAYIpy"},"source":["Likewise, a string that looks like a Boolean will **NOT** behave like a boolean."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"PN0FTHz2YRPM"},"outputs":[],"source":["type('False')"]},{"cell_type":"markdown","metadata":{"id":"-0p3SmmgWdif"},"source":["`int`, `str` etcetera can be used to convert values between different types. Careful though, not all conversions you can think of will do what you expect!"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"FPeklTLXWuXl"},"outputs":[],"source":["int('10')\n","float('10')\n","str(10)\n","str(False)\n","bool('False') # !!!\n","None # no conversion exists"]},{"cell_type":"markdown","metadata":{"id":"YGU7CclEYz2y"},"source":["It is useful to know that each character in a string is stored as an integer. `ord(character)` lets you retrieve that integer."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"U_63E6jfab6N"},"outputs":[],"source":["ord('2')\n","ord('a')\n","ord('A')\n","ord('\\n')\n","ord('\\t')"]},{"cell_type":"markdown","metadata":{"id":"0dh3t4uZa9AT"},"source":["`chr(integer)` does the opposite: it lets you retrieve the character corresponding to a given integer."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"ffIkwHahcGv9"},"outputs":[],"source":["chr(49)\n","chr(98)\n","chr(946)\n","chr(22823)\n","chr(129327)"]},{"cell_type":"markdown","metadata":{"id":"InNZNIOpezlG"},"source":["## Variables\n","\n","- container holding a value\n","- assigning\n"," - `variable_name = value`\n","- reassigning\n"," - `name = 'sheean'`\n"," - `name = 'julian'`\n","- in Python, no strict type\n"," - `a = 1`\n"," - `a = 'hello'`"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"wgaSYh4yfGCx"},"outputs":[],"source":["a = 1\n","b = \"hello\"\n","c = True\n","d = None\n","\n","print(a)"]},{"cell_type":"markdown","metadata":{"id":"BD2s2EqKfQtx"},"source":["Tip: the notebook remembers variables from previous cells, but only if you executed them."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"pJ-N_rc8fZia"},"outputs":[],"source":["print(a)\n","print(b)\n","print(c)"]},{"cell_type":"markdown","metadata":{"id":"uADEckPZgGt_"},"source":["Anything can go in a variable, not just single values"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"ehEig-sCgL48"},"outputs":[],"source":["d = a\n","print(d)\n","\n","e = print\n","e(b)"]},{"cell_type":"markdown","metadata":{"id":"TaiYZ3R6ghq0"},"source":["Beware! When we assign a value, the variable stores it at that exact moment. \n","Changing one variable does not change another.\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"pIIq1la9g1Qr"},"outputs":[],"source":["number = 0\n","\n","container = number\n","\n","print(container)\n","\n","number = 1\n","\n","print(container)\n","print(number)\n"]},{"cell_type":"markdown","metadata":{"id":"nMn_xu6Ih3hJ"},"source":["### Variable names\n","\n","#### Rules\n","\n","- must start with a letter or underscore (`_`)\n","- can only contain letters (`a-z`, `A-Z`), numbers (`0-9`), and underscore (`_`)\n","- variable names are case sensitive. `name` is a different variable from `Name` \n","\n","#### Conventions\n","\n","Next to the rules, there are some conventions. \n","Please stick to them, so others will have no trouble reading and understanding your code.\n","- lowercase\n"," - DO: `name`\n"," - DON'T: `Name`\n"," - DON'T: `NAME`1\n","- readable\n"," - DO: `name`\n"," - DON'T: `n_a_m_e`\n"," - DON'T: `x098277`\n","- multiple words2\n"," - DO: `first_name` (*snake_case*)\n"," - DON'T: `FirstName` (*PascalCase*)\n"," - DON'T `firstName` (*camelCase*)\n","- comprehensive (but short)\n"," - DO: `name`\n"," - DON'T: `n`\n"," - DON'T: `the_name_of_the_person_i_want_to_print` \n","\n","1 *Fully uppercased variable names actually indicate constants, and adhere to Python conventions. We will explain this later.* \n","2 *This is purely a cultural thing. Other languages (and even other concepts within Python) use different casing*"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"m4pTI2BEn-Z-"},"outputs":[],"source":[]},{"cell_type":"markdown","metadata":{"id":"pDU1uK2Igmki"},"source":["## Exercise 2.1: Variables and state\n","\n","In each of the code blocks below, try to predict what will be printed, then run the code. If your guess was incorrect, try to figure out why the result is different. If your guess was correct, celebrate!"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"FydkKnx_hUPq"},"outputs":[],"source":["flavor = 'vanilla'\n","print(flavor)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"LTWods50h-Ij"},"outputs":[],"source":["temperature = 70\n","print(flavor)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"l2vT4L7piGRf"},"outputs":[],"source":["print(temperature)\n","temperature = 35\n","print(temperature)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"k1Z_yWLXiWy7"},"outputs":[],"source":["dog_name = 'Bobby'\n","cat_name = 'Garfield'\n","dog_name = cat_name\n","cat_name = dog_name\n","print(dog_name)\n","print(cat_name)"]},{"cell_type":"markdown","metadata":{"id":"BZ50KykuAYPs"},"source":["Before running the following code, try to explain why it does *not* output `chocolate`."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"X3xHg6K4Aicn"},"outputs":[],"source":["sweet = 'chocolate'\n","savory = 'cheese'\n","dessert = 'sweet'\n","print(dessert)"]},{"cell_type":"markdown","metadata":{"id":"0az3tNK7WD3G"},"source":["In each of the following lines of code, replace `None` by a value of your choice. Make it such that `my_int` is an integer, `my_float` is a float, `my_bool` is a boolean and `my_string` is a string."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"lY-M8mfSXDfG"},"outputs":[],"source":["my_int = None\n","my_float = None\n","my_bool = None\n","my_string = None"]},{"cell_type":"markdown","metadata":{"id":"dvNIQh7KYuJb"},"source":["## Exercise 2.2: Bonus"]},{"cell_type":"markdown","metadata":{"id":"GI9fyUO8XOcp"},"source":["How could you verify in code whether the variables you wrote above have the correct type? Write this code below.\n","\n","Hint: scroll back to the section [Difference between types](#scrollTo=Difference_between_types)."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"Om6z53RXYBoS"},"outputs":[],"source":[]},{"cell_type":"markdown","metadata":{"id":"h6UKIMXCj3w9"},"source":["In the code block below, replace the question mark `?` by a variable name. Make it such that the final output is `'Hooray!'`."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"3MI6_DY7ku30"},"outputs":[],"source":["cheer = 'Hooray!'\n","alarm = 'Oh no!'\n","anthem = 'Wilhelmus van Nassauwe'\n","alarm = anthem\n","? = cheer\n","cheer = anthem\n","anthem = alarm\n","print(anthem)"]},{"cell_type":"markdown","metadata":{"id":"ZXd6jCn90CA_"},"source":["## Expressions\n","\n","- An expression is a combination between *operands* and *operators*. \n","- Think of arithmetic: `1 + 2`\n","\n"]},{"cell_type":"markdown","metadata":{"id":"s8uJy_kI9C8S"},"source":["### Arithmetic"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"f9ACtc0e13i-"},"outputs":[],"source":["1 + 6\n","5 - 2\n","10 / 3\n","5 * 5\n","\n","a = 8\n","b = 10\n","\n","c = b - a\n","print(c)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"w9cfOflP8DEJ"},"outputs":[],"source":["# multiple operators\n","4 + (3 * 5)\n","(4 + 3) * 5"]},{"cell_type":"markdown","metadata":{"id":"zkt9aNKm28Lc"},"source":["### String expressions"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"6os4-rt43ThF"},"outputs":[],"source":["\"hello\" + \" world!\"\n","a = \"my age is: \"\n","b = 33\n","\n","a + b"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"SyrelaMu42UZ"},"outputs":[],"source":["a = 'hello'\n","a * 5"]},{"cell_type":"markdown","metadata":{"id":"DPbfP65xXlfb"},"source":["## Reassigning variables\n","\n","- You can use a variable itself when reassigning. This is useful when trying to expand an existing variable."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"ogL2Iw3-Xs15"},"outputs":[],"source":["a = \"hello\"\n","a = a + \", world!\"\n","print(a)\n","\n","b = 'bye'\n","b = b + b\n","print(b)\n","\n","b = b + b\n","print(b)"]},{"cell_type":"markdown","metadata":{"id":"z_bXvnya5J2_"},"source":["## Exercise 2.3: Expressions\n","\n"]},{"cell_type":"markdown","metadata":{"id":"j9xhznQlbCBf"},"source":["1. Try to predict the value of each of the following code blocks. Can you explain any surprises?"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"0QcMB4xwbSfL"},"outputs":[],"source":["1 + 1"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"eHKN9kP9bWkm"},"outputs":[],"source":["1 * 1"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"uINoRNNbbXwJ"},"outputs":[],"source":["a = 1\n","b = 2\n","a + b"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"4xyWkYlnbc_8"},"outputs":[],"source":["c = b\n","a * b * c"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"7tW2T4mebljv"},"outputs":[],"source":["'hakuna' + 'matata'"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"uEdPHIhBb2Mw"},"outputs":[],"source":["liquid = 'water~'\n","liquid * 3"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"-D7BB50Qceo2"},"outputs":[],"source":["5 - 2 - 1"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"OQUGr5rGck3m"},"outputs":[],"source":["5 - (2 - 1)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"Jc8fawaIdCD5"},"outputs":[],"source":["income = 100\n","tax = 20\n","net_income = income - tax\n","tax = 15\n","net_income"]},{"cell_type":"markdown","metadata":{"id":"TlLkcRv-droI"},"source":["2. In the code block below, change the values of `character` and `multiplier` so that the output becomes `'Goooood!'`."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"pYZUkeDJenob"},"outputs":[],"source":["character = 'o'\n","multiplier = 5\n","\n","begin = 'G'\n","middle = character * multiplier\n","end = 'd!'\n","\n","begin + middle + end"]},{"cell_type":"markdown","metadata":{"id":"G8X4n_a8a5tl"},"source":["3. Rewrite your Hello world-program:\n"," - Build the \"hello, world!\" string using multiple variables.\n"," - Make the program say \"hello, world!\" 3 times, each time on a new line"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"nDUVvhDEfIVI"},"outputs":[],"source":[]},{"cell_type":"markdown","metadata":{"id":"3HCqdTj2fVPK"},"source":["## Exercise 2.4: Bonus"]},{"cell_type":"markdown","metadata":{"id":"EKFdkLkWa8EY"},"source":["1. Find out and describe what the following operators do:\n"," - `%` (e.g. `10 % 2`)\n"," - `//` (e.g. `10 // 2`)\n"," - `**` (e.g. `10 ** 2`)\n"," - Tip: write the expressions using variables. Change the variables and see how it affects the outcome."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"V59a7vpDfzO2"},"outputs":[],"source":[]},{"cell_type":"markdown","metadata":{"id":"o0WYIAUla-AX"},"source":["2. Predict what will be printed on each line in the following code block, then run the code to check your answer. If you made any mistakes, try to figure out what is causing the difference."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"Z9Vd9pdIUzT5"},"outputs":[],"source":["word = 'stylometry'\n","repeats = 3\n","extra = 2\n","\n","print((word * repeats) + str(extra))\n","print(word * (repeats + extra))\n","print(word * repeats + str(extra))\n","print((word + str(extra)) * repeats)\n","print(word + str(extra * repeats))\n","print(word + str(extra) * repeats)"]},{"cell_type":"markdown","metadata":{"id":"DRObBQZHgsIG"},"source":["3. Using just the variables `word`, `repeats` and `extra` from the previous code block, write a single big expression that evaluates to `'stylometry555stylometry'`."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"uLuiUk10gqwM"},"outputs":[],"source":[]},{"cell_type":"markdown","metadata":{"id":"QaamMzJY6ISR"},"source":["## Boolean expressions\n","Expressions that result in `True` or `False`\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"kLjya4Au6Ucu"},"outputs":[],"source":["# equals\n","1 == 1\n","\"hello\" == 'hello'\n","'2' == 2"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"tYNp6nUp7ixW"},"outputs":[],"source":["# does not equal\n","1 != 1\n","\"hello\" != 'hello'\n","'2' != 2"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"oixxKDHV7s5J"},"outputs":[],"source":["# greater than\n","2 > 1\n","2 > 5\n","'b' > 'a'\n","\n","2 > 2\n","\n","# greater than (or equal)\n","2 >= 2"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"Z198kfCf75sU"},"outputs":[],"source":["# less than\n","2 < 1\n","2 < 5\n","'b' < 'a'\n","2 < 2\n","\n","# less than (or equal)\n","2 <= 2"]},{"cell_type":"markdown","metadata":{"id":"pvrcgKU18OrJ"},"source":["## Exercise 2.5: boolean expressions"]},{"cell_type":"markdown","metadata":{"id":"sWdnjezoil9j"},"source":["1. Predict for each boolean expression below: `True` or `False`? If any of your predictions is incorrect, try to find the reason."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"RpSvrom3Z8fQ"},"outputs":[],"source":["name = 'Sheean'\n","height = 2\n","\n","name * height == 'Sheean Sheean'\n","height < 2\n","2 <= height\n","1 < height <= 2\n","2 <= height < 1\n","name <= 'Julian'\n","height * 3 + 1 >= height"]},{"cell_type":"markdown","metadata":{"id":"zJiaGIlZBN_P"},"source":["2. Run the code block below. Did you expect this result? Can you explain it?"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"_PrnFf2lioMB"},"outputs":[],"source":["1 == True"]},{"cell_type":"markdown","metadata":{"id":"q8QviA70kdQE"},"source":["3. Replace one value in each of the following expressions so the expression becomes `True`."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"qDQ9Ob5Zkrqm"},"outputs":[],"source":["1 > height\n","height == 0\n","'Julian' > name * 2\n","name < 1 * name"]},{"cell_type":"markdown","metadata":{"id":"YbdV7SQVmDVV"},"source":["4. Replace one operator in each of the following expressions so the expression becomes `True`."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"hzjXIwkAmChv"},"outputs":[],"source":["5 < 4\n","2 + 1 == 1\n","3 + 3 == 3 + 2"]},{"cell_type":"markdown","metadata":{"id":"SwyAMDzDn2_X"},"source":["5. The *Groot woordenboek van de Nederlandse taal* by Van Dale publishers, colloquially known as \"De dikke Van Dale\", is a three-tome Dutch dictionary. The first tome contains words starting a–i, the second j–q and the third r–z. In the code block below, write an expression that will tell you whether `word` should be in the second tome. Try different values of `word` to verify that your solution is correct. You do not need to check whether the word is actually Dutch!"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"lmCGfx_DqMWe"},"outputs":[],"source":["word = 'archaïsch'\n","\n","# your expression here"]},{"cell_type":"markdown","metadata":{"id":"jXSxbjf4q6q5"},"source":["## Next module\n","\n","[3. Conditionals](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw)"]}],"metadata":{"colab":{"provenance":[]},"kernelspec":{"display_name":"Python 3","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.10.12"}},"nbformat":4,"nbformat_minor":0} +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "fqMJHzNk5yXQ" + }, + "source": [ + "# Module 2: Values and Expressions\n", + "\n", + "### CDH course \"Programming in Python\"\n", + "\n", + "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "\n", + "Previous module: [1. Introduction](https://colab.research.google.com/drive/1i4wJpUIr77Fh1renWNjt00Bd51NbC1nB)\n", + "\n", + "### This module\n", + "\n", + "- The basic types of values that exist in Python.\n", + "- How to store values for later use.\n", + "- How to create values from other values." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "AqGUR1POYVNL" + }, + "source": [ + "## Primitive types and values\n", + "\n", + "> In computer science, primitive data types are a set of basic data types from which all other data types are constructed. [wikipedia](https://en.wikipedia.org/wiki/Primitive_data_type)\n", + "\n", + "In Python:\n", + "\n", + "- integer - `int`\n", + "- floating point - `float`\n", + "- string - `str`\n", + "- boolean - `bool`\n", + "- none - `None`\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8sSOrG7FdMT5" + }, + "source": [ + "### integer\n", + "'whole number'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "wTlfkN-Sa_MG" + }, + "outputs": [], + "source": [ + "1\n", + "2\n", + "-12\n", + "289883891009329819081202\n", + "0" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "iBCZZWfDdS7o" + }, + "source": [ + "### floating point\n", + "'decimal number'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "2JbaI2-pbTF5" + }, + "outputs": [], + "source": [ + "2.1\n", + "-3.2\n", + "12.8\n", + "0.0\n", + ".8" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "om0zZYCBdd8F" + }, + "source": [ + "### string\n", + "'text'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "DB6KZC5Sblwy" + }, + "outputs": [], + "source": [ + "'hello'\n", + "'we can choose between single'\n", + "\"or double quotes!\"\n", + "\n", + "\"I don't want to do this\"\n", + "\n", + "# escaping difficult characters with \\ \n", + "\"I won't say \\\"banana\\\"\\\\\"\n", + "\n", + "# line breaks are preserved\n", + "'''a long string\n", + "\n", + "that can go over multiple lines'''\n", + "\n", + "''\n", + "\"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "dQ6u3Syk4fS4" + }, + "source": [ + "*escape characters* only show when printing" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "7QCMtj3S4d6E" + }, + "outputs": [], + "source": [ + "# escape character: \\n (newline)\n", + "print('hello \\n world')\n", + "'hello \\n world'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ouf6r2zmdjY9" + }, + "source": [ + "### boolean\n", + "true or false" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "8n81rHXFb9cl" + }, + "outputs": [], + "source": [ + "True\n", + "False\n", + "false\n", + "true\n", + "\"True\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xS0efw6fdoW9" + }, + "source": [ + "### None\n", + "'nothing here'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "fOZdR0YUcFRb" + }, + "outputs": [], + "source": [ + "None\n", + "none\n", + "\"none\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "jockLUXXd2Ad" + }, + "source": [ + "## Difference between types\n", + "\n", + "Use `type(value)` to find the type of a value." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "id": "lHtfczHxd89N" + }, + "outputs": [], + "source": [ + "type(10)\n", + "type(3.14159)\n", + "type('hello')\n", + "type(True)\n", + "type('True')\n", + "type(None)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kZffa20rXLOQ" + }, + "source": [ + "Careful! A number in quotes is not actually a number, but a string. A string that looks like a number will **NOT** behave like a number." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "-Zv10HJFXUW8" + }, + "outputs": [], + "source": [ + "type('10')\n", + "type('3.14159')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rJ_mNXXAYIpy" + }, + "source": [ + "Likewise, a string that looks like a Boolean will **NOT** behave like a boolean." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "PN0FTHz2YRPM" + }, + "outputs": [], + "source": [ + "type('False')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-0p3SmmgWdif" + }, + "source": [ + "`int`, `str` etcetera can be used to convert values between different types. Careful though, not all conversions you can think of will do what you expect!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "FPeklTLXWuXl" + }, + "outputs": [], + "source": [ + "int('10')\n", + "float('10')\n", + "str(10)\n", + "str(False)\n", + "bool('False') # !!!\n", + "None # no conversion exists" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "YGU7CclEYz2y" + }, + "source": [ + "It is useful to know that each character in a string is stored as an integer. `ord(character)` lets you retrieve that integer." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "U_63E6jfab6N" + }, + "outputs": [], + "source": [ + "ord('2')\n", + "ord('a')\n", + "ord('A')\n", + "ord('\\n')\n", + "ord('\\t')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0dh3t4uZa9AT" + }, + "source": [ + "`chr(integer)` does the opposite: it lets you retrieve the character corresponding to a given integer." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ffIkwHahcGv9" + }, + "outputs": [], + "source": [ + "chr(49)\n", + "chr(98)\n", + "chr(946)\n", + "chr(22823)\n", + "chr(129327)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "InNZNIOpezlG" + }, + "source": [ + "## Variables\n", + "\n", + "- container holding a value\n", + "- assigning\n", + " - `variable_name = value`\n", + "- reassigning\n", + " - `name = 'sheean'`\n", + " - `name = 'julian'`\n", + "- in Python, no strict type\n", + " - `a = 1`\n", + " - `a = 'hello'`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "wgaSYh4yfGCx" + }, + "outputs": [], + "source": [ + "a = 1\n", + "b = \"hello\"\n", + "c = True\n", + "d = None\n", + "\n", + "print(a)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BD2s2EqKfQtx" + }, + "source": [ + "Tip: the notebook remembers variables from previous cells, but only if you executed them." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "pJ-N_rc8fZia" + }, + "outputs": [], + "source": [ + "print(a)\n", + "print(b)\n", + "print(c)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "uADEckPZgGt_" + }, + "source": [ + "Anything can go in a variable, not just single values" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ehEig-sCgL48" + }, + "outputs": [], + "source": [ + "d = a\n", + "print(d)\n", + "\n", + "e = print\n", + "e(b)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "TaiYZ3R6ghq0" + }, + "source": [ + "Beware! When we assign a value, the variable stores it at that exact moment. \n", + "Changing one variable does not change another.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "pIIq1la9g1Qr", + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "number = 0\n", + "\n", + "container = number\n", + "\n", + "print(container)\n", + "\n", + "number = 1\n", + "\n", + "print(container)\n", + "print(number)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "nMn_xu6Ih3hJ" + }, + "source": [ + "### Variable names\n", + "\n", + "#### Rules\n", + "\n", + "- must start with a letter or underscore (`_`)\n", + "- can only contain letters (`a-z`, `A-Z`), numbers (`0-9`), and underscore (`_`)\n", + "- variable names are case sensitive. `name` is a different variable from `Name` \n", + "\n", + "#### Conventions\n", + "\n", + "Next to the rules, there are some conventions. \n", + "Please stick to them, so others will have no trouble reading and understanding your code.\n", + "- lowercase\n", + " - DO: `name`\n", + " - DON'T: `Name`\n", + " - DON'T: `NAME`1\n", + "- readable\n", + " - DO: `name`\n", + " - DON'T: `n_a_m_e`\n", + " - DON'T: `x098277`\n", + "- multiple words2\n", + " - DO: `first_name` (*snake_case*)\n", + " - DON'T: `FirstName` (*PascalCase*)\n", + " - DON'T `firstName` (*camelCase*)\n", + "- comprehensive (but short)\n", + " - DO: `name`\n", + " - DON'T: `n`\n", + " - DON'T: `the_name_of_the_person_i_want_to_print` \n", + "\n", + "1 *Fully uppercased variable names actually indicate constants, and adhere to Python conventions. We will explain this later.* \n", + "2 *This is purely a cultural thing. Other languages (and even other concepts within Python) use different casing*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "m4pTI2BEn-Z-" + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pDU1uK2Igmki" + }, + "source": [ + "## Exercise 2.1: Variables and state\n", + "\n", + "In each of the code blocks below, try to predict what will be printed, then run the code. If your guess was incorrect, try to figure out why the result is different. If your guess was correct, celebrate!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "FydkKnx_hUPq" + }, + "outputs": [], + "source": [ + "flavor = 'vanilla'\n", + "print(flavor)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "LTWods50h-Ij" + }, + "outputs": [], + "source": [ + "temperature = 70\n", + "print(flavor)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "l2vT4L7piGRf" + }, + "outputs": [], + "source": [ + "print(temperature)\n", + "temperature = 35\n", + "print(temperature)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "k1Z_yWLXiWy7" + }, + "outputs": [], + "source": [ + "dog_name = 'Bobby'\n", + "cat_name = 'Garfield'\n", + "dog_name = cat_name\n", + "cat_name = dog_name\n", + "print(dog_name)\n", + "print(cat_name)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BZ50KykuAYPs" + }, + "source": [ + "Before running the following code, try to explain why it does *not* output `chocolate`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "X3xHg6K4Aicn" + }, + "outputs": [], + "source": [ + "sweet = 'chocolate'\n", + "savory = 'cheese'\n", + "dessert = 'sweet'\n", + "print(dessert)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0az3tNK7WD3G" + }, + "source": [ + "In each of the following lines of code, replace `None` by a value of your choice. Make it such that `my_int` is an integer, `my_float` is a float, `my_bool` is a boolean and `my_string` is a string." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "lY-M8mfSXDfG" + }, + "outputs": [], + "source": [ + "my_int = None\n", + "my_float = None\n", + "my_bool = None\n", + "my_string = None" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "dvNIQh7KYuJb" + }, + "source": [ + "## Exercise 2.2: Bonus" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GI9fyUO8XOcp" + }, + "source": [ + "How could you verify in code whether the variables you wrote above have the correct type? Write this code below.\n", + "\n", + "Hint: scroll back to the section [Difference between types](#scrollTo=Difference_between_types)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Om6z53RXYBoS" + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "h6UKIMXCj3w9" + }, + "source": [ + "In the code block below, replace the question mark `?` by a variable name. Make it such that the final output is `'Hooray!'`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "3MI6_DY7ku30" + }, + "outputs": [], + "source": [ + "cheer = 'Hooray!'\n", + "alarm = 'Oh no!'\n", + "anthem = 'Wilhelmus van Nassauwe'\n", + "alarm = anthem\n", + "? = cheer\n", + "cheer = anthem\n", + "anthem = alarm\n", + "print(anthem)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ZXd6jCn90CA_" + }, + "source": [ + "## Expressions\n", + "\n", + "- An expression is a combination between *operands* and *operators*. \n", + "- Think of arithmetic: `1 + 2`\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "s8uJy_kI9C8S" + }, + "source": [ + "### Arithmetic" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "f9ACtc0e13i-" + }, + "outputs": [], + "source": [ + "1 + 6\n", + "5 - 2\n", + "10 / 3\n", + "5 * 5\n", + "\n", + "a = 8\n", + "b = 10\n", + "\n", + "c = b - a\n", + "print(c)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "w9cfOflP8DEJ" + }, + "outputs": [], + "source": [ + "# multiple operators\n", + "4 + (3 * 5)\n", + "(4 + 3) * 5" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zkt9aNKm28Lc" + }, + "source": [ + "### String expressions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "6os4-rt43ThF" + }, + "outputs": [], + "source": [ + "\"hello\" + \" world!\"\n", + "a = \"my age is: \"\n", + "b = 33\n", + "\n", + "a + b" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "SyrelaMu42UZ" + }, + "outputs": [], + "source": [ + "a = 'hello'\n", + "a * 5" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "DPbfP65xXlfb" + }, + "source": [ + "## Reassigning variables\n", + "\n", + "- You can use a variable itself when reassigning. This is useful when trying to expand an existing variable." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ogL2Iw3-Xs15" + }, + "outputs": [], + "source": [ + "a = \"hello\"\n", + "a = a + \", world!\"\n", + "print(a)\n", + "\n", + "b = 'bye'\n", + "b = b + b\n", + "print(b)\n", + "\n", + "b = b + b\n", + "print(b)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "z_bXvnya5J2_" + }, + "source": [ + "## Exercise 2.3: Expressions\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "j9xhznQlbCBf" + }, + "source": [ + "1. Try to predict the value of each of the following code blocks. Can you explain any surprises?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "0QcMB4xwbSfL" + }, + "outputs": [], + "source": [ + "1 + 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "eHKN9kP9bWkm" + }, + "outputs": [], + "source": [ + "1 * 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "uINoRNNbbXwJ" + }, + "outputs": [], + "source": [ + "a = 1\n", + "b = 2\n", + "a + b" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "4xyWkYlnbc_8" + }, + "outputs": [], + "source": [ + "c = b\n", + "a * b * c" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "7tW2T4mebljv" + }, + "outputs": [], + "source": [ + "'hakuna' + 'matata'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "uEdPHIhBb2Mw" + }, + "outputs": [], + "source": [ + "liquid = 'water~'\n", + "liquid * 3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "-D7BB50Qceo2" + }, + "outputs": [], + "source": [ + "5 - 2 - 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "OQUGr5rGck3m" + }, + "outputs": [], + "source": [ + "5 - (2 - 1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Jc8fawaIdCD5" + }, + "outputs": [], + "source": [ + "income = 100\n", + "tax = 20\n", + "net_income = income - tax\n", + "tax = 15\n", + "net_income" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "TlLkcRv-droI" + }, + "source": [ + "2. In the code block below, change the values of `character` and `multiplier` so that the output becomes `'Goooood!'`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "pYZUkeDJenob" + }, + "outputs": [], + "source": [ + "character = 'o'\n", + "multiplier = 5\n", + "\n", + "begin = 'G'\n", + "middle = character * multiplier\n", + "end = 'd!'\n", + "\n", + "begin + middle + end" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "G8X4n_a8a5tl" + }, + "source": [ + "3. Rewrite your Hello world-program:\n", + " - Build the \"hello, world!\" string using multiple variables.\n", + " - Make the program say \"hello, world!\" 3 times, each time on a new line" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "nDUVvhDEfIVI" + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3HCqdTj2fVPK" + }, + "source": [ + "## Exercise 2.4: Bonus" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "EKFdkLkWa8EY" + }, + "source": [ + "1. Find out and describe what the following operators do:\n", + " - `%` (e.g. `10 % 2`)\n", + " - `//` (e.g. `10 // 2`)\n", + " - `**` (e.g. `10 ** 2`)\n", + " - Tip: write the expressions using variables. Change the variables and see how it affects the outcome." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "V59a7vpDfzO2" + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "o0WYIAUla-AX" + }, + "source": [ + "2. Predict what will be printed on each line in the following code block, then run the code to check your answer. If you made any mistakes, try to figure out what is causing the difference." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Z9Vd9pdIUzT5" + }, + "outputs": [], + "source": [ + "word = 'stylometry'\n", + "repeats = 3\n", + "extra = 2\n", + "\n", + "print((word * repeats) + str(extra))\n", + "print(word * (repeats + extra))\n", + "print(word * repeats + str(extra))\n", + "print((word + str(extra)) * repeats)\n", + "print(word + str(extra * repeats))\n", + "print(word + str(extra) * repeats)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "DRObBQZHgsIG" + }, + "source": [ + "3. Using just the variables `word`, `repeats` and `extra` from the previous code block, write a single big expression that evaluates to `'stylometry555stylometry'`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "uLuiUk10gqwM" + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "QaamMzJY6ISR" + }, + "source": [ + "## Boolean expressions\n", + "Expressions that result in `True` or `False`\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "kLjya4Au6Ucu" + }, + "outputs": [], + "source": [ + "# equals\n", + "1 == 1\n", + "\"hello\" == 'hello'\n", + "'2' == 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "tYNp6nUp7ixW" + }, + "outputs": [], + "source": [ + "# does not equal\n", + "1 != 1\n", + "\"hello\" != 'hello'\n", + "'2' != 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "oixxKDHV7s5J" + }, + "outputs": [], + "source": [ + "# greater than\n", + "2 > 1\n", + "2 > 5\n", + "'b' > 'a'\n", + "\n", + "2 > 2\n", + "\n", + "# greater than (or equal)\n", + "2 >= 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Z198kfCf75sU" + }, + "outputs": [], + "source": [ + "# less than\n", + "2 < 1\n", + "2 < 5\n", + "'b' < 'a'\n", + "2 < 2\n", + "\n", + "# less than (or equal)\n", + "2 <= 2" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pvrcgKU18OrJ" + }, + "source": [ + "## Exercise 2.5: boolean expressions" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "sWdnjezoil9j" + }, + "source": [ + "1. Predict for each boolean expression below: `True` or `False`? If any of your predictions is incorrect, try to find the reason." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "RpSvrom3Z8fQ" + }, + "outputs": [], + "source": [ + "name = 'Sheean'\n", + "height = 2\n", + "\n", + "name * height == 'Sheean Sheean'\n", + "height < 2\n", + "2 <= height\n", + "1 < height <= 2\n", + "2 <= height < 1\n", + "name <= 'Julian'\n", + "height * 3 + 1 >= height" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zJiaGIlZBN_P" + }, + "source": [ + "2. Run the code block below. Did you expect this result? Can you explain it?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "_PrnFf2lioMB" + }, + "outputs": [], + "source": [ + "1 == True" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "q8QviA70kdQE" + }, + "source": [ + "3. Replace one value in each of the following expressions so the expression becomes `True`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "qDQ9Ob5Zkrqm" + }, + "outputs": [], + "source": [ + "1 > height\n", + "height == 0\n", + "'Julian' > name * 2\n", + "name < 1 * name" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "YbdV7SQVmDVV" + }, + "source": [ + "4. Replace one operator in each of the following expressions so the expression becomes `True`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "hzjXIwkAmChv" + }, + "outputs": [], + "source": [ + "5 < 4\n", + "2 + 1 == 1\n", + "3 + 3 == 3 + 2" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SwyAMDzDn2_X" + }, + "source": [ + "5. The *Groot woordenboek van de Nederlandse taal* by Van Dale publishers, colloquially known as \"De dikke Van Dale\", is a three-tome Dutch dictionary. The first tome contains words starting a–i, the second j–q and the third r–z. In the code block below, write an expression that will tell you whether `word` should be in the second tome. Try different values of `word` to verify that your solution is correct. You do not need to check whether the word is actually Dutch!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "lmCGfx_DqMWe" + }, + "outputs": [], + "source": [ + "word = 'archaïsch'\n", + "\n", + "# your expression here" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "jXSxbjf4q6q5" + }, + "source": [ + "## Next module\n", + "\n", + "[3. Conditionals](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw)" + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "jupytext": { + "main_language": "python" + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/lessons/02 values and expressions.py b/lessons/02 values and expressions.py new file mode 100644 index 0000000..83ec85f --- /dev/null +++ b/lessons/02 values and expressions.py @@ -0,0 +1,575 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# name: python3 +# --- + +# %% [markdown] id="fqMJHzNk5yXQ" +# # Module 2: Values and Expressions +# +# ### CDH course "Programming in Python" +# +# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# +# Previous module: [1. Introduction](https://colab.research.google.com/drive/1i4wJpUIr77Fh1renWNjt00Bd51NbC1nB) +# +# ### This module +# +# - The basic types of values that exist in Python. +# - How to store values for later use. +# - How to create values from other values. + +# %% [markdown] id="AqGUR1POYVNL" +# ## Primitive types and values +# +# > In computer science, primitive data types are a set of basic data types from which all other data types are constructed. [wikipedia](https://en.wikipedia.org/wiki/Primitive_data_type) +# +# In Python: +# +# - integer - `int` +# - floating point - `float` +# - string - `str` +# - boolean - `bool` +# - none - `None` +# + +# %% [markdown] id="8sSOrG7FdMT5" +# ### integer +# 'whole number' + +# %% id="wTlfkN-Sa_MG" +1 +2 +-12 +289883891009329819081202 +0 + +# %% [markdown] id="iBCZZWfDdS7o" +# ### floating point +# 'decimal number' + +# %% id="2JbaI2-pbTF5" +2.1 +-3.2 +12.8 +0.0 +.8 + +# %% [markdown] id="om0zZYCBdd8F" +# ### string +# 'text' + +# %% id="DB6KZC5Sblwy" +'hello' +'we can choose between single' +"or double quotes!" + +"I don't want to do this" + +# escaping difficult characters with \ +"I won't say \"banana\"\\" + +# line breaks are preserved +'''a long string + +that can go over multiple lines''' + +'' +"" + +# %% [markdown] id="dQ6u3Syk4fS4" +# *escape characters* only show when printing + +# %% id="7QCMtj3S4d6E" +# escape character: \n (newline) +print('hello \n world') +'hello \n world' + +# %% [markdown] id="ouf6r2zmdjY9" +# ### boolean +# true or false + +# %% id="8n81rHXFb9cl" +True +False +false +true +"True" + +# %% [markdown] id="xS0efw6fdoW9" +# ### None +# 'nothing here' + +# %% id="fOZdR0YUcFRb" +None +none +"none" + +# %% [markdown] id="jockLUXXd2Ad" +# ## Difference between types +# +# Use `type(value)` to find the type of a value. + +# %% id="lHtfczHxd89N" +type(10) +type(3.14159) +type('hello') +type(True) +type('True') +type(None) + +# %% [markdown] id="kZffa20rXLOQ" +# Careful! A number in quotes is not actually a number, but a string. A string that looks like a number will **NOT** behave like a number. + +# %% id="-Zv10HJFXUW8" +type('10') +type('3.14159') + +# %% [markdown] id="rJ_mNXXAYIpy" +# Likewise, a string that looks like a Boolean will **NOT** behave like a boolean. + +# %% id="PN0FTHz2YRPM" +type('False') + +# %% [markdown] id="-0p3SmmgWdif" +# `int`, `str` etcetera can be used to convert values between different types. Careful though, not all conversions you can think of will do what you expect! + +# %% id="FPeklTLXWuXl" +int('10') +float('10') +str(10) +str(False) +bool('False') # !!! +None # no conversion exists + +# %% [markdown] id="YGU7CclEYz2y" +# It is useful to know that each character in a string is stored as an integer. `ord(character)` lets you retrieve that integer. + +# %% id="U_63E6jfab6N" +ord('2') +ord('a') +ord('A') +ord('\n') +ord('\t') + +# %% [markdown] id="0dh3t4uZa9AT" +# `chr(integer)` does the opposite: it lets you retrieve the character corresponding to a given integer. + +# %% id="ffIkwHahcGv9" +chr(49) +chr(98) +chr(946) +chr(22823) +chr(129327) + +# %% [markdown] id="InNZNIOpezlG" +# ## Variables +# +# - container holding a value +# - assigning +# - `variable_name = value` +# - reassigning +# - `name = 'sheean'` +# - `name = 'julian'` +# - in Python, no strict type +# - `a = 1` +# - `a = 'hello'` + +# %% id="wgaSYh4yfGCx" +a = 1 +b = "hello" +c = True +d = None + +print(a) + +# %% [markdown] id="BD2s2EqKfQtx" +# Tip: the notebook remembers variables from previous cells, but only if you executed them. + +# %% id="pJ-N_rc8fZia" +print(a) +print(b) +print(c) + +# %% [markdown] id="uADEckPZgGt_" +# Anything can go in a variable, not just single values + +# %% id="ehEig-sCgL48" +d = a +print(d) + +e = print +e(b) + +# %% [markdown] id="TaiYZ3R6ghq0" +# Beware! When we assign a value, the variable stores it at that exact moment. +# Changing one variable does not change another. +# + +# %% id="pIIq1la9g1Qr" +number = 0 + +container = number + +print(container) + +number = 1 + +print(container) +print(number) + + +# %% [markdown] id="nMn_xu6Ih3hJ" +# ### Variable names +# +# #### Rules +# +# - must start with a letter or underscore (`_`) +# - can only contain letters (`a-z`, `A-Z`), numbers (`0-9`), and underscore (`_`) +# - variable names are case sensitive. `name` is a different variable from `Name` +# +# #### Conventions +# +# Next to the rules, there are some conventions. +# Please stick to them, so others will have no trouble reading and understanding your code. +# - lowercase +# - DO: `name` +# - DON'T: `Name` +# - DON'T: `NAME`1 +# - readable +# - DO: `name` +# - DON'T: `n_a_m_e` +# - DON'T: `x098277` +# - multiple words2 +# - DO: `first_name` (*snake_case*) +# - DON'T: `FirstName` (*PascalCase*) +# - DON'T `firstName` (*camelCase*) +# - comprehensive (but short) +# - DO: `name` +# - DON'T: `n` +# - DON'T: `the_name_of_the_person_i_want_to_print` +# +# 1 *Fully uppercased variable names actually indicate constants, and adhere to Python conventions. We will explain this later.* +# 2 *This is purely a cultural thing. Other languages (and even other concepts within Python) use different casing* + +# %% id="m4pTI2BEn-Z-" + +# %% [markdown] id="pDU1uK2Igmki" +# ## Exercise 2.1: Variables and state +# +# In each of the code blocks below, try to predict what will be printed, then run the code. If your guess was incorrect, try to figure out why the result is different. If your guess was correct, celebrate! + +# %% id="FydkKnx_hUPq" +flavor = 'vanilla' +print(flavor) + +# %% id="LTWods50h-Ij" +temperature = 70 +print(flavor) + +# %% id="l2vT4L7piGRf" +print(temperature) +temperature = 35 +print(temperature) + +# %% id="k1Z_yWLXiWy7" +dog_name = 'Bobby' +cat_name = 'Garfield' +dog_name = cat_name +cat_name = dog_name +print(dog_name) +print(cat_name) + +# %% [markdown] id="BZ50KykuAYPs" +# Before running the following code, try to explain why it does *not* output `chocolate`. + +# %% id="X3xHg6K4Aicn" +sweet = 'chocolate' +savory = 'cheese' +dessert = 'sweet' +print(dessert) + +# %% [markdown] id="0az3tNK7WD3G" +# In each of the following lines of code, replace `None` by a value of your choice. Make it such that `my_int` is an integer, `my_float` is a float, `my_bool` is a boolean and `my_string` is a string. + +# %% id="lY-M8mfSXDfG" +my_int = None +my_float = None +my_bool = None +my_string = None + +# %% [markdown] id="dvNIQh7KYuJb" +# ## Exercise 2.2: Bonus + +# %% [markdown] id="GI9fyUO8XOcp" +# How could you verify in code whether the variables you wrote above have the correct type? Write this code below. +# +# Hint: scroll back to the section [Difference between types](#scrollTo=Difference_between_types). + +# %% id="Om6z53RXYBoS" + +# %% [markdown] id="h6UKIMXCj3w9" +# In the code block below, replace the question mark `?` by a variable name. Make it such that the final output is `'Hooray!'`. + +# %% id="3MI6_DY7ku30" +cheer = 'Hooray!' +alarm = 'Oh no!' +anthem = 'Wilhelmus van Nassauwe' +alarm = anthem +? = cheer +cheer = anthem +anthem = alarm +print(anthem) + +# %% [markdown] id="ZXd6jCn90CA_" +# ## Expressions +# +# - An expression is a combination between *operands* and *operators*. +# - Think of arithmetic: `1 + 2` +# +# + +# %% [markdown] id="s8uJy_kI9C8S" +# ### Arithmetic + +# %% id="f9ACtc0e13i-" +1 + 6 +5 - 2 +10 / 3 +5 * 5 + +a = 8 +b = 10 + +c = b - a +print(c) + +# %% id="w9cfOflP8DEJ" +# multiple operators +4 + (3 * 5) +(4 + 3) * 5 + +# %% [markdown] id="zkt9aNKm28Lc" +# ### String expressions + +# %% id="6os4-rt43ThF" +"hello" + " world!" +a = "my age is: " +b = 33 + +a + b + +# %% id="SyrelaMu42UZ" +a = 'hello' +a * 5 + +# %% [markdown] id="DPbfP65xXlfb" +# ## Reassigning variables +# +# - You can use a variable itself when reassigning. This is useful when trying to expand an existing variable. + +# %% id="ogL2Iw3-Xs15" +a = "hello" +a = a + ", world!" +print(a) + +b = 'bye' +b = b + b +print(b) + +b = b + b +print(b) + +# %% [markdown] id="z_bXvnya5J2_" +# ## Exercise 2.3: Expressions +# +# + +# %% [markdown] id="j9xhznQlbCBf" +# 1. Try to predict the value of each of the following code blocks. Can you explain any surprises? + +# %% id="0QcMB4xwbSfL" +1 + 1 + +# %% id="eHKN9kP9bWkm" +1 * 1 + +# %% id="uINoRNNbbXwJ" +a = 1 +b = 2 +a + b + +# %% id="4xyWkYlnbc_8" +c = b +a * b * c + +# %% id="7tW2T4mebljv" +'hakuna' + 'matata' + +# %% id="uEdPHIhBb2Mw" +liquid = 'water~' +liquid * 3 + +# %% id="-D7BB50Qceo2" +5 - 2 - 1 + +# %% id="OQUGr5rGck3m" +5 - (2 - 1) + +# %% id="Jc8fawaIdCD5" +income = 100 +tax = 20 +net_income = income - tax +tax = 15 +net_income + +# %% [markdown] id="TlLkcRv-droI" +# 2. In the code block below, change the values of `character` and `multiplier` so that the output becomes `'Goooood!'`. + +# %% id="pYZUkeDJenob" +character = 'o' +multiplier = 5 + +begin = 'G' +middle = character * multiplier +end = 'd!' + +begin + middle + end + +# %% [markdown] id="G8X4n_a8a5tl" +# 3. Rewrite your Hello world-program: +# - Build the "hello, world!" string using multiple variables. +# - Make the program say "hello, world!" 3 times, each time on a new line + +# %% id="nDUVvhDEfIVI" + +# %% [markdown] id="3HCqdTj2fVPK" +# ## Exercise 2.4: Bonus + +# %% [markdown] id="EKFdkLkWa8EY" +# 1. Find out and describe what the following operators do: +# - `%` (e.g. `10 % 2`) +# - `//` (e.g. `10 // 2`) +# - `**` (e.g. `10 ** 2`) +# - Tip: write the expressions using variables. Change the variables and see how it affects the outcome. + +# %% id="V59a7vpDfzO2" + +# %% [markdown] id="o0WYIAUla-AX" +# 2. Predict what will be printed on each line in the following code block, then run the code to check your answer. If you made any mistakes, try to figure out what is causing the difference. + +# %% id="Z9Vd9pdIUzT5" +word = 'stylometry' +repeats = 3 +extra = 2 + +print((word * repeats) + str(extra)) +print(word * (repeats + extra)) +print(word * repeats + str(extra)) +print((word + str(extra)) * repeats) +print(word + str(extra * repeats)) +print(word + str(extra) * repeats) + +# %% [markdown] id="DRObBQZHgsIG" +# 3. Using just the variables `word`, `repeats` and `extra` from the previous code block, write a single big expression that evaluates to `'stylometry555stylometry'`. + +# %% id="uLuiUk10gqwM" + +# %% [markdown] id="QaamMzJY6ISR" +# ## Boolean expressions +# Expressions that result in `True` or `False` +# + +# %% id="kLjya4Au6Ucu" +# equals +1 == 1 +"hello" == 'hello' +'2' == 2 + +# %% id="tYNp6nUp7ixW" +# does not equal +1 != 1 +"hello" != 'hello' +'2' != 2 + +# %% id="oixxKDHV7s5J" +# greater than +2 > 1 +2 > 5 +'b' > 'a' + +2 > 2 + +# greater than (or equal) +2 >= 2 + +# %% id="Z198kfCf75sU" +# less than +2 < 1 +2 < 5 +'b' < 'a' +2 < 2 + +# less than (or equal) +2 <= 2 + +# %% [markdown] id="pvrcgKU18OrJ" +# ## Exercise 2.5: boolean expressions + +# %% [markdown] id="sWdnjezoil9j" +# 1. Predict for each boolean expression below: `True` or `False`? If any of your predictions is incorrect, try to find the reason. + +# %% id="RpSvrom3Z8fQ" +name = 'Sheean' +height = 2 + +name * height == 'Sheean Sheean' +height < 2 +2 <= height +1 < height <= 2 +2 <= height < 1 +name <= 'Julian' +height * 3 + 1 >= height + +# %% [markdown] id="zJiaGIlZBN_P" +# 2. Run the code block below. Did you expect this result? Can you explain it? + +# %% id="_PrnFf2lioMB" +1 == True + +# %% [markdown] id="q8QviA70kdQE" +# 3. Replace one value in each of the following expressions so the expression becomes `True`. + +# %% id="qDQ9Ob5Zkrqm" +1 > height +height == 0 +'Julian' > name * 2 +name < 1 * name + +# %% [markdown] id="YbdV7SQVmDVV" +# 4. Replace one operator in each of the following expressions so the expression becomes `True`. + +# %% id="hzjXIwkAmChv" +5 < 4 +2 + 1 == 1 +3 + 3 == 3 + 2 + +# %% [markdown] id="SwyAMDzDn2_X" +# 5. The *Groot woordenboek van de Nederlandse taal* by Van Dale publishers, colloquially known as "De dikke Van Dale", is a three-tome Dutch dictionary. The first tome contains words starting a–i, the second j–q and the third r–z. In the code block below, write an expression that will tell you whether `word` should be in the second tome. Try different values of `word` to verify that your solution is correct. You do not need to check whether the word is actually Dutch! + +# %% id="lmCGfx_DqMWe" +word = 'archaïsch' + +# your expression here + +# %% [markdown] id="jXSxbjf4q6q5" +# ## Next module +# +# [3. Conditionals](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw) diff --git a/lessons/03 conditionals.ipynb b/lessons/03 conditionals.ipynb index 4ffcb7c..3676ceb 100644 --- a/lessons/03 conditionals.ipynb +++ b/lessons/03 conditionals.ipynb @@ -1 +1,470 @@ -{"cells":[{"cell_type":"markdown","metadata":{"id":"fqMJHzNk5yXQ"},"source":["# Module 3: Conditionals\n","\n","### CDH course \"Programming in Python\"\n","\n","[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n","\n","Previous module: [2. Values and expressions](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp)\n","\n","### This module\n","\n","- Execute code only under specific conditions."]},{"cell_type":"markdown","metadata":{"id":"SshSsbtF8ldm"},"source":["## `if`\n","\n","- Only evaluate part of the code if some condition is met\n","- Syntax: `if :`\n","- `` can be any expression that evaluates to `True` or `False`\n","- Introducing *indented blocks* "]},{"cell_type":"code","execution_count":null,"metadata":{"id":"Va6X8kIz9ey0"},"outputs":[],"source":["a = 12\n","\n","if a > 10:\n"," print('the condition is met!')"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"ADXv-2u090ql"},"outputs":[],"source":["a = 15\n","\n","if a < 10:\n"," print('everything on the same indentation level belongs to this if-condition')\n"," print('so this will only get printed if the condition is met')\n"," print('and so does this')\n","print('but not this!')"]},{"cell_type":"markdown","metadata":{"id":"5gqn2ac_sg7Y"},"source":["- Indented blocks are the reason we sometimes need `pass` as a placeholder (remember [1. introduction](https://colab.research.google.com/drive/1i4wJpUIr77Fh1renWNjt00Bd51NbC1nB)). It lets us create an indented block, without defining any behavior for it yet."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"ZdH7HnL6tDpS"},"outputs":[],"source":["earth = 'round'\n","\n","if earth == 'square':\n"," # TODO not sure yet how to handle this case\n"," pass"]},{"cell_type":"markdown","metadata":{"id":"sQxxBZwm-FYm"},"source":["## `else`\n","\n","- `if` is nice, but what if we are interested in the other case?"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"atRhlkGq-Mh1"},"outputs":[],"source":["letter = 'b'\n","\n","if letter == 'a':\n"," print('we found the letter a')\n","\n","if letter == 'b': \n"," print('this is not a')\n","\n","if letter == 'c':\n"," print('this is not a')"]},{"cell_type":"markdown","metadata":{"id":"O1repWSS-3Y0"},"source":["Instead of specifying *all other* cases, use `else:`"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"4n9ksUuH-8kE"},"outputs":[],"source":["letter = 'b'\n","\n","if letter == 'a':\n"," print('we found the letter a')\n","else:\n"," print('this is not a')"]},{"cell_type":"markdown","metadata":{"id":"Rg1ohowkAGFh"},"source":["## `elif`\n","\n","Specify extra cases with `elif :` (else if)\n","\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"JbiihBgdANIM"},"outputs":[],"source":["letter = 'a'\n","\n","if letter == 'a':\n"," print('we found the letter a')\n","elif letter == 'b':\n"," print('we found the letter b')\n","else:\n"," print('this is not a or b')"]},{"cell_type":"markdown","metadata":{"id":"lVOu8HvVIEj6"},"source":["## Multiple conditions\n","- We can *nest* conditions"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"MH_RrrBRIPh4"},"outputs":[],"source":["number = 11\n","\n","if number > 2:\n"," if number < 10:\n"," print('between 2 and 10')\n"," print('larger than 2, but not smaller than 10')\n","\n","# There is a mistake in the code above, can you find\n","# and fix it?"]},{"cell_type":"markdown","metadata":{"id":"8Lvam7rpIms6"},"source":["- Even better: we can *combine* conditions\n","- use ` and `, ` or ` and `not `\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"zJ8rBgcfIw4I"},"outputs":[],"source":["number = 11\n","if number > 2 and number < 10:\n"," print('between 2 and 10')\n","\n","letter = 'd'\n","if letter == 'a' or letter == 'b' or letter == 'c':\n"," print('a or b or c')\n","\n","if not (letter == 'a' or letter == 'b'):\n"," print('neither a nor b')"]},{"cell_type":"markdown","metadata":{"id":"SmsIZBLEtg9r"},"source":["- `and`, `or`, `not` are operators, just like `+` and `==`. You can use them outside conditionals."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"XMWJeaujt2lj"},"outputs":[],"source":["height = 185\n","weight = 52\n","\n","tall = height > 180\n","light = weight < 65\n","\n","skinny = tall and light\n","print(skinny)"]},{"cell_type":"markdown","metadata":{"id":"tvXa9KWXAwge"},"source":["## Exercise 3.1: if/elif/else"]},{"cell_type":"markdown","metadata":{"id":"pKBuViM9u7ZC"},"source":["1. Try to predict the output of the following code blocks. Can you explain any surprises?"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"sEQyw2xJvzQN"},"outputs":[],"source":["if 3 <= 2:\n"," print('What a strange day.')\n","print('What a strange day.')"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"FnCFfEb_v-0Y"},"outputs":[],"source":["if None == 0:\n"," pass\n"," print('I got nothing to do.')"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"arta4ZQ0vDSC"},"outputs":[],"source":["name = 'Julian'\n","\n","if name < 'Sheean':\n"," print('Alphabetically before Sheean')\n","elif name > 'Sheean':\n"," print('Alphabetically after Sheean')\n","else:\n"," print('Same name as Sheean')"]},{"cell_type":"markdown","metadata":{"id":"4SVSiqHSu4WX"},"source":["2. In the following code block, replace `None` with your own condition in the `if` statement. If `value` is greater than `5`, `size` must be `'large'`; otherwise, `size` must be `'small'`. Change `value` a few times and rerun the code to check that your condition is correct."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"czmdFacjeUaL"},"outputs":[],"source":["value = 4\n","\n","if None:\n"," size = 'large'\n","else:\n"," size = 'small'\n","\n","print(size)"]},{"cell_type":"markdown","metadata":{"id":"sEOwMi04e6WW"},"source":["3. Write an `if`/`elif`/`else` statement like the previous, but with different cutoff points: if `value` is less than `4`, `size` must be `'small'`; if `value` is between `4` and `6` (inclusive), `size` must be `'medium'`; if `value` is greater than `6`, `size` must be `'large'`."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"8HeaY6l9f9iA"},"outputs":[],"source":["value = 4\n","\n","# your if/elif/else here\n","\n","print(size)"]},{"cell_type":"markdown","metadata":{"id":"TWzbez_2RUh4"},"source":["4. Rewrite the conditional that checks if a letter is neither 'a' nor 'b', using a different notation."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"XsIg1MD4JrPX"},"outputs":[],"source":["letter = 'c'\n","\n","# original\n","if not (letter == 'a' or letter == 'b'):\n"," print('neither a nor b')"]},{"cell_type":"markdown","metadata":{"id":"-XEYQZJ1ya1j"},"source":["## Exercise 3.2: Bonus"]},{"cell_type":"markdown","metadata":{"id":"POVFwRu_f91I"},"source":["*FizzBuzz part 1* (advanced).\n","Write an `if`/`elif`/`else` statement that behaves as follows:\n","\n","- if `value` is divisible by 3 but not by 5, print `'Fizz'`;\n","- if `value` is divisible by 5 but not by 3, print `'Buzz'`;\n","- if `value` is divisible by 3 *and* by 5, print `'FizzBuzz'`;\n","- in all remaining cases, print `value`.\n","\n","Tip: use the result of [Exercise 2.4.1](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp#scrollTo=Exercise_2_4_Bonus)!"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"SZGeQtqEhiAK"},"outputs":[],"source":["value = 9\n","\n","# Your code here"]},{"cell_type":"markdown","metadata":{"id":"YBC4OfihzFho"},"source":["## Next module\n","\n","[4. Datastructures](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF)"]}],"metadata":{"colab":{"authorship_tag":"ABX9TyOwt+JcPyfyBfscI/sF+CKo","provenance":[],"toc_visible":true},"kernelspec":{"display_name":"Python 3","name":"python3"},"language_info":{"name":"python"}},"nbformat":4,"nbformat_minor":0} +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "fqMJHzNk5yXQ" + }, + "source": [ + "# Module 3: Conditionals\n", + "\n", + "### CDH course \"Programming in Python\"\n", + "\n", + "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "\n", + "Previous module: [2. Values and expressions](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp)\n", + "\n", + "### This module\n", + "\n", + "- Execute code only under specific conditions." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SshSsbtF8ldm" + }, + "source": [ + "## `if`\n", + "\n", + "- Only evaluate part of the code if some condition is met\n", + "- Syntax: `if :`\n", + "- `` can be any expression that evaluates to `True` or `False`\n", + "- Introducing *indented blocks* " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Va6X8kIz9ey0" + }, + "outputs": [], + "source": [ + "a = 12\n", + "\n", + "if a > 10:\n", + " print('the condition is met!')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ADXv-2u090ql" + }, + "outputs": [], + "source": [ + "a = 15\n", + "\n", + "if a < 10:\n", + " print('everything on the same indentation level belongs to this if-condition')\n", + " print('so this will only get printed if the condition is met')\n", + " print('and so does this')\n", + "print('but not this!')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5gqn2ac_sg7Y" + }, + "source": [ + "- Indented blocks are the reason we sometimes need `pass` as a placeholder (remember [1. introduction](https://colab.research.google.com/drive/1i4wJpUIr77Fh1renWNjt00Bd51NbC1nB)). It lets us create an indented block, without defining any behavior for it yet." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ZdH7HnL6tDpS" + }, + "outputs": [], + "source": [ + "earth = 'round'\n", + "\n", + "if earth == 'square':\n", + " # TODO not sure yet how to handle this case\n", + " pass" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "sQxxBZwm-FYm" + }, + "source": [ + "## `else`\n", + "\n", + "- `if` is nice, but what if we are interested in the other case?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "atRhlkGq-Mh1" + }, + "outputs": [], + "source": [ + "letter = 'b'\n", + "\n", + "if letter == 'a':\n", + " print('we found the letter a')\n", + "\n", + "if letter == 'b': \n", + " print('this is not a')\n", + "\n", + "if letter == 'c':\n", + " print('this is not a')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "O1repWSS-3Y0" + }, + "source": [ + "Instead of specifying *all other* cases, use `else:`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "4n9ksUuH-8kE" + }, + "outputs": [], + "source": [ + "letter = 'b'\n", + "\n", + "if letter == 'a':\n", + " print('we found the letter a')\n", + "else:\n", + " print('this is not a')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Rg1ohowkAGFh" + }, + "source": [ + "## `elif`\n", + "\n", + "Specify extra cases with `elif :` (else if)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "JbiihBgdANIM" + }, + "outputs": [], + "source": [ + "letter = 'a'\n", + "\n", + "if letter == 'a':\n", + " print('we found the letter a')\n", + "elif letter == 'b':\n", + " print('we found the letter b')\n", + "else:\n", + " print('this is not a or b')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "lVOu8HvVIEj6" + }, + "source": [ + "## Multiple conditions\n", + "- We can *nest* conditions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "MH_RrrBRIPh4" + }, + "outputs": [], + "source": [ + "number = 11\n", + "\n", + "if number > 2:\n", + " if number < 10:\n", + " print('between 2 and 10')\n", + " print('larger than 2, but not smaller than 10')\n", + "\n", + "# There is a mistake in the code above, can you find\n", + "# and fix it?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8Lvam7rpIms6" + }, + "source": [ + "- Even better: we can *combine* conditions\n", + "- use ` and `, ` or ` and `not `\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "zJ8rBgcfIw4I" + }, + "outputs": [], + "source": [ + "number = 11\n", + "if number > 2 and number < 10:\n", + " print('between 2 and 10')\n", + "\n", + "letter = 'd'\n", + "if letter == 'a' or letter == 'b' or letter == 'c':\n", + " print('a or b or c')\n", + "\n", + "if not (letter == 'a' or letter == 'b'):\n", + " print('neither a nor b')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SmsIZBLEtg9r" + }, + "source": [ + "- `and`, `or`, `not` are operators, just like `+` and `==`. You can use them outside conditionals." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "XMWJeaujt2lj" + }, + "outputs": [], + "source": [ + "height = 185\n", + "weight = 52\n", + "\n", + "tall = height > 180\n", + "light = weight < 65\n", + "\n", + "skinny = tall and light\n", + "print(skinny)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tvXa9KWXAwge" + }, + "source": [ + "## Exercise 3.1: if/elif/else" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pKBuViM9u7ZC" + }, + "source": [ + "1. Try to predict the output of the following code blocks. Can you explain any surprises?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "sEQyw2xJvzQN" + }, + "outputs": [], + "source": [ + "if 3 <= 2:\n", + " print('What a strange day.')\n", + "print('What a strange day.')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "FnCFfEb_v-0Y" + }, + "outputs": [], + "source": [ + "if None == 0:\n", + " pass\n", + " print('I got nothing to do.')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "arta4ZQ0vDSC" + }, + "outputs": [], + "source": [ + "name = 'Julian'\n", + "\n", + "if name < 'Sheean':\n", + " print('Alphabetically before Sheean')\n", + "elif name > 'Sheean':\n", + " print('Alphabetically after Sheean')\n", + "else:\n", + " print('Same name as Sheean')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "4SVSiqHSu4WX" + }, + "source": [ + "2. In the following code block, replace `None` with your own condition in the `if` statement. If `value` is greater than `5`, `size` must be `'large'`; otherwise, `size` must be `'small'`. Change `value` a few times and rerun the code to check that your condition is correct." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "czmdFacjeUaL" + }, + "outputs": [], + "source": [ + "value = 4\n", + "\n", + "if None:\n", + " size = 'large'\n", + "else:\n", + " size = 'small'\n", + "\n", + "print(size)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "sEOwMi04e6WW" + }, + "source": [ + "3. Write an `if`/`elif`/`else` statement like the previous, but with different cutoff points: if `value` is less than `4`, `size` must be `'small'`; if `value` is between `4` and `6` (inclusive), `size` must be `'medium'`; if `value` is greater than `6`, `size` must be `'large'`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "8HeaY6l9f9iA" + }, + "outputs": [], + "source": [ + "value = 4\n", + "\n", + "# your if/elif/else here\n", + "\n", + "print(size)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "TWzbez_2RUh4" + }, + "source": [ + "4. Rewrite the conditional that checks if a letter is neither 'a' nor 'b', using a different notation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "XsIg1MD4JrPX" + }, + "outputs": [], + "source": [ + "letter = 'c'\n", + "\n", + "# original\n", + "if not (letter == 'a' or letter == 'b'):\n", + " print('neither a nor b')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-XEYQZJ1ya1j" + }, + "source": [ + "## Exercise 3.2: Bonus" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "POVFwRu_f91I" + }, + "source": [ + "*FizzBuzz part 1* (advanced).\n", + "Write an `if`/`elif`/`else` statement that behaves as follows:\n", + "\n", + "- if `value` is divisible by 3 but not by 5, print `'Fizz'`;\n", + "- if `value` is divisible by 5 but not by 3, print `'Buzz'`;\n", + "- if `value` is divisible by 3 *and* by 5, print `'FizzBuzz'`;\n", + "- in all remaining cases, print `value`.\n", + "\n", + "Tip: use the result of [Exercise 2.4.1](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp#scrollTo=Exercise_2_4_Bonus)!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "SZGeQtqEhiAK" + }, + "outputs": [], + "source": [ + "value = 9\n", + "\n", + "# Your code here" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "YBC4OfihzFho" + }, + "source": [ + "## Next module\n", + "\n", + "[4. Datastructures](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF)" + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyOwt+JcPyfyBfscI/sF+CKo", + "provenance": [], + "toc_visible": true + }, + "jupytext": { + "main_language": "python" + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/lessons/03 conditionals.py b/lessons/03 conditionals.py new file mode 100644 index 0000000..68bac63 --- /dev/null +++ b/lessons/03 conditionals.py @@ -0,0 +1,231 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# name: python3 +# --- + +# %% [markdown] id="fqMJHzNk5yXQ" +# # Module 3: Conditionals +# +# ### CDH course "Programming in Python" +# +# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# +# Previous module: [2. Values and expressions](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp) +# +# ### This module +# +# - Execute code only under specific conditions. + +# %% [markdown] id="SshSsbtF8ldm" +# ## `if` +# +# - Only evaluate part of the code if some condition is met +# - Syntax: `if :` +# - `` can be any expression that evaluates to `True` or `False` +# - Introducing *indented blocks* + +# %% id="Va6X8kIz9ey0" +a = 12 + +if a > 10: + print('the condition is met!') + +# %% id="ADXv-2u090ql" +a = 15 + +if a < 10: + print('everything on the same indentation level belongs to this if-condition') + print('so this will only get printed if the condition is met') + print('and so does this') +print('but not this!') + +# %% [markdown] id="5gqn2ac_sg7Y" +# - Indented blocks are the reason we sometimes need `pass` as a placeholder (remember [1. introduction](https://colab.research.google.com/drive/1i4wJpUIr77Fh1renWNjt00Bd51NbC1nB)). It lets us create an indented block, without defining any behavior for it yet. + +# %% id="ZdH7HnL6tDpS" +earth = 'round' + +if earth == 'square': + # TODO not sure yet how to handle this case + pass + +# %% [markdown] id="sQxxBZwm-FYm" +# ## `else` +# +# - `if` is nice, but what if we are interested in the other case? + +# %% id="atRhlkGq-Mh1" +letter = 'b' + +if letter == 'a': + print('we found the letter a') + +if letter == 'b': + print('this is not a') + +if letter == 'c': + print('this is not a') + +# %% [markdown] id="O1repWSS-3Y0" +# Instead of specifying *all other* cases, use `else:` + +# %% id="4n9ksUuH-8kE" +letter = 'b' + +if letter == 'a': + print('we found the letter a') +else: + print('this is not a') + +# %% [markdown] id="Rg1ohowkAGFh" +# ## `elif` +# +# Specify extra cases with `elif :` (else if) +# +# + +# %% id="JbiihBgdANIM" +letter = 'a' + +if letter == 'a': + print('we found the letter a') +elif letter == 'b': + print('we found the letter b') +else: + print('this is not a or b') + +# %% [markdown] id="lVOu8HvVIEj6" +# ## Multiple conditions +# - We can *nest* conditions + +# %% id="MH_RrrBRIPh4" +number = 11 + +if number > 2: + if number < 10: + print('between 2 and 10') + print('larger than 2, but not smaller than 10') + +# There is a mistake in the code above, can you find +# and fix it? + +# %% [markdown] id="8Lvam7rpIms6" +# - Even better: we can *combine* conditions +# - use ` and `, ` or ` and `not ` +# + +# %% id="zJ8rBgcfIw4I" +number = 11 +if number > 2 and number < 10: + print('between 2 and 10') + +letter = 'd' +if letter == 'a' or letter == 'b' or letter == 'c': + print('a or b or c') + +if not (letter == 'a' or letter == 'b'): + print('neither a nor b') + +# %% [markdown] id="SmsIZBLEtg9r" +# - `and`, `or`, `not` are operators, just like `+` and `==`. You can use them outside conditionals. + +# %% id="XMWJeaujt2lj" +height = 185 +weight = 52 + +tall = height > 180 +light = weight < 65 + +skinny = tall and light +print(skinny) + +# %% [markdown] id="tvXa9KWXAwge" +# ## Exercise 3.1: if/elif/else + +# %% [markdown] id="pKBuViM9u7ZC" +# 1. Try to predict the output of the following code blocks. Can you explain any surprises? + +# %% id="sEQyw2xJvzQN" +if 3 <= 2: + print('What a strange day.') +print('What a strange day.') + +# %% id="FnCFfEb_v-0Y" +if None == 0: + pass + print('I got nothing to do.') + +# %% id="arta4ZQ0vDSC" +name = 'Julian' + +if name < 'Sheean': + print('Alphabetically before Sheean') +elif name > 'Sheean': + print('Alphabetically after Sheean') +else: + print('Same name as Sheean') + +# %% [markdown] id="4SVSiqHSu4WX" +# 2. In the following code block, replace `None` with your own condition in the `if` statement. If `value` is greater than `5`, `size` must be `'large'`; otherwise, `size` must be `'small'`. Change `value` a few times and rerun the code to check that your condition is correct. + +# %% id="czmdFacjeUaL" +value = 4 + +if None: + size = 'large' +else: + size = 'small' + +print(size) + +# %% [markdown] id="sEOwMi04e6WW" +# 3. Write an `if`/`elif`/`else` statement like the previous, but with different cutoff points: if `value` is less than `4`, `size` must be `'small'`; if `value` is between `4` and `6` (inclusive), `size` must be `'medium'`; if `value` is greater than `6`, `size` must be `'large'`. + +# %% id="8HeaY6l9f9iA" +value = 4 + +# your if/elif/else here + +print(size) + +# %% [markdown] id="TWzbez_2RUh4" +# 4. Rewrite the conditional that checks if a letter is neither 'a' nor 'b', using a different notation. + +# %% id="XsIg1MD4JrPX" +letter = 'c' + +# original +if not (letter == 'a' or letter == 'b'): + print('neither a nor b') + +# %% [markdown] id="-XEYQZJ1ya1j" +# ## Exercise 3.2: Bonus + +# %% [markdown] id="POVFwRu_f91I" +# *FizzBuzz part 1* (advanced). +# Write an `if`/`elif`/`else` statement that behaves as follows: +# +# - if `value` is divisible by 3 but not by 5, print `'Fizz'`; +# - if `value` is divisible by 5 but not by 3, print `'Buzz'`; +# - if `value` is divisible by 3 *and* by 5, print `'FizzBuzz'`; +# - in all remaining cases, print `value`. +# +# Tip: use the result of [Exercise 2.4.1](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp#scrollTo=Exercise_2_4_Bonus)! + +# %% id="SZGeQtqEhiAK" +value = 9 + +# Your code here + +# %% [markdown] id="YBC4OfihzFho" +# ## Next module +# +# [4. Datastructures](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF) diff --git a/lessons/04 datastructures.ipynb b/lessons/04 datastructures.ipynb index 2491668..493b559 100644 --- a/lessons/04 datastructures.ipynb +++ b/lessons/04 datastructures.ipynb @@ -1 +1,671 @@ -{"cells":[{"cell_type":"markdown","metadata":{"id":"fqMJHzNk5yXQ"},"source":["# Module 4: Data Structures\n","\n","### CDH course \"Programming in Python\"\n","\n","[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n","\n","Previous module: [3. Conditionals](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw)\n","\n","### This module\n","\n","- Working with collections of many values"]},{"cell_type":"markdown","metadata":{"id":"rDdBkbX5kmUD"},"source":["## Data structures\n","\n","- Way to organize data, to make accessing it efficient\n","- Different types of data structures available\n","- For now, we will work with `list` and `tuple`"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"bmAaWSOPm87H"},"outputs":[],"source":["student1 = 'jasmin'\n","student2 = 'ravi'\n","student3 = 'john'\n","# not very efficient, what if we want to add another student? Or take one out?"]},{"cell_type":"markdown","metadata":{"id":"8Vl5wbRunR82"},"source":["## Lists\n","\n","- `list`: an ordered collection of values\n","- One type of *iterable*, a collection you that allows iteration over its elements\n","- Syntax: `[element1, element2, ...]`\n","- Empty list also exists: `[]`"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"sewvhM8JnhJZ"},"outputs":[],"source":["students = ['jasmin', 'ravi', 'john']\n","\n","print(students)"]},{"cell_type":"markdown","metadata":{"id":"zmKyJPoQnwmK"},"source":["Lists can contain values of mixed types:"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"Bp6_Mev2nzZV"},"outputs":[],"source":["['hello', 1, False]"]},{"cell_type":"markdown","metadata":{"id":"dt_IOpu_rqbk"},"source":["Lists can also contain variables"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"TTCPoO7QrtVy"},"outputs":[],"source":["usa = 'United States of America'\n","nl = 'The Netherlands'\n","countries = [usa, nl]"]},{"cell_type":"markdown","metadata":{"id":"uYR5FBUEoR0P"},"source":["### Accessing elements\n","- Every element has an *index*\n","- Index goes from 0 to length of the list - 1\n","- Negative index counts backwards from the last element"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"2eL0BOUJodLK"},"outputs":[],"source":["students = ['jasmin', 'ravi', 'john']\n","students[0]\n","students[1]\n","students[2]\n","students[-1]"]},{"cell_type":"markdown","metadata":{"id":"cyX0YcO5uZRa"},"source":["- Lists can be *unpacked* into variables"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"m6ETvPPXuc4z"},"outputs":[],"source":["numbers = [1, 2, 3]\n","one, two, three = numbers"]},{"cell_type":"markdown","metadata":{"id":"KvOEQrqRrS0T"},"source":["### Changing elements\n","- Assign element at index just like you would a variable"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"wFyceuSArcEB"},"outputs":[],"source":["students = ['jasmin', 'ravi', 'john']\n","students[0] = 'johanna'\n","\n","new_student = 'mark'\n","students[1] = new_student\n","\n","students"]},{"cell_type":"markdown","metadata":{"id":"DixopwTyr6gN"},"source":["### Adding and removing elements\n","- The `+` operator works for two lists\n","- The `.append(value)` and `.remove(index)` functions works on a list"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"nPyn0UHcsDbG"},"outputs":[],"source":["hello_world = ['hello', ',', 'world']\n","exclamation = ['!']\n","\n","full_sentence = hello_world + exclamation\n","print(full_sentence)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"Y2B57KQRsO2a"},"outputs":[],"source":["# note: .append() works in-place, you don't need to reassign the variable\n","students = ['jasmin', 'ravi', 'john']\n","students.append('mark')\n","print(students)\n","\n","students.remove('john')\n","# or by index:\n","# del students[2]\n","print(students)"]},{"cell_type":"markdown","metadata":{"id":"kUUwwkDVtXOC"},"source":["### Nested lists\n","- Anything goes in a list, including *another* list"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"EPxrRcL0tbpN"},"outputs":[],"source":["small_list = [4, 5, 6]\n","big_list = [1, 2, 3, small_list]\n","\n","print(big_list)\n","print(big_list[-1])\n","print(type(big_list[-1]))\n","\n","# Access the last element of the small_list inside big_list:"]},{"cell_type":"markdown","metadata":{"id":"1HDqXMbWwmbk"},"source":["### Accessing multiple elements\n","- Select multiple values at once: *slicing*\n","- Syntax: `list[start_index:end_index]`\n","- end_index is *exclusive*, so 'up to' end"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"wIS3jCYlw2P6"},"outputs":[],"source":["students = ['jasmin', 'ravi', 'john']\n","# 0 1 2 3\n","students[0:1]\n","students[0:2]\n"]},{"cell_type":"markdown","metadata":{"id":"BblECQZfw7Uy"},"source":["`start_index` and `end_index` are optional"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"6fIsX2VvxEq9"},"outputs":[],"source":["students = ['jasmin', 'ravi', 'john']\n","students[1:]\n","students[:-1]\n","students[:]\n"]},{"cell_type":"markdown","metadata":{"id":"n9lZQ72ExR4Z"},"source":["- slices can be used to reassign list elements\n","\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"C5AIMJEHxWnX"},"outputs":[],"source":["students = ['jasmin', 'ravi', 'john']\n","\n","students[0:2] = ['johanna', 'mark']\n","\n","print(students)"]},{"cell_type":"markdown","metadata":{"id":"CPHEdywi-IuC"},"source":["- in this way, you can also add or remove elements in the middle"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"Tzhdcojp-TTn"},"outputs":[],"source":["students = ['jasmin', 'ravi', 'john']\n","\n","students[1:2] = []\n","print(students)\n","\n","students[1:1] = ['ravi']\n","print(students)"]},{"cell_type":"markdown","metadata":{"id":"nfpm1orRO34Q"},"source":["### Checking if an element is in a list\n","- Use the syntax ` in `"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"0A9JACKJPCYt"},"outputs":[],"source":["students = ['jasmin', 'ravi', 'john']\n","\n","'ravi' in students\n","'Ravi' in students"]},{"cell_type":"markdown","metadata":{"id":"2NX28b3sZscv"},"source":["### Useful tricks\n","- the `len` *function* (we will learn about functions later) gives us the length of a list"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"A7UHSeTtZ2nw"},"outputs":[],"source":["students = ['jasmin', 'ravi', 'john']\n","len(students)"]},{"cell_type":"markdown","metadata":{"id":"cO6hX3FBZ6cC"},"source":["- `list.index()` finds a value and gives us the index"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"VPmLssc7aByj"},"outputs":[],"source":["students = ['jasmin', 'ravi', 'john']\n","students.index('ravi')"]},{"cell_type":"markdown","metadata":{"id":"ZyOZeS2SuRJ6"},"source":["## Tuples\n","- Different type of *iterable*\n","- Syntax: `(element1, element2, ...)`\n","- Important difference: not *mutable* (cannot change elements)\n","- Often used to unpack, we will work with tuples in data analysis"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"etiZVM_puu4Z"},"outputs":[],"source":["students = ('jasmin', 'ravi', 'john')\n","students[0]"]},{"cell_type":"markdown","metadata":{"id":"70aMsClGPRy9"},"source":["## Exercise 4.1: Lists\n","\n","1. For each of the `print` statements below, what do you expect is printed? Run the lines to check predictions"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"KMUxwcSqPlU1"},"outputs":[],"source":["countries = ['japan', 'hungary', 'maldives', 'gabon', 'bhutan']\n","\n","print(countries[0])\n","print(countries[-3])\n","print(countries[0:1] + countries[2:4])\n","\n","more_countries = countries + ['mexico', 'haiti']\n","print(more_countries)\n","\n","countries.append(['mexico', 'haiti'])\n","print(countries)"]},{"cell_type":"markdown","metadata":{"id":"TyebsOIpU6hv"},"source":["2. Transform the list below into `['jasmin', 'john', 'ravi']` in one line of code. \n","\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"H8o6vsHKVKoq"},"outputs":[],"source":["students = ['jasmin', 'ravi', 'john']"]},{"cell_type":"markdown","metadata":{"id":"HMU5X7XFWbCw"},"source":["3. For each of the print statements below, what do you expect is printed? Run the lines to check predictions."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"u_RWc8wBWgMT"},"outputs":[],"source":["random_fruit = 'pineapple'\n","fruits = ['apple', 'pear', random_fruit]\n","print(fruits)\n","\n","random_fruit = 'blueberry'\n","print(fruits)\n","\n","random_veggie = ['brussel sprouts']\n","veggies = ['broccoli', 'green beans', random_veggie]\n","print(veggies)\n","\n","random_veggie.append('kale')\n","print(veggies)"]},{"cell_type":"markdown","metadata":{"id":"3BfUO-jKS_u1"},"source":["## Exercise 4.2: Bonus\n","\n","Below we introduce another parameter in the list slice. Try to explain what it does."]},{"cell_type":"code","execution_count":2,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":627,"status":"ok","timestamp":1681202305255,"user":{"displayName":"Mees van Stiphout","userId":"10520931415894572279"},"user_tz":-120},"id":"Y9oxyQb7TIPI","outputId":"158288d5-94e0-4068-d13e-af2a5c85177f"},"outputs":[{"name":"stdout","output_type":"stream","text":["['japan', 'hungary', 'maldives', 'gabon', 'bhutan']\n","['japan', 'maldives', 'bhutan']\n","['bhutan', 'gabon', 'maldives', 'hungary', 'japan']\n","['bhutan', 'maldives', 'japan']\n"]}],"source":["countries = ['japan', 'hungary', 'maldives', 'gabon', 'bhutan']\n","\n","print(countries[0:5:1])\n","print(countries[0:5:2])\n","print(countries[-1::-1])\n","print(countries[-1::-2])"]},{"cell_type":"markdown","metadata":{"id":"Mb6CvHt3CaA0"},"source":["The piece of code below is supposed to recognize \"fancy\" words: words that are longer than 5 characters, contain at least one copy of the letter 'a' and start with an uppercase. However, the code is broken. It does not recognize any of our fancy example words.\n","\n","1. Change the value of `word` into each of the examples in the comments on the first two lines and then run the code. See for yourself that the code considers none of the example words fancy. Try some other words as well.\n","3. Try to understand why the code is giving the wrong result. Can you come up with a word that the code does consider fancy?\n","4. Repair the code so that it gives the right result for all examples, and any other words that you come up with."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"QQyGzsqCCe3o"},"outputs":[],"source":["# fancy: Alhambra, Arthur, Jasmine, Turandot\n","# not so fancy: Jeep, paper, Python, Ada\n","word = 'Alhambra'\n","\n","lengthy = len(word) > 5\n","has_a = 'a' in word\n","first_uppercase = 'A' <= word[1] <= 'Z'\n","\n","if lengthy and has_a and first_uppercase:\n"," print('The word is fancy')\n","else:\n"," print('The word is not so fancy')"]},{"cell_type":"markdown","metadata":{"id":"HiEWGB1V1W4U"},"source":["## Next module\n","\n","[5. Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM)"]}],"metadata":{"colab":{"provenance":[],"toc_visible":true},"kernelspec":{"display_name":"Python 3","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.10.12"}},"nbformat":4,"nbformat_minor":0} +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "fqMJHzNk5yXQ" + }, + "source": [ + "# Module 4: Data Structures\n", + "\n", + "### CDH course \"Programming in Python\"\n", + "\n", + "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "\n", + "Previous module: [3. Conditionals](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw)\n", + "\n", + "### This module\n", + "\n", + "- Working with collections of many values" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rDdBkbX5kmUD" + }, + "source": [ + "## Data structures\n", + "\n", + "- Way to organize data, to make accessing it efficient\n", + "- Different types of data structures available\n", + "- For now, we will work with `list` and `tuple`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "bmAaWSOPm87H" + }, + "outputs": [], + "source": [ + "student1 = 'jasmin'\n", + "student2 = 'ravi'\n", + "student3 = 'john'\n", + "# not very efficient, what if we want to add another student? Or take one out?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8Vl5wbRunR82" + }, + "source": [ + "## Lists\n", + "\n", + "- `list`: an ordered collection of values\n", + "- One type of *iterable*, a collection you that allows iteration over its elements\n", + "- Syntax: `[element1, element2, ...]`\n", + "- Empty list also exists: `[]`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "sewvhM8JnhJZ" + }, + "outputs": [], + "source": [ + "students = ['jasmin', 'ravi', 'john']\n", + "\n", + "print(students)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zmKyJPoQnwmK" + }, + "source": [ + "Lists can contain values of mixed types:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Bp6_Mev2nzZV" + }, + "outputs": [], + "source": [ + "['hello', 1, False]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "dt_IOpu_rqbk" + }, + "source": [ + "Lists can also contain variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "TTCPoO7QrtVy" + }, + "outputs": [], + "source": [ + "usa = 'United States of America'\n", + "nl = 'The Netherlands'\n", + "countries = [usa, nl]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "uYR5FBUEoR0P" + }, + "source": [ + "### Accessing elements\n", + "- Every element has an *index*\n", + "- Index goes from 0 to length of the list - 1\n", + "- Negative index counts backwards from the last element" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "2eL0BOUJodLK" + }, + "outputs": [], + "source": [ + "students = ['jasmin', 'ravi', 'john']\n", + "students[0]\n", + "students[1]\n", + "students[2]\n", + "students[-1]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "cyX0YcO5uZRa" + }, + "source": [ + "- Lists can be *unpacked* into variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "m6ETvPPXuc4z" + }, + "outputs": [], + "source": [ + "numbers = [1, 2, 3]\n", + "one, two, three = numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "KvOEQrqRrS0T" + }, + "source": [ + "### Changing elements\n", + "- Assign element at index just like you would a variable" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "wFyceuSArcEB" + }, + "outputs": [], + "source": [ + "students = ['jasmin', 'ravi', 'john']\n", + "students[0] = 'johanna'\n", + "\n", + "new_student = 'mark'\n", + "students[1] = new_student\n", + "\n", + "students" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "DixopwTyr6gN" + }, + "source": [ + "### Adding and removing elements\n", + "- The `+` operator works for two lists\n", + "- The `.append(value)` and `.remove(index)` functions works on a list" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "nPyn0UHcsDbG" + }, + "outputs": [], + "source": [ + "hello_world = ['hello', ',', 'world']\n", + "exclamation = ['!']\n", + "\n", + "full_sentence = hello_world + exclamation\n", + "print(full_sentence)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Y2B57KQRsO2a" + }, + "outputs": [], + "source": [ + "# note: .append() works in-place, you don't need to reassign the variable\n", + "students = ['jasmin', 'ravi', 'john']\n", + "students.append('mark')\n", + "print(students)\n", + "\n", + "students.remove('john')\n", + "# or by index:\n", + "# del students[2]\n", + "print(students)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kUUwwkDVtXOC" + }, + "source": [ + "### Nested lists\n", + "- Anything goes in a list, including *another* list" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "EPxrRcL0tbpN" + }, + "outputs": [], + "source": [ + "small_list = [4, 5, 6]\n", + "big_list = [1, 2, 3, small_list]\n", + "\n", + "print(big_list)\n", + "print(big_list[-1])\n", + "print(type(big_list[-1]))\n", + "\n", + "# Access the last element of the small_list inside big_list:" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1HDqXMbWwmbk" + }, + "source": [ + "### Accessing multiple elements\n", + "- Select multiple values at once: *slicing*\n", + "- Syntax: `list[start_index:end_index]`\n", + "- end_index is *exclusive*, so 'up to' end" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "wIS3jCYlw2P6", + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "students = ['jasmin', 'ravi', 'john']\n", + "# 0 1 2 3\n", + "students[0:1]\n", + "students[0:2]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BblECQZfw7Uy" + }, + "source": [ + "`start_index` and `end_index` are optional" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "6fIsX2VvxEq9", + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "students = ['jasmin', 'ravi', 'john']\n", + "students[1:]\n", + "students[:-1]\n", + "students[:]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "n9lZQ72ExR4Z" + }, + "source": [ + "- slices can be used to reassign list elements\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "C5AIMJEHxWnX" + }, + "outputs": [], + "source": [ + "students = ['jasmin', 'ravi', 'john']\n", + "\n", + "students[0:2] = ['johanna', 'mark']\n", + "\n", + "print(students)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "CPHEdywi-IuC" + }, + "source": [ + "- in this way, you can also add or remove elements in the middle" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Tzhdcojp-TTn" + }, + "outputs": [], + "source": [ + "students = ['jasmin', 'ravi', 'john']\n", + "\n", + "students[1:2] = []\n", + "print(students)\n", + "\n", + "students[1:1] = ['ravi']\n", + "print(students)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "nfpm1orRO34Q" + }, + "source": [ + "### Checking if an element is in a list\n", + "- Use the syntax ` in `" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "0A9JACKJPCYt" + }, + "outputs": [], + "source": [ + "students = ['jasmin', 'ravi', 'john']\n", + "\n", + "'ravi' in students\n", + "'Ravi' in students" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2NX28b3sZscv" + }, + "source": [ + "### Useful tricks\n", + "- the `len` *function* (we will learn about functions later) gives us the length of a list" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "A7UHSeTtZ2nw" + }, + "outputs": [], + "source": [ + "students = ['jasmin', 'ravi', 'john']\n", + "len(students)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "cO6hX3FBZ6cC" + }, + "source": [ + "- `list.index()` finds a value and gives us the index" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "VPmLssc7aByj" + }, + "outputs": [], + "source": [ + "students = ['jasmin', 'ravi', 'john']\n", + "students.index('ravi')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ZyOZeS2SuRJ6" + }, + "source": [ + "## Tuples\n", + "- Different type of *iterable*\n", + "- Syntax: `(element1, element2, ...)`\n", + "- Important difference: not *mutable* (cannot change elements)\n", + "- Often used to unpack, we will work with tuples in data analysis" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "etiZVM_puu4Z" + }, + "outputs": [], + "source": [ + "students = ('jasmin', 'ravi', 'john')\n", + "students[0]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "70aMsClGPRy9" + }, + "source": [ + "## Exercise 4.1: Lists\n", + "\n", + "1. For each of the `print` statements below, what do you expect is printed? Run the lines to check predictions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "KMUxwcSqPlU1" + }, + "outputs": [], + "source": [ + "countries = ['japan', 'hungary', 'maldives', 'gabon', 'bhutan']\n", + "\n", + "print(countries[0])\n", + "print(countries[-3])\n", + "print(countries[0:1] + countries[2:4])\n", + "\n", + "more_countries = countries + ['mexico', 'haiti']\n", + "print(more_countries)\n", + "\n", + "countries.append(['mexico', 'haiti'])\n", + "print(countries)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "TyebsOIpU6hv" + }, + "source": [ + "2. Transform the list below into `['jasmin', 'john', 'ravi']` in one line of code. \n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "H8o6vsHKVKoq" + }, + "outputs": [], + "source": [ + "students = ['jasmin', 'ravi', 'john']" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HMU5X7XFWbCw" + }, + "source": [ + "3. For each of the print statements below, what do you expect is printed? Run the lines to check predictions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "u_RWc8wBWgMT" + }, + "outputs": [], + "source": [ + "random_fruit = 'pineapple'\n", + "fruits = ['apple', 'pear', random_fruit]\n", + "print(fruits)\n", + "\n", + "random_fruit = 'blueberry'\n", + "print(fruits)\n", + "\n", + "random_veggie = ['brussel sprouts']\n", + "veggies = ['broccoli', 'green beans', random_veggie]\n", + "print(veggies)\n", + "\n", + "random_veggie.append('kale')\n", + "print(veggies)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3BfUO-jKS_u1" + }, + "source": [ + "## Exercise 4.2: Bonus\n", + "\n", + "Below we introduce another parameter in the list slice. Try to explain what it does." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 627, + "status": "ok", + "timestamp": 1681202305255, + "user": { + "displayName": "Mees van Stiphout", + "userId": "10520931415894572279" + }, + "user_tz": -120 + }, + "id": "Y9oxyQb7TIPI", + "outputId": "158288d5-94e0-4068-d13e-af2a5c85177f" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['japan', 'hungary', 'maldives', 'gabon', 'bhutan']\n", + "['japan', 'maldives', 'bhutan']\n", + "['bhutan', 'gabon', 'maldives', 'hungary', 'japan']\n", + "['bhutan', 'maldives', 'japan']\n" + ] + } + ], + "source": [ + "countries = ['japan', 'hungary', 'maldives', 'gabon', 'bhutan']\n", + "\n", + "print(countries[0:5:1])\n", + "print(countries[0:5:2])\n", + "print(countries[-1::-1])\n", + "print(countries[-1::-2])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Mb6CvHt3CaA0" + }, + "source": [ + "The piece of code below is supposed to recognize \"fancy\" words: words that are longer than 5 characters, contain at least one copy of the letter 'a' and start with an uppercase. However, the code is broken. It does not recognize any of our fancy example words.\n", + "\n", + "1. Change the value of `word` into each of the examples in the comments on the first two lines and then run the code. See for yourself that the code considers none of the example words fancy. Try some other words as well.\n", + "3. Try to understand why the code is giving the wrong result. Can you come up with a word that the code does consider fancy?\n", + "4. Repair the code so that it gives the right result for all examples, and any other words that you come up with." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "QQyGzsqCCe3o" + }, + "outputs": [], + "source": [ + "# fancy: Alhambra, Arthur, Jasmine, Turandot\n", + "# not so fancy: Jeep, paper, Python, Ada\n", + "word = 'Alhambra'\n", + "\n", + "lengthy = len(word) > 5\n", + "has_a = 'a' in word\n", + "first_uppercase = 'A' <= word[1] <= 'Z'\n", + "\n", + "if lengthy and has_a and first_uppercase:\n", + " print('The word is fancy')\n", + "else:\n", + " print('The word is not so fancy')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HiEWGB1V1W4U" + }, + "source": [ + "## Next module\n", + "\n", + "[5. Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM)" + ] + } + ], + "metadata": { + "colab": { + "provenance": [], + "toc_visible": true + }, + "jupytext": { + "main_language": "python" + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/lessons/04 datastructures.py b/lessons/04 datastructures.py new file mode 100644 index 0000000..c78bf62 --- /dev/null +++ b/lessons/04 datastructures.py @@ -0,0 +1,301 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# name: python3 +# --- + +# %% [markdown] id="fqMJHzNk5yXQ" +# # Module 4: Data Structures +# +# ### CDH course "Programming in Python" +# +# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# +# Previous module: [3. Conditionals](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw) +# +# ### This module +# +# - Working with collections of many values + +# %% [markdown] id="rDdBkbX5kmUD" +# ## Data structures +# +# - Way to organize data, to make accessing it efficient +# - Different types of data structures available +# - For now, we will work with `list` and `tuple` + +# %% id="bmAaWSOPm87H" +student1 = 'jasmin' +student2 = 'ravi' +student3 = 'john' +# not very efficient, what if we want to add another student? Or take one out? + +# %% [markdown] id="8Vl5wbRunR82" +# ## Lists +# +# - `list`: an ordered collection of values +# - One type of *iterable*, a collection you that allows iteration over its elements +# - Syntax: `[element1, element2, ...]` +# - Empty list also exists: `[]` + +# %% id="sewvhM8JnhJZ" +students = ['jasmin', 'ravi', 'john'] + +print(students) + +# %% [markdown] id="zmKyJPoQnwmK" +# Lists can contain values of mixed types: + +# %% id="Bp6_Mev2nzZV" +['hello', 1, False] + +# %% [markdown] id="dt_IOpu_rqbk" +# Lists can also contain variables + +# %% id="TTCPoO7QrtVy" +usa = 'United States of America' +nl = 'The Netherlands' +countries = [usa, nl] + +# %% [markdown] id="uYR5FBUEoR0P" +# ### Accessing elements +# - Every element has an *index* +# - Index goes from 0 to length of the list - 1 +# - Negative index counts backwards from the last element + +# %% id="2eL0BOUJodLK" +students = ['jasmin', 'ravi', 'john'] +students[0] +students[1] +students[2] +students[-1] + +# %% [markdown] id="cyX0YcO5uZRa" +# - Lists can be *unpacked* into variables + +# %% id="m6ETvPPXuc4z" +numbers = [1, 2, 3] +one, two, three = numbers + +# %% [markdown] id="KvOEQrqRrS0T" +# ### Changing elements +# - Assign element at index just like you would a variable + +# %% id="wFyceuSArcEB" +students = ['jasmin', 'ravi', 'john'] +students[0] = 'johanna' + +new_student = 'mark' +students[1] = new_student + +students + +# %% [markdown] id="DixopwTyr6gN" +# ### Adding and removing elements +# - The `+` operator works for two lists +# - The `.append(value)` and `.remove(index)` functions works on a list + +# %% id="nPyn0UHcsDbG" +hello_world = ['hello', ',', 'world'] +exclamation = ['!'] + +full_sentence = hello_world + exclamation +print(full_sentence) + +# %% id="Y2B57KQRsO2a" +# note: .append() works in-place, you don't need to reassign the variable +students = ['jasmin', 'ravi', 'john'] +students.append('mark') +print(students) + +students.remove('john') +# or by index: +# del students[2] +print(students) + +# %% [markdown] id="kUUwwkDVtXOC" +# ### Nested lists +# - Anything goes in a list, including *another* list + +# %% id="EPxrRcL0tbpN" +small_list = [4, 5, 6] +big_list = [1, 2, 3, small_list] + +print(big_list) +print(big_list[-1]) +print(type(big_list[-1])) + +# Access the last element of the small_list inside big_list: + +# %% [markdown] id="1HDqXMbWwmbk" +# ### Accessing multiple elements +# - Select multiple values at once: *slicing* +# - Syntax: `list[start_index:end_index]` +# - end_index is *exclusive*, so 'up to' end + +# %% id="wIS3jCYlw2P6" +students = ['jasmin', 'ravi', 'john'] +# 0 1 2 3 +students[0:1] +students[0:2] + + +# %% [markdown] id="BblECQZfw7Uy" +# `start_index` and `end_index` are optional + +# %% id="6fIsX2VvxEq9" +students = ['jasmin', 'ravi', 'john'] +students[1:] +students[:-1] +students[:] + + +# %% [markdown] id="n9lZQ72ExR4Z" +# - slices can be used to reassign list elements +# +# + +# %% id="C5AIMJEHxWnX" +students = ['jasmin', 'ravi', 'john'] + +students[0:2] = ['johanna', 'mark'] + +print(students) + +# %% [markdown] id="CPHEdywi-IuC" +# - in this way, you can also add or remove elements in the middle + +# %% id="Tzhdcojp-TTn" +students = ['jasmin', 'ravi', 'john'] + +students[1:2] = [] +print(students) + +students[1:1] = ['ravi'] +print(students) + +# %% [markdown] id="nfpm1orRO34Q" +# ### Checking if an element is in a list +# - Use the syntax ` in ` + +# %% id="0A9JACKJPCYt" +students = ['jasmin', 'ravi', 'john'] + +'ravi' in students +'Ravi' in students + +# %% [markdown] id="2NX28b3sZscv" +# ### Useful tricks +# - the `len` *function* (we will learn about functions later) gives us the length of a list + +# %% id="A7UHSeTtZ2nw" +students = ['jasmin', 'ravi', 'john'] +len(students) + +# %% [markdown] id="cO6hX3FBZ6cC" +# - `list.index()` finds a value and gives us the index + +# %% id="VPmLssc7aByj" +students = ['jasmin', 'ravi', 'john'] +students.index('ravi') + +# %% [markdown] id="ZyOZeS2SuRJ6" +# ## Tuples +# - Different type of *iterable* +# - Syntax: `(element1, element2, ...)` +# - Important difference: not *mutable* (cannot change elements) +# - Often used to unpack, we will work with tuples in data analysis + +# %% id="etiZVM_puu4Z" +students = ('jasmin', 'ravi', 'john') +students[0] + +# %% [markdown] id="70aMsClGPRy9" +# ## Exercise 4.1: Lists +# +# 1. For each of the `print` statements below, what do you expect is printed? Run the lines to check predictions + +# %% id="KMUxwcSqPlU1" +countries = ['japan', 'hungary', 'maldives', 'gabon', 'bhutan'] + +print(countries[0]) +print(countries[-3]) +print(countries[0:1] + countries[2:4]) + +more_countries = countries + ['mexico', 'haiti'] +print(more_countries) + +countries.append(['mexico', 'haiti']) +print(countries) + +# %% [markdown] id="TyebsOIpU6hv" +# 2. Transform the list below into `['jasmin', 'john', 'ravi']` in one line of code. +# +# + +# %% id="H8o6vsHKVKoq" +students = ['jasmin', 'ravi', 'john'] + +# %% [markdown] id="HMU5X7XFWbCw" +# 3. For each of the print statements below, what do you expect is printed? Run the lines to check predictions. + +# %% id="u_RWc8wBWgMT" +random_fruit = 'pineapple' +fruits = ['apple', 'pear', random_fruit] +print(fruits) + +random_fruit = 'blueberry' +print(fruits) + +random_veggie = ['brussel sprouts'] +veggies = ['broccoli', 'green beans', random_veggie] +print(veggies) + +random_veggie.append('kale') +print(veggies) + +# %% [markdown] id="3BfUO-jKS_u1" +# ## Exercise 4.2: Bonus +# +# Below we introduce another parameter in the list slice. Try to explain what it does. + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 627, "status": "ok", "timestamp": 1681202305255, "user": {"displayName": "Mees van Stiphout", "userId": "10520931415894572279"}, "user_tz": -120} id="Y9oxyQb7TIPI" outputId="158288d5-94e0-4068-d13e-af2a5c85177f" +countries = ['japan', 'hungary', 'maldives', 'gabon', 'bhutan'] + +print(countries[0:5:1]) +print(countries[0:5:2]) +print(countries[-1::-1]) +print(countries[-1::-2]) + +# %% [markdown] id="Mb6CvHt3CaA0" +# The piece of code below is supposed to recognize "fancy" words: words that are longer than 5 characters, contain at least one copy of the letter 'a' and start with an uppercase. However, the code is broken. It does not recognize any of our fancy example words. +# +# 1. Change the value of `word` into each of the examples in the comments on the first two lines and then run the code. See for yourself that the code considers none of the example words fancy. Try some other words as well. +# 3. Try to understand why the code is giving the wrong result. Can you come up with a word that the code does consider fancy? +# 4. Repair the code so that it gives the right result for all examples, and any other words that you come up with. + +# %% id="QQyGzsqCCe3o" +# fancy: Alhambra, Arthur, Jasmine, Turandot +# not so fancy: Jeep, paper, Python, Ada +word = 'Alhambra' + +lengthy = len(word) > 5 +has_a = 'a' in word +first_uppercase = 'A' <= word[1] <= 'Z' + +if lengthy and has_a and first_uppercase: + print('The word is fancy') +else: + print('The word is not so fancy') + +# %% [markdown] id="HiEWGB1V1W4U" +# ## Next module +# +# [5. Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM) diff --git a/lessons/05 assertions.ipynb b/lessons/05 assertions.ipynb index ed94ba1..8d62cb8 100644 --- a/lessons/05 assertions.ipynb +++ b/lessons/05 assertions.ipynb @@ -1 +1,273 @@ -{"cells":[{"cell_type":"markdown","metadata":{"id":"fqMJHzNk5yXQ"},"source":["# Module 5: Assertions\n","\n","### Exercise solutions\n","\n","### CDH course \"Programming in Python\"\n","\n","[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n","\n","Previous module: [4. Datastructures](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF)\n","\n","### This module\n","\n","- Interrupting the program if something isn't right, with useful output."]},{"cell_type":"markdown","metadata":{"id":"bxJaE7DE4ig4"},"source":["## `assert`\n","\n","With an assertion, you tell Python that you want something to be `True`. This is an easy way to insert sanity checks in your code."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"PzRUSOI54_Zq"},"outputs":[],"source":["assert 2 < 3"]},{"cell_type":"markdown","metadata":{"id":"tPPNPy2q5hAU"},"source":["For clarity, you can describe the expectation."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"58Pa2nRq5x8R"},"outputs":[],"source":["sky = 'red'\n","\n","assert sky == 'blue', 'We expect to be on Earth'"]},{"cell_type":"markdown","metadata":{"id":"aLDwedXecSLp"},"source":["You can use `assert` statements to:\n","- provide a sanity check, make sure something is true at some point in the code\n","- provide test conditions for your code, make sure what you programmed actually behaves the way you wish"]},{"cell_type":"markdown","metadata":{"id":"m_FYfvXbbZXe"},"source":["## Exercise 5.1: Assertions\n","In each of the code blocks below, try to predict what will be the output, then run the code. If your guess was incorrect, try to figure out why the result is different. If your guess was correct, celebrate!"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"ztDylwg9biL5"},"outputs":[],"source":["assert True"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"0Uk4w2DBbxfD"},"outputs":[],"source":["assert False, \"The assertion fails because the value is False\""]},{"cell_type":"code","execution_count":null,"metadata":{"id":"orOWCpWVbzKf"},"outputs":[],"source":["assert \"True\""]},{"cell_type":"code","execution_count":null,"metadata":{"id":"F6NjZ7gOb05u"},"outputs":[],"source":["assert \"False\", \"The assertion fails because the value is False\""]},{"cell_type":"code","execution_count":null,"metadata":{"id":"KB_YkNSIb2KT"},"outputs":[],"source":["assert 1"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"1iUK81Nvb3Ri"},"outputs":[],"source":["assert 1 == True, \"The number 1 is not True\""]},{"cell_type":"code","execution_count":null,"metadata":{"id":"Tje6e-Jgb4rn"},"outputs":[],"source":["assert 0"]},{"cell_type":"markdown","metadata":{"id":"xgqh3r7Bcj_F"},"source":["## Exercise 5.2: Bonus - Test-driven development\n","[Test-driven development](https://en.wikipedia.org/wiki/Test-driven_development) is a proccess where you **first** write some test conditions, and then write the code that should satisfy these conditions. \n","Implement the following programs in these steps:\n","1. Create multiple test conditions using `assert`\n","2. Run the cell, the tests should fail (output `AssertionError`)\n","3. Write the code that satisfies the conditions\n","4. The cell should complete succesfully\n","\n","We have already implemented step 1 for these exercises."]},{"cell_type":"markdown","metadata":{"id":"TMWSMWg7dQqB"},"source":["In the code block below, change the value for `b`, and create an expression for `c` using `a` and `b` so that the assert statements succeed.\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"Q_yIUKSRdVjF"},"outputs":[],"source":["a = 12\n","b = 0\n","\n","# Write an expression for c using a and b\n","c = 0\n","\n","assert a < b, 'a should be smaller than b'\n","assert a != b, 'a and b should not be equal'\n","assert c == 18, 'c should be 18'"]},{"cell_type":"markdown","metadata":{"id":"1u_bBUpSfQr5"},"source":["In the code block below, change `students` so that it satisfies the `assert` statements. Also, add messages to the assert statements that explain what they test (in human language)."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"UOp8NFVOfR6Z"},"outputs":[],"source":["students = ['ernie', 'bert']\n","\n","assert len(students) == 3\n","assert students[0] < students[1] < students[2]"]},{"cell_type":"markdown","metadata":{"id":"JaaguG-D3k_i"},"source":["## Next module\n","\n","[6. Loops](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV)"]}],"metadata":{"colab":{"provenance":[],"toc_visible":true},"kernelspec":{"display_name":"Python 3","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.10.12"}},"nbformat":4,"nbformat_minor":0} +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "fqMJHzNk5yXQ" + }, + "source": [ + "# Module 5: Assertions\n", + "\n", + "### Exercise solutions\n", + "\n", + "### CDH course \"Programming in Python\"\n", + "\n", + "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "\n", + "Previous module: [4. Datastructures](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF)\n", + "\n", + "### This module\n", + "\n", + "- Interrupting the program if something isn't right, with useful output." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "bxJaE7DE4ig4" + }, + "source": [ + "## `assert`\n", + "\n", + "With an assertion, you tell Python that you want something to be `True`. This is an easy way to insert sanity checks in your code." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "PzRUSOI54_Zq" + }, + "outputs": [], + "source": [ + "assert 2 < 3" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tPPNPy2q5hAU" + }, + "source": [ + "For clarity, you can describe the expectation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "58Pa2nRq5x8R" + }, + "outputs": [], + "source": [ + "sky = 'red'\n", + "\n", + "assert sky == 'blue', 'We expect to be on Earth'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "aLDwedXecSLp" + }, + "source": [ + "You can use `assert` statements to:\n", + "- provide a sanity check, make sure something is true at some point in the code\n", + "- provide test conditions for your code, make sure what you programmed actually behaves the way you wish" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "m_FYfvXbbZXe" + }, + "source": [ + "## Exercise 5.1: Assertions\n", + "In each of the code blocks below, try to predict what will be the output, then run the code. If your guess was incorrect, try to figure out why the result is different. If your guess was correct, celebrate!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ztDylwg9biL5" + }, + "outputs": [], + "source": [ + "assert True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "0Uk4w2DBbxfD" + }, + "outputs": [], + "source": [ + "assert False, \"The assertion fails because the value is False\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "orOWCpWVbzKf" + }, + "outputs": [], + "source": [ + "assert \"True\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "F6NjZ7gOb05u" + }, + "outputs": [], + "source": [ + "assert \"False\", \"The assertion fails because the value is False\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "KB_YkNSIb2KT" + }, + "outputs": [], + "source": [ + "assert 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "1iUK81Nvb3Ri" + }, + "outputs": [], + "source": [ + "assert 1 == True, \"The number 1 is not True\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Tje6e-Jgb4rn" + }, + "outputs": [], + "source": [ + "assert 0" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xgqh3r7Bcj_F" + }, + "source": [ + "## Exercise 5.2: Bonus - Test-driven development\n", + "[Test-driven development](https://en.wikipedia.org/wiki/Test-driven_development) is a proccess where you **first** write some test conditions, and then write the code that should satisfy these conditions. \n", + "Implement the following programs in these steps:\n", + "1. Create multiple test conditions using `assert`\n", + "2. Run the cell, the tests should fail (output `AssertionError`)\n", + "3. Write the code that satisfies the conditions\n", + "4. The cell should complete succesfully\n", + "\n", + "We have already implemented step 1 for these exercises." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "TMWSMWg7dQqB" + }, + "source": [ + "In the code block below, change the value for `b`, and create an expression for `c` using `a` and `b` so that the assert statements succeed.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Q_yIUKSRdVjF" + }, + "outputs": [], + "source": [ + "a = 12\n", + "b = 0\n", + "\n", + "# Write an expression for c using a and b\n", + "c = 0\n", + "\n", + "assert a < b, 'a should be smaller than b'\n", + "assert a != b, 'a and b should not be equal'\n", + "assert c == 18, 'c should be 18'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1u_bBUpSfQr5" + }, + "source": [ + "In the code block below, change `students` so that it satisfies the `assert` statements. Also, add messages to the assert statements that explain what they test (in human language)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "UOp8NFVOfR6Z" + }, + "outputs": [], + "source": [ + "students = ['ernie', 'bert']\n", + "\n", + "assert len(students) == 3\n", + "assert students[0] < students[1] < students[2]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "JaaguG-D3k_i" + }, + "source": [ + "## Next module\n", + "\n", + "[6. Loops](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV)" + ] + } + ], + "metadata": { + "colab": { + "provenance": [], + "toc_visible": true + }, + "jupytext": { + "main_language": "python" + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/lessons/05 assertions.py b/lessons/05 assertions.py new file mode 100644 index 0000000..abfaf3a --- /dev/null +++ b/lessons/05 assertions.py @@ -0,0 +1,113 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# name: python3 +# --- + +# %% [markdown] id="fqMJHzNk5yXQ" +# # Module 5: Assertions +# +# ### Exercise solutions +# +# ### CDH course "Programming in Python" +# +# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# +# Previous module: [4. Datastructures](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF) +# +# ### This module +# +# - Interrupting the program if something isn't right, with useful output. + +# %% [markdown] id="bxJaE7DE4ig4" +# ## `assert` +# +# With an assertion, you tell Python that you want something to be `True`. This is an easy way to insert sanity checks in your code. + +# %% id="PzRUSOI54_Zq" +assert 2 < 3 + +# %% [markdown] id="tPPNPy2q5hAU" +# For clarity, you can describe the expectation. + +# %% id="58Pa2nRq5x8R" +sky = 'red' + +assert sky == 'blue', 'We expect to be on Earth' + +# %% [markdown] id="aLDwedXecSLp" +# You can use `assert` statements to: +# - provide a sanity check, make sure something is true at some point in the code +# - provide test conditions for your code, make sure what you programmed actually behaves the way you wish + +# %% [markdown] id="m_FYfvXbbZXe" +# ## Exercise 5.1: Assertions +# In each of the code blocks below, try to predict what will be the output, then run the code. If your guess was incorrect, try to figure out why the result is different. If your guess was correct, celebrate! + +# %% id="ztDylwg9biL5" +assert True + +# %% id="0Uk4w2DBbxfD" +assert False, "The assertion fails because the value is False" + +# %% id="orOWCpWVbzKf" +assert "True" + +# %% id="F6NjZ7gOb05u" +assert "False", "The assertion fails because the value is False" + +# %% id="KB_YkNSIb2KT" +assert 1 + +# %% id="1iUK81Nvb3Ri" +assert 1 == True, "The number 1 is not True" + +# %% id="Tje6e-Jgb4rn" +assert 0 + +# %% [markdown] id="xgqh3r7Bcj_F" +# ## Exercise 5.2: Bonus - Test-driven development +# [Test-driven development](https://en.wikipedia.org/wiki/Test-driven_development) is a proccess where you **first** write some test conditions, and then write the code that should satisfy these conditions. +# Implement the following programs in these steps: +# 1. Create multiple test conditions using `assert` +# 2. Run the cell, the tests should fail (output `AssertionError`) +# 3. Write the code that satisfies the conditions +# 4. The cell should complete succesfully +# +# We have already implemented step 1 for these exercises. + +# %% [markdown] id="TMWSMWg7dQqB" +# In the code block below, change the value for `b`, and create an expression for `c` using `a` and `b` so that the assert statements succeed. +# + +# %% id="Q_yIUKSRdVjF" +a = 12 +b = 0 + +# Write an expression for c using a and b +c = 0 + +assert a < b, 'a should be smaller than b' +assert a != b, 'a and b should not be equal' +assert c == 18, 'c should be 18' + +# %% [markdown] id="1u_bBUpSfQr5" +# In the code block below, change `students` so that it satisfies the `assert` statements. Also, add messages to the assert statements that explain what they test (in human language). + +# %% id="UOp8NFVOfR6Z" +students = ['ernie', 'bert'] + +assert len(students) == 3 +assert students[0] < students[1] < students[2] + +# %% [markdown] id="JaaguG-D3k_i" +# ## Next module +# +# [6. Loops](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV) diff --git a/lessons/06 Loops - Legacy.ipynb b/lessons/06 Loops - Legacy.ipynb index 00de5e8..a251347 100644 --- a/lessons/06 Loops - Legacy.ipynb +++ b/lessons/06 Loops - Legacy.ipynb @@ -1 +1,1004 @@ -{"cells":[{"cell_type":"code","execution_count":null,"metadata":{"id":"ZGnlpVha4WwJ"},"outputs":[],"source":[]},{"cell_type":"markdown","metadata":{"id":"fqMJHzNk5yXQ"},"source":["# CDH course \"Programming in Python\"\n","\n","[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)"]},{"cell_type":"markdown","metadata":{"id":"mzWET4w4lAr4"},"source":["# 1. Loops\n","\n","Loops are the most primitive way to run code *repeatedly*. For example:\n","\n","- Run some lines of code for each word in a text.\n","- Run some lines of code for each number in a list.\n","- Run some lines of code until you have found the answer to a question.\n","\n","There are \"smarter\" ways to repeat code that we will cover later in the course."]},{"cell_type":"markdown","metadata":{"id":"d7GJLYfEXwfs"},"source":["## `while` loops\n","\n","A `while` loop looks like an `if` statement, but it repeats as long as the condition is `True`:"]},{"cell_type":"code","execution_count":1,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":517,"status":"ok","timestamp":1667900027935,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"},"user_tz":-60},"id":"Fnut4-DIYmb8","outputId":"91a840be-aa92-47c9-c07f-bb52cb0447bc"},"outputs":[{"name":"stdout","output_type":"stream","text":["Sheean!!!!\n"]}],"source":["name = 'Sheean'\n","while len(name) < 10:\n"," name = name + '!'\n","print(name)"]},{"cell_type":"markdown","metadata":{"id":"p9-je4X0zkhq"},"source":["`while` loops are impractical if you want to repeat something for every element of an iterable, such as a list."]},{"cell_type":"markdown","metadata":{"id":"BDCe1ux90B7r"},"source":["## `for` loops over lists\n","\n","Do something with every element in a list:"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":395,"status":"ok","timestamp":1667900112073,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"},"user_tz":-60},"id":"O0XEpRMI0WG2","outputId":"41b9437b-26e9-45c4-ffb6-48e2d0510e0b"},"outputs":[{"name":"stdout","output_type":"stream","text":["1\n","hello\n","True\n"]}],"source":["the_list = [1, 'hello', True]\n","\n","for element in the_list:\n"," print(element)"]},{"cell_type":"markdown","metadata":{"id":"gdd6v6LU1DlK"},"source":["The body of the loop is an *indented block*, just like in an `if` or `while` statement. You can have multiple lines that repeat together:"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":384,"status":"ok","timestamp":1667900436458,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"},"user_tz":-60},"id":"dpqb0qFc1u0s","outputId":"9311ae9c-066d-4dbf-da9a-7c440473dac3"},"outputs":[{"name":"stdout","output_type":"stream","text":["A\n","A \n","very\n","A very \n","short\n","A very short \n","sentence\n","A very short sentence \n"]}],"source":["words = ['A', 'very', 'short', 'sentence']\n","full_text = ''\n","\n","for word in words:\n"," full_text = full_text + word + ' '\n"," print(word)\n"," print(full_text)"]},{"cell_type":"markdown","metadata":{"id":"vDU53qkB2zo4"},"source":["The loop ends when the indentation ends. Here, we calculate the sum of a list of numbers, and print the sum only at the end:"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":550,"status":"ok","timestamp":1667900533282,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"},"user_tz":-60},"id":"q6ucnoqeUnlI","outputId":"461dfdab-3387-4d9e-d59e-ad73fb8993cd"},"outputs":[{"name":"stdout","output_type":"stream","text":["35\n"]}],"source":["numbers = [9, 9, 4, 7, 6]\n","sum = 0\n","\n","for number in numbers:\n"," sum = sum + number\n","print(sum)"]},{"cell_type":"markdown","metadata":{"id":"0Gun_3cX1ey8"},"source":["## Exercise 1: basic `for` loops\n","\n","1. Make a small change to the following code block (copied from above), so that it prints the `full_text` only at the end, when it is complete."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"nMF8WE3F19HC"},"outputs":[],"source":["words = ['A', 'very', 'short', 'sentence']\n","full_text = ''\n","\n","for word in words:\n"," full_text = full_text + word + ' '\n"," print(word)\n"," print(full_text)"]},{"cell_type":"markdown","metadata":{"id":"8zB10pLC2ZaT"},"source":["2. Using a `for` loop, create a copy of the list of fruits below that has the fruits in the reverse order. Change the contents of the list on the first line to check that your solution is general."]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":363,"status":"ok","timestamp":1667903740371,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"},"user_tz":-60},"id":"bAwZ_ipU28AY","outputId":"6d1d0526-c780-4eaa-d9e4-2262de2691c2"},"outputs":[{"name":"stdout","output_type":"stream","text":["apricot ['apricot']\n","banana ['banana', 'apricot']\n","cherry ['cherry', 'banana', 'apricot']\n","date ['date', 'cherry', 'banana', 'apricot']\n","['date', 'cherry', 'banana', 'apricot']\n"]}],"source":["fruits = ['apricot', 'banana', 'cherry', 'date']\n","\n","# insert your code here\n","# SHOULD output ['date', 'cherry', 'banana', 'apricot']\n","\n","reverse_fruits = fruits[-1::-1]\n","print(reverse_fruits)\n","\n","reverse_fruits = []\n","for fruit in fruits[-1::-1]:\n"," reverse_fruits.append(fruit)\n"," reverse_fruits = reverse_fruits + [fruit]\n","print(reverse_fruits)\n","\n","reverse_fruits = []\n","for fruit in fruits:\n"," reverse_fruits = [fruit] + reverse_fruits\n"," print(fruit, reverse_fruits)\n","print(reverse_fruits)\n","\n","reverse_fruits = []\n","reverse_fruits = [fruits[0]] + reverse_fruits\n","reverse_fruits = [fruits[1]] + reverse_fruits\n","reverse_fruits = [fruits[2]] + reverse_fruits\n","reverse_fruits = [fruits[3]] + reverse_fruits\n","print(reverse_fruits)\n","\n","reverse_fruits = []\n","for fruit in fruits:\n"," reverse_fruits[0:0] = [fruit]\n","print(reverse_fruits)\n","\n","reverse_fruits = []\n","reverse_fruits[0:0] = [fruits[0]]\n","reverse_fruits[0:0] = [fruits[1]]\n","reverse_fruits[0:0] = [fruits[2]]\n","reverse_fruits[0:0] = [fruits[3]]\n","\n","reverse_fruits = []\n","for fruit in fruits:\n"," reverse_fruits[-1:0] = [fruit]\n"]},{"cell_type":"markdown","metadata":{"id":"DATxv0pM2gQc"},"source":["## Variations on loops\n","\n","All of the following variations can also be combined."]},{"cell_type":"markdown","metadata":{"id":"MVcUZD4T7j4h"},"source":["### Infinite loop"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"l40DJzDx2nCz"},"outputs":[],"source":["while True:\n"," print('Hello, living lab!')"]},{"cell_type":"markdown","metadata":{"id":"0axR682t-ub4"},"source":["Generally something to avoid, but sometimes useful with `break`."]},{"cell_type":"markdown","metadata":{"id":"2xzzyBX43Rbq"},"source":["### Breaking out of a loop"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":439,"status":"ok","timestamp":1667904280773,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"},"user_tz":-60},"id":"kbbKDHwd4Eiq","outputId":"6ff54d8a-dfa6-4995-c469-1b3177bce245"},"outputs":[{"name":"stdout","output_type":"stream","text":["apricot\n","banana\n","cherry\n","date\n","Aww no elderberry. 😞\n"]}],"source":["basket = ['apricot', 'banana', 'cherry', 'date']\n","\n","for fruit in basket:\n"," print(fruit)\n"," if fruit == 'elderberry':\n"," print('Yay, this basket has elderberry! 🤤')\n"," break\n","else:\n"," print('Aww no elderberry. 😞')"]},{"cell_type":"markdown","metadata":{"id":"3C9WlDSd-8zw"},"source":["`break`/`else` can be used both with `for` and `while` loops. The `else` is not required."]},{"cell_type":"markdown","metadata":{"id":"ZZGIvGNg673Z"},"source":["### Skipping to the next iteration"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":525,"status":"ok","timestamp":1667904433491,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"},"user_tz":-60},"id":"AyVZVJtL7nMJ","outputId":"2df5b782-b01c-4bea-812f-4f17ab255e5e"},"outputs":[{"name":"stdout","output_type":"stream","text":["dishwashing\n","laundry\n"]}],"source":["chores = ['dishwashing', 'vacuum cleaning', 'laundry']\n","\n","for task in chores:\n"," # I really hate vacuum cleaning\n"," if task == 'vacuum cleaning':\n"," continue\n"," print(task)"]},{"cell_type":"markdown","metadata":{"id":"3fs83n3H_J_j"},"source":["Works both in `for` and `while`."]},{"cell_type":"markdown","metadata":{"id":"FHf46PtqBvVb"},"source":["### Nested loops\n","\n","Nesting loops is **allowed**, to arbitrary depth. `for` and `while` can be mixed. However, nesting loops has a big **disadvantage**: your code quickly becomes less readable.\n","\n","Below, we use nested loops to iterate over nested lists."]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":527,"status":"ok","timestamp":1667904767636,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"},"user_tz":-60},"id":"WffnwizrCvPw","outputId":"64cc2f10-38da-4276-a401-26eeffd95890"},"outputs":[{"name":"stdout","output_type":"stream","text":["new row:\n","bread; fluffy; light brown; \n","new row:\n","olives; savory; dark green; \n","new row:\n","grapes; sweet; shiny red; \n"]}],"source":["table = [\n"," ['bread', 'fluffy', 'light brown'],\n"," ['olives', 'savory', 'dark green'],\n"," ['grapes', 'sweet', 'shiny red'],\n","]\n","\n","for row in table:\n"," print('new row:')\n"," for cell in row:\n"," print(cell, end='; ') # does not finish with a linebreak\n"," print() # puts a linebreak"]},{"cell_type":"markdown","metadata":{"id":"DZVVVSYy8HGd"},"source":["### Iterating over a string"]},{"cell_type":"code","execution_count":2,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":459,"status":"ok","timestamp":1667905025657,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"},"user_tz":-60},"id":"14DRs2jK8Po7","outputId":"bd49b199-0647-4e5b-86fd-19c7d09497ee"},"outputs":[{"name":"stdout","output_type":"stream","text":["107 lowercase, 11 uppercase and 60 other.\n"]}],"source":["invitation = '''\n"," Dear Sheean,\n","\n"," I hereby invite you for my Python party on the 9th of November.\n"," The bar will open at 2 PM. 🍸 Please bring pseudocode.\n","\n"," Yours sincerely,\n"," Julian\n","'''\n","\n","lowercase = 0\n","uppercase = 0\n","other = 0\n","for character in invitation:\n"," if 'a' <= character <= 'z':\n"," lowercase = lowercase + 1\n"," elif 'A' <= character <= 'Z':\n"," uppercase = uppercase + 1\n"," else:\n"," other = other + 1\n","\n","print(lowercase, 'lowercase,', uppercase, 'uppercase and', other, 'other.')"]},{"cell_type":"markdown","metadata":{"id":"HrfN3OhbELuM"},"source":["### Iterating over generated sequences\n","\n","*Generators* are iterables that produce one value at a time, so they do not need to store all their values at the same time.\n","\n","`range` creates a generator that produces consecutive numbers."]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":11,"status":"ok","timestamp":1667905473540,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"},"user_tz":-60},"id":"iqNQLZVcHYRX","outputId":"40591d82-d9d1-4c9c-c887-042daa2f9a81"},"outputs":[{"name":"stdout","output_type":"stream","text":["0\n","1\n","2\n","3\n","4\n"]}],"source":["for number in range(5):\n"," print(number)"]},{"cell_type":"markdown","metadata":{"id":"9GySTLd_Hq9l"},"source":["`enumerate` creates a new iterable based on another iterable, so you get not only the values but also their indices."]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":566,"status":"ok","timestamp":1667905472137,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"},"user_tz":-60},"id":"MIyn99IxH74D","outputId":"0c2f832c-c245-49ef-c51c-da9903e163e5"},"outputs":[{"name":"stdout","output_type":"stream","text":["Fruit number 0 is apricot\n","Fruit number 1 is banana\n","Fruit number 2 is cherry\n","Fruit number 3 is date\n"]}],"source":["basket = ['apricot', 'banana', 'cherry', 'date']\n","\n","for index, fruit in enumerate(basket):\n"," print('Fruit number', index, 'is', fruit)"]},{"cell_type":"markdown","metadata":{"id":"WGlaXiuxISRX"},"source":["There are many more ways to create generators, and many more ways to use them as well. We will see more of them later on in the course."]},{"cell_type":"markdown","metadata":{"id":"-K0CvNkOLEYE"},"source":["## Exercise 2: more loops\n","\n","1. The code block below is written to help you explore what kind of \"stuff\" comes out of `range` and `enumerate`. Read the code from top to bottom. What stands out to you? Why do you think it was written this way?\n","\n","2. Run the code block for each example value of `miracle`. Based on the results, describe in your own words what `range` and `enumerate` do."]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":465,"status":"ok","timestamp":1667910549466,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"},"user_tz":-60},"id":"7gappYcLOrsu","outputId":"6fff15b1-41c9-4798-bbb6-43c9ef12c85d"},"outputs":[{"name":"stdout","output_type":"stream","text":["\n","\n","0\n","1\n","9\n","length: 10\n"]}],"source":["basket = ['apricot', 'banana', 'cherry', 'date']\n","\n","miracle = range(10)\n","# miracle = range(2, 11)\n","# miracle = range(2, 11, 3)\n","# miracle = range(11, 2, -2)\n","# miracle = range(0.1, 1.0, 0.1)\n","# miracle = enumerate(basket)\n","# miracle = enumerate('pirate')\n","# miracle = enumerate(range(10))\n","\n","print(type(miracle))\n","\n","iteration_count = 0\n","for value in miracle:\n"," iteration_count = iteration_count + 1\n"," if iteration_count > 2:\n"," continue\n"," elif iteration_count == 1:\n"," print(type(value))\n"," print(value)\n","print(value)\n","print('length:', iteration_count)"]},{"cell_type":"markdown","metadata":{"id":"2Haq4E95bN6T"},"source":["3. The `input` function, demonstrated below, lets you ask the human user for a string and then store it in a variable. Write a program that keeps asking the user for words until the word is `'stop'`. For each word, report to the user how many characters it contains."]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":6733,"status":"ok","timestamp":1667914234752,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"},"user_tz":-60},"id":"0k_YQbBccyC_","outputId":"15b0c654-f047-4036-b95c-36a42d112d6b"},"outputs":[{"name":"stdout","output_type":"stream","text":["Please give me a word: charming\n","you wrote charming\n","the length is 8\n","Please give me a word: stop\n"]}],"source":["# word = input('Please give me a word: ')\n","# print('you wrote', word)\n","word = ''\n","while word != 'stop':\n"," word = input('Please give me a word: ')\n"," print('you wrote', word)\n"," print('the length is', len(word))\n","\n","word = ''\n","while True:\n"," word = input('Please give me a word: ')\n"," if word == 'stop':\n"," break\n"," # else:\n"," print('you wrote', word)\n"," length = 0\n"," for character in word:\n"," length = length + 1\n"," print('the length is', length)"]},{"cell_type":"markdown","metadata":{"id":"uyqbuhKsUlhG"},"source":["4. *FizzBuzz part 2* (advanced). Look back at your solution to exercise 5.3 from day 1 (*FizzBuzz part 1*). Now write a program that does the following: for each integer from `1` up to and including `100`, print `Fizz`, `Buzz`, `FizzBuzz` or the number itself, following the same rules as before. Separate the words and numbers by commas. Add a newline after every tenth number, so you print ten lines in total. The first line should start like `1, 2, Fizz, 4, Buzz, Fizz, ...` and the last line should be `91, 92, Fizz, 94, Buzz, Fizz, 97, 98, Fizz, Buzz, `."]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":674,"status":"ok","timestamp":1667915778770,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"},"user_tz":-60},"id":"BUeMXIQXaKna","outputId":"2852de82-cad6-4441-8f47-5555c83f08a4"},"outputs":[{"data":{"text/plain":["10"]},"execution_count":74,"metadata":{},"output_type":"execute_result"}],"source":["# the end=', ' argument will help you:\n","print('this prints with a comma instead of a newline', end=', ')\n","print('this as well', end=', ')\n","# empty print() adds a newline:\n","print()\n","print('next line for illustration')\n","\n","# iteration = 0\n","for n in range(1, 101):\n"," # iteration = iteration + 1\n"," if n % 15 == 0:\n"," print('FizzBuzz', end=', ')\n"," elif n % 3 == 0:\n"," print('Fizz', end=', ')\n"," elif n % 5 == 0:\n"," print('Buzz', end=', ')\n"," else:\n"," print(n, end=', ')\n"," if n % 10 == 0:\n"," print()\n","\n","numbers = range(1, 101)\n","for number in numbers:\n"," if not number % 15:\n"," output = 'FizzBuzz'\n"," elif not number % 3:\n"," output = 'Fizz'\n"," elif not number % 5:\n"," output = 'Buzz'\n"," else:\n"," output = number\n"," if number == numbers[-1]:\n"," print(output, end='.')\n"," elif not number % 10:\n"," print(output, end=',\\n')\n"," else:\n"," print(output, end=', ')\n","\n","range(100)[10]"]}],"metadata":{"colab":{"authorship_tag":"ABX9TyO/OJ7mLjXRcw3YDskGFU1J","provenance":[],"toc_visible":true},"kernelspec":{"display_name":"Python 3","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.10.12"}},"nbformat":4,"nbformat_minor":0} +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ZGnlpVha4WwJ" + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "fqMJHzNk5yXQ" + }, + "source": [ + "# CDH course \"Programming in Python\"\n", + "\n", + "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "mzWET4w4lAr4" + }, + "source": [ + "# 1. Loops\n", + "\n", + "Loops are the most primitive way to run code *repeatedly*. For example:\n", + "\n", + "- Run some lines of code for each word in a text.\n", + "- Run some lines of code for each number in a list.\n", + "- Run some lines of code until you have found the answer to a question.\n", + "\n", + "There are \"smarter\" ways to repeat code that we will cover later in the course." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "d7GJLYfEXwfs" + }, + "source": [ + "## `while` loops\n", + "\n", + "A `while` loop looks like an `if` statement, but it repeats as long as the condition is `True`:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 517, + "status": "ok", + "timestamp": 1667900027935, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -60 + }, + "id": "Fnut4-DIYmb8", + "outputId": "91a840be-aa92-47c9-c07f-bb52cb0447bc" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Sheean!!!!\n" + ] + } + ], + "source": [ + "name = 'Sheean'\n", + "while len(name) < 10:\n", + " name = name + '!'\n", + "print(name)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "p9-je4X0zkhq" + }, + "source": [ + "`while` loops are impractical if you want to repeat something for every element of an iterable, such as a list." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BDCe1ux90B7r" + }, + "source": [ + "## `for` loops over lists\n", + "\n", + "Do something with every element in a list:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 395, + "status": "ok", + "timestamp": 1667900112073, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -60 + }, + "id": "O0XEpRMI0WG2", + "outputId": "41b9437b-26e9-45c4-ffb6-48e2d0510e0b" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "hello\n", + "True\n" + ] + } + ], + "source": [ + "the_list = [1, 'hello', True]\n", + "\n", + "for element in the_list:\n", + " print(element)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gdd6v6LU1DlK" + }, + "source": [ + "The body of the loop is an *indented block*, just like in an `if` or `while` statement. You can have multiple lines that repeat together:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 384, + "status": "ok", + "timestamp": 1667900436458, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -60 + }, + "id": "dpqb0qFc1u0s", + "outputId": "9311ae9c-066d-4dbf-da9a-7c440473dac3" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "A\n", + "A \n", + "very\n", + "A very \n", + "short\n", + "A very short \n", + "sentence\n", + "A very short sentence \n" + ] + } + ], + "source": [ + "words = ['A', 'very', 'short', 'sentence']\n", + "full_text = ''\n", + "\n", + "for word in words:\n", + " full_text = full_text + word + ' '\n", + " print(word)\n", + " print(full_text)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "vDU53qkB2zo4" + }, + "source": [ + "The loop ends when the indentation ends. Here, we calculate the sum of a list of numbers, and print the sum only at the end:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 550, + "status": "ok", + "timestamp": 1667900533282, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -60 + }, + "id": "q6ucnoqeUnlI", + "outputId": "461dfdab-3387-4d9e-d59e-ad73fb8993cd" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "35\n" + ] + } + ], + "source": [ + "numbers = [9, 9, 4, 7, 6]\n", + "sum = 0\n", + "\n", + "for number in numbers:\n", + " sum = sum + number\n", + "print(sum)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0Gun_3cX1ey8" + }, + "source": [ + "## Exercise 1: basic `for` loops\n", + "\n", + "1. Make a small change to the following code block (copied from above), so that it prints the `full_text` only at the end, when it is complete." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "nMF8WE3F19HC" + }, + "outputs": [], + "source": [ + "words = ['A', 'very', 'short', 'sentence']\n", + "full_text = ''\n", + "\n", + "for word in words:\n", + " full_text = full_text + word + ' '\n", + " print(word)\n", + " print(full_text)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8zB10pLC2ZaT" + }, + "source": [ + "2. Using a `for` loop, create a copy of the list of fruits below that has the fruits in the reverse order. Change the contents of the list on the first line to check that your solution is general." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 363, + "status": "ok", + "timestamp": 1667903740371, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -60 + }, + "id": "bAwZ_ipU28AY", + "lines_to_next_cell": 2, + "outputId": "6d1d0526-c780-4eaa-d9e4-2262de2691c2" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "apricot ['apricot']\n", + "banana ['banana', 'apricot']\n", + "cherry ['cherry', 'banana', 'apricot']\n", + "date ['date', 'cherry', 'banana', 'apricot']\n", + "['date', 'cherry', 'banana', 'apricot']\n" + ] + } + ], + "source": [ + "fruits = ['apricot', 'banana', 'cherry', 'date']\n", + "\n", + "# insert your code here\n", + "# SHOULD output ['date', 'cherry', 'banana', 'apricot']\n", + "\n", + "reverse_fruits = fruits[-1::-1]\n", + "print(reverse_fruits)\n", + "\n", + "reverse_fruits = []\n", + "for fruit in fruits[-1::-1]:\n", + " reverse_fruits.append(fruit)\n", + " reverse_fruits = reverse_fruits + [fruit]\n", + "print(reverse_fruits)\n", + "\n", + "reverse_fruits = []\n", + "for fruit in fruits:\n", + " reverse_fruits = [fruit] + reverse_fruits\n", + " print(fruit, reverse_fruits)\n", + "print(reverse_fruits)\n", + "\n", + "reverse_fruits = []\n", + "reverse_fruits = [fruits[0]] + reverse_fruits\n", + "reverse_fruits = [fruits[1]] + reverse_fruits\n", + "reverse_fruits = [fruits[2]] + reverse_fruits\n", + "reverse_fruits = [fruits[3]] + reverse_fruits\n", + "print(reverse_fruits)\n", + "\n", + "reverse_fruits = []\n", + "for fruit in fruits:\n", + " reverse_fruits[0:0] = [fruit]\n", + "print(reverse_fruits)\n", + "\n", + "reverse_fruits = []\n", + "reverse_fruits[0:0] = [fruits[0]]\n", + "reverse_fruits[0:0] = [fruits[1]]\n", + "reverse_fruits[0:0] = [fruits[2]]\n", + "reverse_fruits[0:0] = [fruits[3]]\n", + "\n", + "reverse_fruits = []\n", + "for fruit in fruits:\n", + " reverse_fruits[-1:0] = [fruit]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "DATxv0pM2gQc" + }, + "source": [ + "## Variations on loops\n", + "\n", + "All of the following variations can also be combined." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "MVcUZD4T7j4h" + }, + "source": [ + "### Infinite loop" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "l40DJzDx2nCz" + }, + "outputs": [], + "source": [ + "while True:\n", + " print('Hello, living lab!')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0axR682t-ub4" + }, + "source": [ + "Generally something to avoid, but sometimes useful with `break`." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2xzzyBX43Rbq" + }, + "source": [ + "### Breaking out of a loop" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 439, + "status": "ok", + "timestamp": 1667904280773, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -60 + }, + "id": "kbbKDHwd4Eiq", + "outputId": "6ff54d8a-dfa6-4995-c469-1b3177bce245" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "apricot\n", + "banana\n", + "cherry\n", + "date\n", + "Aww no elderberry. 😞\n" + ] + } + ], + "source": [ + "basket = ['apricot', 'banana', 'cherry', 'date']\n", + "\n", + "for fruit in basket:\n", + " print(fruit)\n", + " if fruit == 'elderberry':\n", + " print('Yay, this basket has elderberry! 🤤')\n", + " break\n", + "else:\n", + " print('Aww no elderberry. 😞')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3C9WlDSd-8zw" + }, + "source": [ + "`break`/`else` can be used both with `for` and `while` loops. The `else` is not required." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ZZGIvGNg673Z" + }, + "source": [ + "### Skipping to the next iteration" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 525, + "status": "ok", + "timestamp": 1667904433491, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -60 + }, + "id": "AyVZVJtL7nMJ", + "outputId": "2df5b782-b01c-4bea-812f-4f17ab255e5e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dishwashing\n", + "laundry\n" + ] + } + ], + "source": [ + "chores = ['dishwashing', 'vacuum cleaning', 'laundry']\n", + "\n", + "for task in chores:\n", + " # I really hate vacuum cleaning\n", + " if task == 'vacuum cleaning':\n", + " continue\n", + " print(task)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3fs83n3H_J_j" + }, + "source": [ + "Works both in `for` and `while`." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "FHf46PtqBvVb" + }, + "source": [ + "### Nested loops\n", + "\n", + "Nesting loops is **allowed**, to arbitrary depth. `for` and `while` can be mixed. However, nesting loops has a big **disadvantage**: your code quickly becomes less readable.\n", + "\n", + "Below, we use nested loops to iterate over nested lists." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 527, + "status": "ok", + "timestamp": 1667904767636, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -60 + }, + "id": "WffnwizrCvPw", + "outputId": "64cc2f10-38da-4276-a401-26eeffd95890" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "new row:\n", + "bread; fluffy; light brown; \n", + "new row:\n", + "olives; savory; dark green; \n", + "new row:\n", + "grapes; sweet; shiny red; \n" + ] + } + ], + "source": [ + "table = [\n", + " ['bread', 'fluffy', 'light brown'],\n", + " ['olives', 'savory', 'dark green'],\n", + " ['grapes', 'sweet', 'shiny red'],\n", + "]\n", + "\n", + "for row in table:\n", + " print('new row:')\n", + " for cell in row:\n", + " print(cell, end='; ') # does not finish with a linebreak\n", + " print() # puts a linebreak" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "DZVVVSYy8HGd" + }, + "source": [ + "### Iterating over a string" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 459, + "status": "ok", + "timestamp": 1667905025657, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -60 + }, + "id": "14DRs2jK8Po7", + "outputId": "bd49b199-0647-4e5b-86fd-19c7d09497ee" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "107 lowercase, 11 uppercase and 60 other.\n" + ] + } + ], + "source": [ + "invitation = '''\n", + " Dear Sheean,\n", + "\n", + " I hereby invite you for my Python party on the 9th of November.\n", + " The bar will open at 2 PM. 🍸 Please bring pseudocode.\n", + "\n", + " Yours sincerely,\n", + " Julian\n", + "'''\n", + "\n", + "lowercase = 0\n", + "uppercase = 0\n", + "other = 0\n", + "for character in invitation:\n", + " if 'a' <= character <= 'z':\n", + " lowercase = lowercase + 1\n", + " elif 'A' <= character <= 'Z':\n", + " uppercase = uppercase + 1\n", + " else:\n", + " other = other + 1\n", + "\n", + "print(lowercase, 'lowercase,', uppercase, 'uppercase and', other, 'other.')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HrfN3OhbELuM" + }, + "source": [ + "### Iterating over generated sequences\n", + "\n", + "*Generators* are iterables that produce one value at a time, so they do not need to store all their values at the same time.\n", + "\n", + "`range` creates a generator that produces consecutive numbers." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 11, + "status": "ok", + "timestamp": 1667905473540, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -60 + }, + "id": "iqNQLZVcHYRX", + "outputId": "40591d82-d9d1-4c9c-c887-042daa2f9a81" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n", + "1\n", + "2\n", + "3\n", + "4\n" + ] + } + ], + "source": [ + "for number in range(5):\n", + " print(number)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9GySTLd_Hq9l" + }, + "source": [ + "`enumerate` creates a new iterable based on another iterable, so you get not only the values but also their indices." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 566, + "status": "ok", + "timestamp": 1667905472137, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -60 + }, + "id": "MIyn99IxH74D", + "outputId": "0c2f832c-c245-49ef-c51c-da9903e163e5" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Fruit number 0 is apricot\n", + "Fruit number 1 is banana\n", + "Fruit number 2 is cherry\n", + "Fruit number 3 is date\n" + ] + } + ], + "source": [ + "basket = ['apricot', 'banana', 'cherry', 'date']\n", + "\n", + "for index, fruit in enumerate(basket):\n", + " print('Fruit number', index, 'is', fruit)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "WGlaXiuxISRX" + }, + "source": [ + "There are many more ways to create generators, and many more ways to use them as well. We will see more of them later on in the course." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-K0CvNkOLEYE" + }, + "source": [ + "## Exercise 2: more loops\n", + "\n", + "1. The code block below is written to help you explore what kind of \"stuff\" comes out of `range` and `enumerate`. Read the code from top to bottom. What stands out to you? Why do you think it was written this way?\n", + "\n", + "2. Run the code block for each example value of `miracle`. Based on the results, describe in your own words what `range` and `enumerate` do." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 465, + "status": "ok", + "timestamp": 1667910549466, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -60 + }, + "id": "7gappYcLOrsu", + "outputId": "6fff15b1-41c9-4798-bbb6-43c9ef12c85d" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "0\n", + "1\n", + "9\n", + "length: 10\n" + ] + } + ], + "source": [ + "basket = ['apricot', 'banana', 'cherry', 'date']\n", + "\n", + "miracle = range(10)\n", + "# miracle = range(2, 11)\n", + "# miracle = range(2, 11, 3)\n", + "# miracle = range(11, 2, -2)\n", + "# miracle = range(0.1, 1.0, 0.1)\n", + "# miracle = enumerate(basket)\n", + "# miracle = enumerate('pirate')\n", + "# miracle = enumerate(range(10))\n", + "\n", + "print(type(miracle))\n", + "\n", + "iteration_count = 0\n", + "for value in miracle:\n", + " iteration_count = iteration_count + 1\n", + " if iteration_count > 2:\n", + " continue\n", + " elif iteration_count == 1:\n", + " print(type(value))\n", + " print(value)\n", + "print(value)\n", + "print('length:', iteration_count)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2Haq4E95bN6T" + }, + "source": [ + "3. The `input` function, demonstrated below, lets you ask the human user for a string and then store it in a variable. Write a program that keeps asking the user for words until the word is `'stop'`. For each word, report to the user how many characters it contains." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 6733, + "status": "ok", + "timestamp": 1667914234752, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -60 + }, + "id": "0k_YQbBccyC_", + "outputId": "15b0c654-f047-4036-b95c-36a42d112d6b" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Please give me a word: charming\n", + "you wrote charming\n", + "the length is 8\n", + "Please give me a word: stop\n" + ] + } + ], + "source": [ + "# word = input('Please give me a word: ')\n", + "# print('you wrote', word)\n", + "word = ''\n", + "while word != 'stop':\n", + " word = input('Please give me a word: ')\n", + " print('you wrote', word)\n", + " print('the length is', len(word))\n", + "\n", + "word = ''\n", + "while True:\n", + " word = input('Please give me a word: ')\n", + " if word == 'stop':\n", + " break\n", + " # else:\n", + " print('you wrote', word)\n", + " length = 0\n", + " for character in word:\n", + " length = length + 1\n", + " print('the length is', length)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "uyqbuhKsUlhG" + }, + "source": [ + "4. *FizzBuzz part 2* (advanced). Look back at your solution to exercise 5.3 from day 1 (*FizzBuzz part 1*). Now write a program that does the following: for each integer from `1` up to and including `100`, print `Fizz`, `Buzz`, `FizzBuzz` or the number itself, following the same rules as before. Separate the words and numbers by commas. Add a newline after every tenth number, so you print ten lines in total. The first line should start like `1, 2, Fizz, 4, Buzz, Fizz, ...` and the last line should be `91, 92, Fizz, 94, Buzz, Fizz, 97, 98, Fizz, Buzz, `." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 674, + "status": "ok", + "timestamp": 1667915778770, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -60 + }, + "id": "BUeMXIQXaKna", + "outputId": "2852de82-cad6-4441-8f47-5555c83f08a4" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "10" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# the end=', ' argument will help you:\n", + "print('this prints with a comma instead of a newline', end=', ')\n", + "print('this as well', end=', ')\n", + "# empty print() adds a newline:\n", + "print()\n", + "print('next line for illustration')\n", + "\n", + "# iteration = 0\n", + "for n in range(1, 101):\n", + " # iteration = iteration + 1\n", + " if n % 15 == 0:\n", + " print('FizzBuzz', end=', ')\n", + " elif n % 3 == 0:\n", + " print('Fizz', end=', ')\n", + " elif n % 5 == 0:\n", + " print('Buzz', end=', ')\n", + " else:\n", + " print(n, end=', ')\n", + " if n % 10 == 0:\n", + " print()\n", + "\n", + "numbers = range(1, 101)\n", + "for number in numbers:\n", + " if not number % 15:\n", + " output = 'FizzBuzz'\n", + " elif not number % 3:\n", + " output = 'Fizz'\n", + " elif not number % 5:\n", + " output = 'Buzz'\n", + " else:\n", + " output = number\n", + " if number == numbers[-1]:\n", + " print(output, end='.')\n", + " elif not number % 10:\n", + " print(output, end=',\\n')\n", + " else:\n", + " print(output, end=', ')\n", + "\n", + "range(100)[10]" + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyO/OJ7mLjXRcw3YDskGFU1J", + "provenance": [], + "toc_visible": true + }, + "jupytext": { + "main_language": "python" + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/lessons/06 Loops - Legacy.py b/lessons/06 Loops - Legacy.py new file mode 100644 index 0000000..bdea0a8 --- /dev/null +++ b/lessons/06 Loops - Legacy.py @@ -0,0 +1,356 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# name: python3 +# --- + +# %% id="ZGnlpVha4WwJ" + +# %% [markdown] id="fqMJHzNk5yXQ" +# # CDH course "Programming in Python" +# +# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) + +# %% [markdown] id="mzWET4w4lAr4" +# # 1. Loops +# +# Loops are the most primitive way to run code *repeatedly*. For example: +# +# - Run some lines of code for each word in a text. +# - Run some lines of code for each number in a list. +# - Run some lines of code until you have found the answer to a question. +# +# There are "smarter" ways to repeat code that we will cover later in the course. + +# %% [markdown] id="d7GJLYfEXwfs" +# ## `while` loops +# +# A `while` loop looks like an `if` statement, but it repeats as long as the condition is `True`: + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 517, "status": "ok", "timestamp": 1667900027935, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}, "user_tz": -60} id="Fnut4-DIYmb8" outputId="91a840be-aa92-47c9-c07f-bb52cb0447bc" +name = 'Sheean' +while len(name) < 10: + name = name + '!' +print(name) + +# %% [markdown] id="p9-je4X0zkhq" +# `while` loops are impractical if you want to repeat something for every element of an iterable, such as a list. + +# %% [markdown] id="BDCe1ux90B7r" +# ## `for` loops over lists +# +# Do something with every element in a list: + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 395, "status": "ok", "timestamp": 1667900112073, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}, "user_tz": -60} id="O0XEpRMI0WG2" outputId="41b9437b-26e9-45c4-ffb6-48e2d0510e0b" +the_list = [1, 'hello', True] + +for element in the_list: + print(element) + +# %% [markdown] id="gdd6v6LU1DlK" +# The body of the loop is an *indented block*, just like in an `if` or `while` statement. You can have multiple lines that repeat together: + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 384, "status": "ok", "timestamp": 1667900436458, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}, "user_tz": -60} id="dpqb0qFc1u0s" outputId="9311ae9c-066d-4dbf-da9a-7c440473dac3" +words = ['A', 'very', 'short', 'sentence'] +full_text = '' + +for word in words: + full_text = full_text + word + ' ' + print(word) + print(full_text) + +# %% [markdown] id="vDU53qkB2zo4" +# The loop ends when the indentation ends. Here, we calculate the sum of a list of numbers, and print the sum only at the end: + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 550, "status": "ok", "timestamp": 1667900533282, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}, "user_tz": -60} id="q6ucnoqeUnlI" outputId="461dfdab-3387-4d9e-d59e-ad73fb8993cd" +numbers = [9, 9, 4, 7, 6] +sum = 0 + +for number in numbers: + sum = sum + number +print(sum) + +# %% [markdown] id="0Gun_3cX1ey8" +# ## Exercise 1: basic `for` loops +# +# 1. Make a small change to the following code block (copied from above), so that it prints the `full_text` only at the end, when it is complete. + +# %% id="nMF8WE3F19HC" +words = ['A', 'very', 'short', 'sentence'] +full_text = '' + +for word in words: + full_text = full_text + word + ' ' + print(word) + print(full_text) + +# %% [markdown] id="8zB10pLC2ZaT" +# 2. Using a `for` loop, create a copy of the list of fruits below that has the fruits in the reverse order. Change the contents of the list on the first line to check that your solution is general. + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 363, "status": "ok", "timestamp": 1667903740371, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}, "user_tz": -60} id="bAwZ_ipU28AY" outputId="6d1d0526-c780-4eaa-d9e4-2262de2691c2" +fruits = ['apricot', 'banana', 'cherry', 'date'] + +# insert your code here +# SHOULD output ['date', 'cherry', 'banana', 'apricot'] + +reverse_fruits = fruits[-1::-1] +print(reverse_fruits) + +reverse_fruits = [] +for fruit in fruits[-1::-1]: + reverse_fruits.append(fruit) + reverse_fruits = reverse_fruits + [fruit] +print(reverse_fruits) + +reverse_fruits = [] +for fruit in fruits: + reverse_fruits = [fruit] + reverse_fruits + print(fruit, reverse_fruits) +print(reverse_fruits) + +reverse_fruits = [] +reverse_fruits = [fruits[0]] + reverse_fruits +reverse_fruits = [fruits[1]] + reverse_fruits +reverse_fruits = [fruits[2]] + reverse_fruits +reverse_fruits = [fruits[3]] + reverse_fruits +print(reverse_fruits) + +reverse_fruits = [] +for fruit in fruits: + reverse_fruits[0:0] = [fruit] +print(reverse_fruits) + +reverse_fruits = [] +reverse_fruits[0:0] = [fruits[0]] +reverse_fruits[0:0] = [fruits[1]] +reverse_fruits[0:0] = [fruits[2]] +reverse_fruits[0:0] = [fruits[3]] + +reverse_fruits = [] +for fruit in fruits: + reverse_fruits[-1:0] = [fruit] + + +# %% [markdown] id="DATxv0pM2gQc" +# ## Variations on loops +# +# All of the following variations can also be combined. + +# %% [markdown] id="MVcUZD4T7j4h" +# ### Infinite loop + +# %% id="l40DJzDx2nCz" +while True: + print('Hello, living lab!') + +# %% [markdown] id="0axR682t-ub4" +# Generally something to avoid, but sometimes useful with `break`. + +# %% [markdown] id="2xzzyBX43Rbq" +# ### Breaking out of a loop + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 439, "status": "ok", "timestamp": 1667904280773, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}, "user_tz": -60} id="kbbKDHwd4Eiq" outputId="6ff54d8a-dfa6-4995-c469-1b3177bce245" +basket = ['apricot', 'banana', 'cherry', 'date'] + +for fruit in basket: + print(fruit) + if fruit == 'elderberry': + print('Yay, this basket has elderberry! 🤤') + break +else: + print('Aww no elderberry. 😞') + +# %% [markdown] id="3C9WlDSd-8zw" +# `break`/`else` can be used both with `for` and `while` loops. The `else` is not required. + +# %% [markdown] id="ZZGIvGNg673Z" +# ### Skipping to the next iteration + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 525, "status": "ok", "timestamp": 1667904433491, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}, "user_tz": -60} id="AyVZVJtL7nMJ" outputId="2df5b782-b01c-4bea-812f-4f17ab255e5e" +chores = ['dishwashing', 'vacuum cleaning', 'laundry'] + +for task in chores: + # I really hate vacuum cleaning + if task == 'vacuum cleaning': + continue + print(task) + +# %% [markdown] id="3fs83n3H_J_j" +# Works both in `for` and `while`. + +# %% [markdown] id="FHf46PtqBvVb" +# ### Nested loops +# +# Nesting loops is **allowed**, to arbitrary depth. `for` and `while` can be mixed. However, nesting loops has a big **disadvantage**: your code quickly becomes less readable. +# +# Below, we use nested loops to iterate over nested lists. + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 527, "status": "ok", "timestamp": 1667904767636, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}, "user_tz": -60} id="WffnwizrCvPw" outputId="64cc2f10-38da-4276-a401-26eeffd95890" +table = [ + ['bread', 'fluffy', 'light brown'], + ['olives', 'savory', 'dark green'], + ['grapes', 'sweet', 'shiny red'], +] + +for row in table: + print('new row:') + for cell in row: + print(cell, end='; ') # does not finish with a linebreak + print() # puts a linebreak + +# %% [markdown] id="DZVVVSYy8HGd" +# ### Iterating over a string + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 459, "status": "ok", "timestamp": 1667905025657, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}, "user_tz": -60} id="14DRs2jK8Po7" outputId="bd49b199-0647-4e5b-86fd-19c7d09497ee" +invitation = ''' + Dear Sheean, + + I hereby invite you for my Python party on the 9th of November. + The bar will open at 2 PM. 🍸 Please bring pseudocode. + + Yours sincerely, + Julian +''' + +lowercase = 0 +uppercase = 0 +other = 0 +for character in invitation: + if 'a' <= character <= 'z': + lowercase = lowercase + 1 + elif 'A' <= character <= 'Z': + uppercase = uppercase + 1 + else: + other = other + 1 + +print(lowercase, 'lowercase,', uppercase, 'uppercase and', other, 'other.') + +# %% [markdown] id="HrfN3OhbELuM" +# ### Iterating over generated sequences +# +# *Generators* are iterables that produce one value at a time, so they do not need to store all their values at the same time. +# +# `range` creates a generator that produces consecutive numbers. + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 11, "status": "ok", "timestamp": 1667905473540, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}, "user_tz": -60} id="iqNQLZVcHYRX" outputId="40591d82-d9d1-4c9c-c887-042daa2f9a81" +for number in range(5): + print(number) + +# %% [markdown] id="9GySTLd_Hq9l" +# `enumerate` creates a new iterable based on another iterable, so you get not only the values but also their indices. + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 566, "status": "ok", "timestamp": 1667905472137, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}, "user_tz": -60} id="MIyn99IxH74D" outputId="0c2f832c-c245-49ef-c51c-da9903e163e5" +basket = ['apricot', 'banana', 'cherry', 'date'] + +for index, fruit in enumerate(basket): + print('Fruit number', index, 'is', fruit) + +# %% [markdown] id="WGlaXiuxISRX" +# There are many more ways to create generators, and many more ways to use them as well. We will see more of them later on in the course. + +# %% [markdown] id="-K0CvNkOLEYE" +# ## Exercise 2: more loops +# +# 1. The code block below is written to help you explore what kind of "stuff" comes out of `range` and `enumerate`. Read the code from top to bottom. What stands out to you? Why do you think it was written this way? +# +# 2. Run the code block for each example value of `miracle`. Based on the results, describe in your own words what `range` and `enumerate` do. + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 465, "status": "ok", "timestamp": 1667910549466, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}, "user_tz": -60} id="7gappYcLOrsu" outputId="6fff15b1-41c9-4798-bbb6-43c9ef12c85d" +basket = ['apricot', 'banana', 'cherry', 'date'] + +miracle = range(10) +# miracle = range(2, 11) +# miracle = range(2, 11, 3) +# miracle = range(11, 2, -2) +# miracle = range(0.1, 1.0, 0.1) +# miracle = enumerate(basket) +# miracle = enumerate('pirate') +# miracle = enumerate(range(10)) + +print(type(miracle)) + +iteration_count = 0 +for value in miracle: + iteration_count = iteration_count + 1 + if iteration_count > 2: + continue + elif iteration_count == 1: + print(type(value)) + print(value) +print(value) +print('length:', iteration_count) + +# %% [markdown] id="2Haq4E95bN6T" +# 3. The `input` function, demonstrated below, lets you ask the human user for a string and then store it in a variable. Write a program that keeps asking the user for words until the word is `'stop'`. For each word, report to the user how many characters it contains. + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 6733, "status": "ok", "timestamp": 1667914234752, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}, "user_tz": -60} id="0k_YQbBccyC_" outputId="15b0c654-f047-4036-b95c-36a42d112d6b" +# word = input('Please give me a word: ') +# print('you wrote', word) +word = '' +while word != 'stop': + word = input('Please give me a word: ') + print('you wrote', word) + print('the length is', len(word)) + +word = '' +while True: + word = input('Please give me a word: ') + if word == 'stop': + break + # else: + print('you wrote', word) + length = 0 + for character in word: + length = length + 1 + print('the length is', length) + +# %% [markdown] id="uyqbuhKsUlhG" +# 4. *FizzBuzz part 2* (advanced). Look back at your solution to exercise 5.3 from day 1 (*FizzBuzz part 1*). Now write a program that does the following: for each integer from `1` up to and including `100`, print `Fizz`, `Buzz`, `FizzBuzz` or the number itself, following the same rules as before. Separate the words and numbers by commas. Add a newline after every tenth number, so you print ten lines in total. The first line should start like `1, 2, Fizz, 4, Buzz, Fizz, ...` and the last line should be `91, 92, Fizz, 94, Buzz, Fizz, 97, 98, Fizz, Buzz, `. + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 674, "status": "ok", "timestamp": 1667915778770, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}, "user_tz": -60} id="BUeMXIQXaKna" outputId="2852de82-cad6-4441-8f47-5555c83f08a4" +# the end=', ' argument will help you: +print('this prints with a comma instead of a newline', end=', ') +print('this as well', end=', ') +# empty print() adds a newline: +print() +print('next line for illustration') + +# iteration = 0 +for n in range(1, 101): + # iteration = iteration + 1 + if n % 15 == 0: + print('FizzBuzz', end=', ') + elif n % 3 == 0: + print('Fizz', end=', ') + elif n % 5 == 0: + print('Buzz', end=', ') + else: + print(n, end=', ') + if n % 10 == 0: + print() + +numbers = range(1, 101) +for number in numbers: + if not number % 15: + output = 'FizzBuzz' + elif not number % 3: + output = 'Fizz' + elif not number % 5: + output = 'Buzz' + else: + output = number + if number == numbers[-1]: + print(output, end='.') + elif not number % 10: + print(output, end=',\n') + else: + print(output, end=', ') + +range(100)[10] diff --git a/lessons/06 Loops.ipynb b/lessons/06 Loops.ipynb index 5956ce8..ddc5281 100644 --- a/lessons/06 Loops.ipynb +++ b/lessons/06 Loops.ipynb @@ -1 +1,608 @@ -{"cells":[{"cell_type":"markdown","metadata":{"id":"fqMJHzNk5yXQ"},"source":["# Module 6: Loops\n","\n","### CDH course \"Programming in Python\"\n","\n","[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n","\n","Previous module: [5. Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM?usp=share_link) "]},{"cell_type":"markdown","metadata":{"id":"mzWET4w4lAr4"},"source":["# 1. Loops\n","\n","Loops are the most primitive way to run code *repeatedly*. For example:\n","\n","- Run some lines of code for each word in a text.\n","- Run some lines of code for each number in a list.\n","- Run some lines of code until you have found the answer to a question.\n","\n","There are \"smarter\" ways to repeat code that we will cover later in the course."]},{"cell_type":"markdown","metadata":{"id":"d7GJLYfEXwfs"},"source":["## `while` loops\n","\n","A `while` loop looks like an `if` statement, but it repeats as long as the condition is `True`:"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"Fnut4-DIYmb8"},"outputs":[],"source":["name = 'Julian'\n","while len(name) < 10:\n"," name = name + '!'\n","print(name)"]},{"cell_type":"markdown","metadata":{"id":"p9-je4X0zkhq"},"source":["`while` loops are impractical if you want to repeat something for every element of an iterable, such as a list."]},{"cell_type":"markdown","metadata":{"id":"BDCe1ux90B7r"},"source":["## `for` loops over lists\n","\n","For loops do something for every element in a list, so the length of the list is the number of iterations. What is especially useful about for-loops is that for every iteration, the item in the list can be used in the indented code block. This means that you can do a certain action for each item in the list without having to provide the index for that item. For example:"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"O0XEpRMI0WG2"},"outputs":[],"source":["the_list = [1, 'hello', True]\n","\n","for element in the_list:\n"," print(element)"]},{"cell_type":"markdown","metadata":{"id":"gdd6v6LU1DlK"},"source":["The body of the loop is an *indented block*, just like in an `if` or `while` statement. You can have multiple lines that repeat together:"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"dpqb0qFc1u0s"},"outputs":[],"source":["words = ['A', 'very', 'short', 'sentence']\n","full_text = ''\n","\n","for word in words:\n"," full_text = full_text + word + ' '\n"," print(word)\n"," print(full_text)"]},{"cell_type":"markdown","metadata":{"id":"vDU53qkB2zo4"},"source":["The loop ends when the indentation ends. Here, we calculate the sum of a list of numbers, and print the sum only at the end:"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":550,"status":"ok","timestamp":1667900533282,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"},"user_tz":-60},"id":"q6ucnoqeUnlI","outputId":"461dfdab-3387-4d9e-d59e-ad73fb8993cd"},"outputs":[{"name":"stdout","output_type":"stream","text":["35\n"]}],"source":["numbers = [9, 9, 4, 7, 6]\n","sum = 0\n","\n","for number in numbers:\n"," sum = sum + number\n","print(sum)"]},{"cell_type":"markdown","metadata":{"id":"0Gun_3cX1ey8"},"source":["## Exercise 1: basic `for` loops\n","\n","1. Make a small change to the following code block (copied from above), so that it prints the `full_text` only at the end, when it is complete."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"nMF8WE3F19HC"},"outputs":[],"source":["words = ['A', 'very', 'short', 'sentence']\n","full_text = ''\n","\n","for word in words:\n"," full_text = full_text + word + ' '\n"," print(word)\n"," print(full_text)"]},{"cell_type":"markdown","metadata":{"id":"8zB10pLC2ZaT"},"source":["2. Using a `for` loop, create a copy of the list of fruits below that has the fruits in the reverse order. Change the contents of the list on the first line to check that your solution is general."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"bAwZ_ipU28AY"},"outputs":[],"source":["fruits = ['apricot', 'banana', 'cherry', 'date']\n","\n","# insert your code here\n","# SHOULD output ['date', 'cherry', 'banana', 'apricot']\n"]},{"cell_type":"markdown","metadata":{"id":"DATxv0pM2gQc"},"source":["## Variations on loops\n","\n","All of the following variations can also be combined."]},{"cell_type":"markdown","metadata":{"id":"MVcUZD4T7j4h"},"source":["### Infinite loop"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"l40DJzDx2nCz"},"outputs":[],"source":["while True:\n"," print('Hello, living lab!')"]},{"cell_type":"markdown","metadata":{"id":"0axR682t-ub4"},"source":["Generally something to avoid, but sometimes useful with `break`."]},{"cell_type":"markdown","metadata":{"id":"2xzzyBX43Rbq"},"source":["### Breaking out of a loop\n","You can break out of a loop by using the `break` statement, like shown here:"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"kbbKDHwd4Eiq"},"outputs":[],"source":["basket = ['apricot', 'banana', 'cherry', 'date']\n","\n","for fruit in basket:\n"," print(fruit)\n"," if fruit == 'elderberry':\n"," print('Yay, this basket has elderberry! 🤤')\n"," break\n","else:\n"," print('Aww no elderberry. 😞')"]},{"cell_type":"markdown","metadata":{"id":"3C9WlDSd-8zw"},"source":["`break`/`else` can be used both with `for` and `while` loops. The `else` is not required though, you can have an `if` statement without it, in which case nothing will happen if the `if` statement returns `False`."]},{"cell_type":"markdown","metadata":{"id":"ZZGIvGNg673Z"},"source":["### Skipping to the next iteration"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"AyVZVJtL7nMJ"},"outputs":[],"source":["chores = ['dishwashing', 'vacuum cleaning', 'laundry']\n","\n","for task in chores:\n"," # I really hate vacuum cleaning\n"," if task == 'vacuum cleaning':\n"," continue\n"," print(task)"]},{"cell_type":"markdown","metadata":{"id":"3fs83n3H_J_j"},"source":["Works both in `for` and `while` loops."]},{"cell_type":"markdown","metadata":{"id":"FHf46PtqBvVb"},"source":["### Nested loops\n","\n","Nesting loops is **allowed**, to arbitrary depth. `for` and `while` can be mixed. However, nesting loops has a big **disadvantage**: your code quickly becomes less readable.\n","\n","Below, we use nested loops to iterate over nested lists."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"WffnwizrCvPw"},"outputs":[],"source":["table = [\n"," ['bread', 'fluffy', 'light brown'],\n"," ['olives', 'savory', 'dark green'],\n"," ['grapes', 'sweet', 'shiny red'],\n","]\n","\n","for row in table:\n"," print('new row:')\n"," for cell in row:\n"," print(cell, end='; ') # does not finish with a linebreak\n"," print() # puts a linebreak"]},{"cell_type":"markdown","metadata":{"id":"DZVVVSYy8HGd"},"source":["### Iterating over a string\n","Note here that when you iterate over a string, python treats the string as if it were a list with characters, including whitespaces. If you want to iterate over the words in a string, you can use the `.split()` function to split the string into a list of strings where each item is one word (separated by whitespace in the original string)."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"14DRs2jK8Po7"},"outputs":[],"source":["invitation = '''\n"," Dear Sheean,\n","\n"," I hereby invite you for my Python party on the 11th of April.\n"," The bar will open at 2 PM. 🍸 Please bring pseudocode.\n","\n"," Yours sincerely,\n"," Julian\n","'''\n","\n","lowercase = 0\n","uppercase = 0\n","other = 0\n","for character in invitation:\n"," if 'a' <= character <= 'z':\n"," lowercase = lowercase + 1\n"," elif 'A' <= character <= 'Z':\n"," uppercase = uppercase + 1\n"," else:\n"," other = other + 1\n","\n","print(lowercase, 'lowercase,', uppercase, 'uppercase and', other, 'other.')\n","\n","long_words = 0\n","short_words = 0\n","for word in invitation.split():\n"," if len(word) > 5:\n"," long_words = long_words + 1\n"," else:\n"," short_words = short_words + 1\n","print(short_words, 'short words', long_words, 'long words')"]},{"cell_type":"markdown","metadata":{"id":"HrfN3OhbELuM"},"source":["### Iterating over generated sequences\n","\n","*Generators* are iterables that produce one value at a time, so they do not need to store all their values at the same time.\n","\n","`range` creates a generator that produces consecutive numbers."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"iqNQLZVcHYRX"},"outputs":[],"source":["for number in range(5):\n"," print(number)"]},{"cell_type":"markdown","metadata":{"id":"9GySTLd_Hq9l"},"source":["`enumerate` creates a new iterable based on another iterable, so you get not only the values but also their indices."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"MIyn99IxH74D"},"outputs":[],"source":["basket = ['apricot', 'banana', 'cherry', 'date']\n","\n","for index, fruit in enumerate(basket):\n"," print('Fruit number', index, 'is', fruit)"]},{"cell_type":"markdown","metadata":{"id":"WGlaXiuxISRX"},"source":["There are many more ways to create generators, and many more ways to use them as well. We will see more of them later on in the course."]},{"cell_type":"markdown","metadata":{"id":"-K0CvNkOLEYE"},"source":["## Exercise 2: more loops\n","\n","1. The code block below is written to help you explore what kind of \"stuff\" comes out of `range` and `enumerate`. Read the code from top to bottom. What stands out to you? Why do you think it was written this way?\n","\n","2. Run the code block for each example value of `miracle`. Based on the results, describe in your own words what `range` and `enumerate` do."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"7gappYcLOrsu"},"outputs":[],"source":["basket = ['apricot', 'banana', 'cherry', 'date']\n","\n","miracle = range(10)\n","# miracle = range(2, 11)\n","# miracle = range(2, 11, 3)\n","# miracle = range(11, 2, -2)\n","# miracle = range(0.1, 1.0, 0.1)\n","# miracle = enumerate(basket)\n","# miracle = enumerate('pirate')\n","# miracle = enumerate(range(10))\n","\n","print('type of miracle:', type(miracle))\n","\n","iteration_count = 0\n","for value in miracle:\n"," iteration_count = iteration_count + 1\n"," if iteration_count > 2:\n"," continue\n"," elif iteration_count == 1:\n"," print('type of first iteration:', type(value))\n"," print('value of current iteration:', value)\n","\n","print('number of iterations:', iteration_count)"]},{"cell_type":"markdown","metadata":{"id":"2Haq4E95bN6T"},"source":["3. The `input` function, demonstrated below, lets you ask the human user for a string and then store it in a variable. Write a program that keeps asking the user for words until the word is `'stop'`. For each word, report to the user how many characters it contains."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"0k_YQbBccyC_"},"outputs":[],"source":["word = input('Please give me a word: ')\n","print('you wrote', word)\n"]},{"cell_type":"markdown","metadata":{"id":"uyqbuhKsUlhG"},"source":["4. *FizzBuzz part 2* (advanced). Look back at your solution to exercise 3.2 from day 1 (*FizzBuzz part 1*). Now write a program that does the following: for each integer from `1` up to and including `100`, print `Fizz`, `Buzz`, `FizzBuzz` or the number itself, following the same rules as before. Separate the words and numbers by commas. Add a newline after every tenth number, so you print ten lines in total. The first line should start like `1, 2, Fizz, 4, Buzz, Fizz, ...` and the last line should be `91, 92, Fizz, 94, Buzz, Fizz, 97, 98, Fizz, Buzz, `."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"BUeMXIQXaKna"},"outputs":[],"source":["# the end=', ' argument will help you:\n","print('this prints with a comma instead of a newline', end=', ')\n","print('this as well', end=', ')\n","# empty print() adds a newline:\n","print()\n","print('next line for illustration')\n"]},{"cell_type":"markdown","metadata":{"id":"0eGibfk04LI0"},"source":["## Next module\n","\n","[7. Functions](https://colab.research.google.com/drive/1APhegD_KpLRFn5bC6Ater2wFpT8_Opfr)"]}],"metadata":{"colab":{"provenance":[],"toc_visible":true},"kernelspec":{"display_name":"Python 3","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.10.12"}},"nbformat":4,"nbformat_minor":0} +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "fqMJHzNk5yXQ" + }, + "source": [ + "# Module 6: Loops\n", + "\n", + "### CDH course \"Programming in Python\"\n", + "\n", + "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "\n", + "Previous module: [5. Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM?usp=share_link) " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "mzWET4w4lAr4" + }, + "source": [ + "# 1. Loops\n", + "\n", + "Loops are the most primitive way to run code *repeatedly*. For example:\n", + "\n", + "- Run some lines of code for each word in a text.\n", + "- Run some lines of code for each number in a list.\n", + "- Run some lines of code until you have found the answer to a question.\n", + "\n", + "There are \"smarter\" ways to repeat code that we will cover later in the course." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "d7GJLYfEXwfs" + }, + "source": [ + "## `while` loops\n", + "\n", + "A `while` loop looks like an `if` statement, but it repeats as long as the condition is `True`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Fnut4-DIYmb8" + }, + "outputs": [], + "source": [ + "name = 'Julian'\n", + "while len(name) < 10:\n", + " name = name + '!'\n", + "print(name)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "p9-je4X0zkhq" + }, + "source": [ + "`while` loops are impractical if you want to repeat something for every element of an iterable, such as a list." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BDCe1ux90B7r" + }, + "source": [ + "## `for` loops over lists\n", + "\n", + "For loops do something for every element in a list, so the length of the list is the number of iterations. What is especially useful about for-loops is that for every iteration, the item in the list can be used in the indented code block. This means that you can do a certain action for each item in the list without having to provide the index for that item. For example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "O0XEpRMI0WG2" + }, + "outputs": [], + "source": [ + "the_list = [1, 'hello', True]\n", + "\n", + "for element in the_list:\n", + " print(element)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gdd6v6LU1DlK" + }, + "source": [ + "The body of the loop is an *indented block*, just like in an `if` or `while` statement. You can have multiple lines that repeat together:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "dpqb0qFc1u0s" + }, + "outputs": [], + "source": [ + "words = ['A', 'very', 'short', 'sentence']\n", + "full_text = ''\n", + "\n", + "for word in words:\n", + " full_text = full_text + word + ' '\n", + " print(word)\n", + " print(full_text)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "vDU53qkB2zo4" + }, + "source": [ + "The loop ends when the indentation ends. Here, we calculate the sum of a list of numbers, and print the sum only at the end:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 550, + "status": "ok", + "timestamp": 1667900533282, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -60 + }, + "id": "q6ucnoqeUnlI", + "outputId": "461dfdab-3387-4d9e-d59e-ad73fb8993cd" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "35\n" + ] + } + ], + "source": [ + "numbers = [9, 9, 4, 7, 6]\n", + "sum = 0\n", + "\n", + "for number in numbers:\n", + " sum = sum + number\n", + "print(sum)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0Gun_3cX1ey8" + }, + "source": [ + "## Exercise 1: basic `for` loops\n", + "\n", + "1. Make a small change to the following code block (copied from above), so that it prints the `full_text` only at the end, when it is complete." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "nMF8WE3F19HC" + }, + "outputs": [], + "source": [ + "words = ['A', 'very', 'short', 'sentence']\n", + "full_text = ''\n", + "\n", + "for word in words:\n", + " full_text = full_text + word + ' '\n", + " print(word)\n", + " print(full_text)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8zB10pLC2ZaT" + }, + "source": [ + "2. Using a `for` loop, create a copy of the list of fruits below that has the fruits in the reverse order. Change the contents of the list on the first line to check that your solution is general." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "bAwZ_ipU28AY", + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "fruits = ['apricot', 'banana', 'cherry', 'date']\n", + "\n", + "# insert your code here\n", + "# SHOULD output ['date', 'cherry', 'banana', 'apricot']" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "DATxv0pM2gQc" + }, + "source": [ + "## Variations on loops\n", + "\n", + "All of the following variations can also be combined." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "MVcUZD4T7j4h" + }, + "source": [ + "### Infinite loop" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "l40DJzDx2nCz" + }, + "outputs": [], + "source": [ + "while True:\n", + " print('Hello, living lab!')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0axR682t-ub4" + }, + "source": [ + "Generally something to avoid, but sometimes useful with `break`." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2xzzyBX43Rbq" + }, + "source": [ + "### Breaking out of a loop\n", + "You can break out of a loop by using the `break` statement, like shown here:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "kbbKDHwd4Eiq" + }, + "outputs": [], + "source": [ + "basket = ['apricot', 'banana', 'cherry', 'date']\n", + "\n", + "for fruit in basket:\n", + " print(fruit)\n", + " if fruit == 'elderberry':\n", + " print('Yay, this basket has elderberry! 🤤')\n", + " break\n", + "else:\n", + " print('Aww no elderberry. 😞')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3C9WlDSd-8zw" + }, + "source": [ + "`break`/`else` can be used both with `for` and `while` loops. The `else` is not required though, you can have an `if` statement without it, in which case nothing will happen if the `if` statement returns `False`." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ZZGIvGNg673Z" + }, + "source": [ + "### Skipping to the next iteration" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "AyVZVJtL7nMJ" + }, + "outputs": [], + "source": [ + "chores = ['dishwashing', 'vacuum cleaning', 'laundry']\n", + "\n", + "for task in chores:\n", + " # I really hate vacuum cleaning\n", + " if task == 'vacuum cleaning':\n", + " continue\n", + " print(task)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3fs83n3H_J_j" + }, + "source": [ + "Works both in `for` and `while` loops." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "FHf46PtqBvVb" + }, + "source": [ + "### Nested loops\n", + "\n", + "Nesting loops is **allowed**, to arbitrary depth. `for` and `while` can be mixed. However, nesting loops has a big **disadvantage**: your code quickly becomes less readable.\n", + "\n", + "Below, we use nested loops to iterate over nested lists." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "WffnwizrCvPw" + }, + "outputs": [], + "source": [ + "table = [\n", + " ['bread', 'fluffy', 'light brown'],\n", + " ['olives', 'savory', 'dark green'],\n", + " ['grapes', 'sweet', 'shiny red'],\n", + "]\n", + "\n", + "for row in table:\n", + " print('new row:')\n", + " for cell in row:\n", + " print(cell, end='; ') # does not finish with a linebreak\n", + " print() # puts a linebreak" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "DZVVVSYy8HGd" + }, + "source": [ + "### Iterating over a string\n", + "Note here that when you iterate over a string, python treats the string as if it were a list with characters, including whitespaces. If you want to iterate over the words in a string, you can use the `.split()` function to split the string into a list of strings where each item is one word (separated by whitespace in the original string)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "14DRs2jK8Po7" + }, + "outputs": [], + "source": [ + "invitation = '''\n", + " Dear Sheean,\n", + "\n", + " I hereby invite you for my Python party on the 11th of April.\n", + " The bar will open at 2 PM. 🍸 Please bring pseudocode.\n", + "\n", + " Yours sincerely,\n", + " Julian\n", + "'''\n", + "\n", + "lowercase = 0\n", + "uppercase = 0\n", + "other = 0\n", + "for character in invitation:\n", + " if 'a' <= character <= 'z':\n", + " lowercase = lowercase + 1\n", + " elif 'A' <= character <= 'Z':\n", + " uppercase = uppercase + 1\n", + " else:\n", + " other = other + 1\n", + "\n", + "print(lowercase, 'lowercase,', uppercase, 'uppercase and', other, 'other.')\n", + "\n", + "long_words = 0\n", + "short_words = 0\n", + "for word in invitation.split():\n", + " if len(word) > 5:\n", + " long_words = long_words + 1\n", + " else:\n", + " short_words = short_words + 1\n", + "print(short_words, 'short words', long_words, 'long words')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HrfN3OhbELuM" + }, + "source": [ + "### Iterating over generated sequences\n", + "\n", + "*Generators* are iterables that produce one value at a time, so they do not need to store all their values at the same time.\n", + "\n", + "`range` creates a generator that produces consecutive numbers." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "iqNQLZVcHYRX" + }, + "outputs": [], + "source": [ + "for number in range(5):\n", + " print(number)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9GySTLd_Hq9l" + }, + "source": [ + "`enumerate` creates a new iterable based on another iterable, so you get not only the values but also their indices." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "MIyn99IxH74D" + }, + "outputs": [], + "source": [ + "basket = ['apricot', 'banana', 'cherry', 'date']\n", + "\n", + "for index, fruit in enumerate(basket):\n", + " print('Fruit number', index, 'is', fruit)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "WGlaXiuxISRX" + }, + "source": [ + "There are many more ways to create generators, and many more ways to use them as well. We will see more of them later on in the course." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-K0CvNkOLEYE" + }, + "source": [ + "## Exercise 2: more loops\n", + "\n", + "1. The code block below is written to help you explore what kind of \"stuff\" comes out of `range` and `enumerate`. Read the code from top to bottom. What stands out to you? Why do you think it was written this way?\n", + "\n", + "2. Run the code block for each example value of `miracle`. Based on the results, describe in your own words what `range` and `enumerate` do." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "7gappYcLOrsu" + }, + "outputs": [], + "source": [ + "basket = ['apricot', 'banana', 'cherry', 'date']\n", + "\n", + "miracle = range(10)\n", + "# miracle = range(2, 11)\n", + "# miracle = range(2, 11, 3)\n", + "# miracle = range(11, 2, -2)\n", + "# miracle = range(0.1, 1.0, 0.1)\n", + "# miracle = enumerate(basket)\n", + "# miracle = enumerate('pirate')\n", + "# miracle = enumerate(range(10))\n", + "\n", + "print('type of miracle:', type(miracle))\n", + "\n", + "iteration_count = 0\n", + "for value in miracle:\n", + " iteration_count = iteration_count + 1\n", + " if iteration_count > 2:\n", + " continue\n", + " elif iteration_count == 1:\n", + " print('type of first iteration:', type(value))\n", + " print('value of current iteration:', value)\n", + "\n", + "print('number of iterations:', iteration_count)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2Haq4E95bN6T" + }, + "source": [ + "3. The `input` function, demonstrated below, lets you ask the human user for a string and then store it in a variable. Write a program that keeps asking the user for words until the word is `'stop'`. For each word, report to the user how many characters it contains." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "0k_YQbBccyC_", + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "word = input('Please give me a word: ')\n", + "print('you wrote', word)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "uyqbuhKsUlhG" + }, + "source": [ + "4. *FizzBuzz part 2* (advanced). Look back at your solution to exercise 3.2 from day 1 (*FizzBuzz part 1*). Now write a program that does the following: for each integer from `1` up to and including `100`, print `Fizz`, `Buzz`, `FizzBuzz` or the number itself, following the same rules as before. Separate the words and numbers by commas. Add a newline after every tenth number, so you print ten lines in total. The first line should start like `1, 2, Fizz, 4, Buzz, Fizz, ...` and the last line should be `91, 92, Fizz, 94, Buzz, Fizz, 97, 98, Fizz, Buzz, `." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "BUeMXIQXaKna", + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "# the end=', ' argument will help you:\n", + "print('this prints with a comma instead of a newline', end=', ')\n", + "print('this as well', end=', ')\n", + "# empty print() adds a newline:\n", + "print()\n", + "print('next line for illustration')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0eGibfk04LI0" + }, + "source": [ + "## Next module\n", + "\n", + "[7. Functions](https://colab.research.google.com/drive/1APhegD_KpLRFn5bC6Ater2wFpT8_Opfr)" + ] + } + ], + "metadata": { + "colab": { + "provenance": [], + "toc_visible": true + }, + "jupytext": { + "main_language": "python" + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/lessons/06 Loops.py b/lessons/06 Loops.py new file mode 100644 index 0000000..bc58df7 --- /dev/null +++ b/lessons/06 Loops.py @@ -0,0 +1,289 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# name: python3 +# --- + +# %% [markdown] id="fqMJHzNk5yXQ" +# # Module 6: Loops +# +# ### CDH course "Programming in Python" +# +# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# +# Previous module: [5. Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM?usp=share_link) + +# %% [markdown] id="mzWET4w4lAr4" +# # 1. Loops +# +# Loops are the most primitive way to run code *repeatedly*. For example: +# +# - Run some lines of code for each word in a text. +# - Run some lines of code for each number in a list. +# - Run some lines of code until you have found the answer to a question. +# +# There are "smarter" ways to repeat code that we will cover later in the course. + +# %% [markdown] id="d7GJLYfEXwfs" +# ## `while` loops +# +# A `while` loop looks like an `if` statement, but it repeats as long as the condition is `True`: + +# %% id="Fnut4-DIYmb8" +name = 'Julian' +while len(name) < 10: + name = name + '!' +print(name) + +# %% [markdown] id="p9-je4X0zkhq" +# `while` loops are impractical if you want to repeat something for every element of an iterable, such as a list. + +# %% [markdown] id="BDCe1ux90B7r" +# ## `for` loops over lists +# +# For loops do something for every element in a list, so the length of the list is the number of iterations. What is especially useful about for-loops is that for every iteration, the item in the list can be used in the indented code block. This means that you can do a certain action for each item in the list without having to provide the index for that item. For example: + +# %% id="O0XEpRMI0WG2" +the_list = [1, 'hello', True] + +for element in the_list: + print(element) + +# %% [markdown] id="gdd6v6LU1DlK" +# The body of the loop is an *indented block*, just like in an `if` or `while` statement. You can have multiple lines that repeat together: + +# %% id="dpqb0qFc1u0s" +words = ['A', 'very', 'short', 'sentence'] +full_text = '' + +for word in words: + full_text = full_text + word + ' ' + print(word) + print(full_text) + +# %% [markdown] id="vDU53qkB2zo4" +# The loop ends when the indentation ends. Here, we calculate the sum of a list of numbers, and print the sum only at the end: + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 550, "status": "ok", "timestamp": 1667900533282, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}, "user_tz": -60} id="q6ucnoqeUnlI" outputId="461dfdab-3387-4d9e-d59e-ad73fb8993cd" +numbers = [9, 9, 4, 7, 6] +sum = 0 + +for number in numbers: + sum = sum + number +print(sum) + +# %% [markdown] id="0Gun_3cX1ey8" +# ## Exercise 1: basic `for` loops +# +# 1. Make a small change to the following code block (copied from above), so that it prints the `full_text` only at the end, when it is complete. + +# %% id="nMF8WE3F19HC" +words = ['A', 'very', 'short', 'sentence'] +full_text = '' + +for word in words: + full_text = full_text + word + ' ' + print(word) + print(full_text) + +# %% [markdown] id="8zB10pLC2ZaT" +# 2. Using a `for` loop, create a copy of the list of fruits below that has the fruits in the reverse order. Change the contents of the list on the first line to check that your solution is general. + +# %% id="bAwZ_ipU28AY" +fruits = ['apricot', 'banana', 'cherry', 'date'] + +# insert your code here +# SHOULD output ['date', 'cherry', 'banana', 'apricot'] + + +# %% [markdown] id="DATxv0pM2gQc" +# ## Variations on loops +# +# All of the following variations can also be combined. + +# %% [markdown] id="MVcUZD4T7j4h" +# ### Infinite loop + +# %% id="l40DJzDx2nCz" +while True: + print('Hello, living lab!') + +# %% [markdown] id="0axR682t-ub4" +# Generally something to avoid, but sometimes useful with `break`. + +# %% [markdown] id="2xzzyBX43Rbq" +# ### Breaking out of a loop +# You can break out of a loop by using the `break` statement, like shown here: + +# %% id="kbbKDHwd4Eiq" +basket = ['apricot', 'banana', 'cherry', 'date'] + +for fruit in basket: + print(fruit) + if fruit == 'elderberry': + print('Yay, this basket has elderberry! 🤤') + break +else: + print('Aww no elderberry. 😞') + +# %% [markdown] id="3C9WlDSd-8zw" +# `break`/`else` can be used both with `for` and `while` loops. The `else` is not required though, you can have an `if` statement without it, in which case nothing will happen if the `if` statement returns `False`. + +# %% [markdown] id="ZZGIvGNg673Z" +# ### Skipping to the next iteration + +# %% id="AyVZVJtL7nMJ" +chores = ['dishwashing', 'vacuum cleaning', 'laundry'] + +for task in chores: + # I really hate vacuum cleaning + if task == 'vacuum cleaning': + continue + print(task) + +# %% [markdown] id="3fs83n3H_J_j" +# Works both in `for` and `while` loops. + +# %% [markdown] id="FHf46PtqBvVb" +# ### Nested loops +# +# Nesting loops is **allowed**, to arbitrary depth. `for` and `while` can be mixed. However, nesting loops has a big **disadvantage**: your code quickly becomes less readable. +# +# Below, we use nested loops to iterate over nested lists. + +# %% id="WffnwizrCvPw" +table = [ + ['bread', 'fluffy', 'light brown'], + ['olives', 'savory', 'dark green'], + ['grapes', 'sweet', 'shiny red'], +] + +for row in table: + print('new row:') + for cell in row: + print(cell, end='; ') # does not finish with a linebreak + print() # puts a linebreak + +# %% [markdown] id="DZVVVSYy8HGd" +# ### Iterating over a string +# Note here that when you iterate over a string, python treats the string as if it were a list with characters, including whitespaces. If you want to iterate over the words in a string, you can use the `.split()` function to split the string into a list of strings where each item is one word (separated by whitespace in the original string). + +# %% id="14DRs2jK8Po7" +invitation = ''' + Dear Sheean, + + I hereby invite you for my Python party on the 11th of April. + The bar will open at 2 PM. 🍸 Please bring pseudocode. + + Yours sincerely, + Julian +''' + +lowercase = 0 +uppercase = 0 +other = 0 +for character in invitation: + if 'a' <= character <= 'z': + lowercase = lowercase + 1 + elif 'A' <= character <= 'Z': + uppercase = uppercase + 1 + else: + other = other + 1 + +print(lowercase, 'lowercase,', uppercase, 'uppercase and', other, 'other.') + +long_words = 0 +short_words = 0 +for word in invitation.split(): + if len(word) > 5: + long_words = long_words + 1 + else: + short_words = short_words + 1 +print(short_words, 'short words', long_words, 'long words') + +# %% [markdown] id="HrfN3OhbELuM" +# ### Iterating over generated sequences +# +# *Generators* are iterables that produce one value at a time, so they do not need to store all their values at the same time. +# +# `range` creates a generator that produces consecutive numbers. + +# %% id="iqNQLZVcHYRX" +for number in range(5): + print(number) + +# %% [markdown] id="9GySTLd_Hq9l" +# `enumerate` creates a new iterable based on another iterable, so you get not only the values but also their indices. + +# %% id="MIyn99IxH74D" +basket = ['apricot', 'banana', 'cherry', 'date'] + +for index, fruit in enumerate(basket): + print('Fruit number', index, 'is', fruit) + +# %% [markdown] id="WGlaXiuxISRX" +# There are many more ways to create generators, and many more ways to use them as well. We will see more of them later on in the course. + +# %% [markdown] id="-K0CvNkOLEYE" +# ## Exercise 2: more loops +# +# 1. The code block below is written to help you explore what kind of "stuff" comes out of `range` and `enumerate`. Read the code from top to bottom. What stands out to you? Why do you think it was written this way? +# +# 2. Run the code block for each example value of `miracle`. Based on the results, describe in your own words what `range` and `enumerate` do. + +# %% id="7gappYcLOrsu" +basket = ['apricot', 'banana', 'cherry', 'date'] + +miracle = range(10) +# miracle = range(2, 11) +# miracle = range(2, 11, 3) +# miracle = range(11, 2, -2) +# miracle = range(0.1, 1.0, 0.1) +# miracle = enumerate(basket) +# miracle = enumerate('pirate') +# miracle = enumerate(range(10)) + +print('type of miracle:', type(miracle)) + +iteration_count = 0 +for value in miracle: + iteration_count = iteration_count + 1 + if iteration_count > 2: + continue + elif iteration_count == 1: + print('type of first iteration:', type(value)) + print('value of current iteration:', value) + +print('number of iterations:', iteration_count) + +# %% [markdown] id="2Haq4E95bN6T" +# 3. The `input` function, demonstrated below, lets you ask the human user for a string and then store it in a variable. Write a program that keeps asking the user for words until the word is `'stop'`. For each word, report to the user how many characters it contains. + +# %% id="0k_YQbBccyC_" +word = input('Please give me a word: ') +print('you wrote', word) + + +# %% [markdown] id="uyqbuhKsUlhG" +# 4. *FizzBuzz part 2* (advanced). Look back at your solution to exercise 3.2 from day 1 (*FizzBuzz part 1*). Now write a program that does the following: for each integer from `1` up to and including `100`, print `Fizz`, `Buzz`, `FizzBuzz` or the number itself, following the same rules as before. Separate the words and numbers by commas. Add a newline after every tenth number, so you print ten lines in total. The first line should start like `1, 2, Fizz, 4, Buzz, Fizz, ...` and the last line should be `91, 92, Fizz, 94, Buzz, Fizz, 97, 98, Fizz, Buzz, `. + +# %% id="BUeMXIQXaKna" +# the end=', ' argument will help you: +print('this prints with a comma instead of a newline', end=', ') +print('this as well', end=', ') +# empty print() adds a newline: +print() +print('next line for illustration') + + +# %% [markdown] id="0eGibfk04LI0" +# ## Next module +# +# [7. Functions](https://colab.research.google.com/drive/1APhegD_KpLRFn5bC6Ater2wFpT8_Opfr) diff --git a/lessons/07 Functions.ipynb b/lessons/07 Functions.ipynb index 648a7c0..12a3fba 100644 --- a/lessons/07 Functions.ipynb +++ b/lessons/07 Functions.ipynb @@ -1 +1,1546 @@ -{"cells":[{"cell_type":"markdown","metadata":{"id":"fqMJHzNk5yXQ"},"source":["# Module 7: Functions\n","\n","### CDH course \"Programming in Python\"\n","\n","[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n","\n","Previous module: [6. Loops](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV)\n","\n","### This module\n","\n","- Storing code in a variable so you can reuse it.\n","- Being explicit about the purpose of your code."]},{"cell_type":"markdown","metadata":{"id":"mnc8HshNPLte"},"source":["## (Re)Introducing functions\n","\n","A function is a reusable piece of code, stored in a variable. You *call* a function by passing it a list of *arguments* between parentheses. The function *returns* a value, which you can then use in an expression or store in another variable. We have already seen several functions:"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"IP2mOWYgR-QA"},"outputs":[],"source":["result = ord('🎵')\n","print(type(result), result)\n","\n","result = len([1, 2, 3])\n","print(type(result), result)\n","\n","result = str(ord)\n","print(type(result), result)"]},{"cell_type":"markdown","metadata":{"id":"u_YUDiccZKwh"},"source":["The fact that functions are variables, is illustrated by the fact that you can store them in variables and even reassign them:"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"Mct6Hy3HZa5H"},"outputs":[],"source":["my_ord = ord\n","print(my_ord('a'))\n","ord = chr\n","print(ord(97))\n","\n","# don't do this with 'list'!!!!!\n","\n","# back to sanity!\n","ord = my_ord\n","\n","# this can also restore sanity\n","%reset_selective ord\n","\n","# reset resets everything\n","%reset\n","\n","ord('a')"]},{"cell_type":"markdown","metadata":{"id":"hBgqVL7sSyFx"},"source":["The list of arguments may be empty. For example, if you pass no arguments to `list`, it returns an empty list:"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"wb98z1kBS9tm"},"outputs":[],"source":["result = list()\n","print(result)"]},{"cell_type":"markdown","metadata":{"id":"Byutz190TEH-"},"source":["You can also pass multiple arguments (depending on the function)."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"N7YW9_X_UOuB"},"outputs":[],"source":["result = max(3, 4, 5)\n","print(result)"]},{"cell_type":"markdown","metadata":{"id":"H0SocqBbXQ6f"},"source":["A *named argument* or *keyword argument*, prefixed with `name=`, makes the purpose of the argument explicit. Below, the argument with the name `end` has the value `' '`."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"uTk4InjdXg5g"},"outputs":[],"source":["print('one', end=' ')\n","print('two')"]},{"cell_type":"markdown","metadata":{"id":"dCUHGgepctcp"},"source":["Arguments *without* a name are called *positional* arguments, because their roles are determined by the order in which they are supplied.\n","\n","In our example of `max(3, 4, 5)` above, `3`, `4`, and `5` are positional arguments. In this case, the order of the numbers does not actually matter (the maximum is the same, no matter what).\n","\n","However, for most functions, this is not the case! An example you have already seen is `range`: the first argument is the _start_ and the second argument is the _stop_."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"yCyPbsvAd5PB"},"outputs":[],"source":["assert max(3, 4, 5) is max(4, 3, 5)\n","\n","assert range(1, 5) is not range(5, 1)"]},{"cell_type":"markdown","metadata":{"id":"ECGJttnRUX_J"},"source":["A function may return \"nothing\", i.e., `None`."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"gAMO-QD4Uuxe"},"outputs":[],"source":["result = print('hello')\n","print(result)"]},{"cell_type":"markdown","metadata":{"id":"Bj4diwbcU1Q7"},"source":["A function may even return multiple results, i.e., a tuple. We can *unpack* those directly into multiple variables, but we can also store the whole tuple in a single variable."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"yYRQIGnoVHWA"},"outputs":[],"source":["my_tuple = tuple(['Jelte', 'Julian'])\n","result1, result2 = my_tuple\n","print(my_tuple)\n","print(result1)\n","print(result2)"]},{"cell_type":"markdown","metadata":{"id":"xMHYhvPhVXet"},"source":["Some functions have *side effects*: apart from taking arguments and returning results, they also have some other external effect."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"OKEha-rwVo99"},"outputs":[],"source":["result1 = input('Please enter PIN: ') # returns input from user\n","result2 = print(result1) # returns nothing\n","print(result2)\n","print(result1)"]},{"cell_type":"markdown","metadata":{"id":"emAs76qOagVF"},"source":["## Functions are awesome!\n","\n","The following code, which does *not* use enough functions, is **bad**:"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"n0UOjklcbEHQ"},"outputs":[],"source":["print('\"banana\" has 6 characters and starts with a lowercase')\n","print('\"Ada\" has 3 characters and starts with an uppercase')\n","print('\"epigraph\" has 8 characters and starts with a lowercase')"]},{"cell_type":"markdown","metadata":{"id":"vAt_0PfecAGK"},"source":["Problems:\n","\n","- repetitive\n","- not explicit\n","- unmaintainable\n","\n","To some extent, we can address these problems with a loop:"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"MeUnr3KNcaVZ"},"outputs":[],"source":["words = ['banana', 'Ada', 'epigraph']\n","\n","for word in words:\n"," quoted = '\"' + word + '\"'\n"," length = len(word)\n"," first_character = word[0]\n"," if 'A' <= first_character <= 'Z':\n"," initial = 'an uppercase'\n"," else:\n"," initial = 'a lowercase'\n"," print(quoted, 'has', length, 'characters and starts with', initial)"]},{"cell_type":"markdown","metadata":{"id":"FL0w8VMkeLm7"},"source":["BUT\n","\n","- still not explicit\n","- still repetitive, need to repeat the loop if I want to do this elsewhere"]},{"cell_type":"markdown","metadata":{"id":"bBiz8La8jv81"},"source":["Functions let us solve all problems!"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"HOjpPJBNiSvr"},"outputs":[],"source":["def describe(word):\n"," quoted = '\"' + word + '\"'\n"," length = str(len(word))\n"," first_character = word[0]\n"," if 'A' <= first_character <= 'Z':\n"," initial = 'an uppercase'\n"," else:\n"," initial = 'a lowercase'\n"," return quoted + ' has ' + length + ' characters and starts with ' + initial\n","\n","words = ['banana', 'Ada', 'epigraph']\n","\n","for word in words:\n"," print(describe(word))"]},{"cell_type":"markdown","metadata":{"id":"y77LwKKjk-2D"},"source":["## Basic function definitions\n","\n","At a minimum, you need the keyword `def`, a name, a pair of parentheses, a colon, and an indented block. The indented block is called the *function body*. It can contain anything!"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"Ph3RsaBbllvB"},"outputs":[],"source":["# Defining the function with the name \"example\"\n","def example():\n"," pass # ah, that placeholder again\n","\n","# Calling the function with the name \"example\"\n","result = example()\n","print(result)"]},{"cell_type":"markdown","metadata":{"id":"6Esxqtn0y1Ai"},"source":["Keep in mind that a function *definition* only stores the code of the function body inside a variable with the name of the function. The code **does not run** until you *call* the function."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"vBfiX0-WzWQg"},"outputs":[],"source":["def example():\n"," # Don't call print inside a function!\n"," # We only do this here for demonstration purposes.\n"," print('Hello, world!')\n","\n","print(type(example), example)"]},{"cell_type":"markdown","metadata":{"id":"S3cY2mRgl9z8"},"source":["Between the parentheses, we list the *parameters*, the \"slots\" that can be filled by passing arguments when calling the function.\n","\n","The parameters (and any new variables that you make within the function body) have limited _scope_: they only live within the body of the function."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"fWAzMGqfnC8p"},"outputs":[],"source":["def example(name, age):\n"," print(name)\n"," print(age)\n","\n","name = 'Sheean'\n","age = 36\n","\n","print(name)\n","print(age)\n","\n","result = example('Julian', 35)\n","print(result)\n","\n","print(name)\n","print(age)"]},{"cell_type":"markdown","metadata":{"id":"J5Pd_TjZqnwj"},"source":["At the end of the function body, a function will implicitly return `None`. The `return` keyword lets you return a different value, return before the end of the body, or both. You can use the keyword multiple times, but **a function can return only once**, so an early return will skip the remainder of the function body."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"48ARWWmvrgjz"},"outputs":[],"source":["def heaviness(item, weight):\n"," if type(weight) not in (int, float):\n"," # early return, implicit None\n"," return\n"," if weight >= 5:\n"," # early return, different value\n"," return 'heavy'\n"," # final return, different value\n"," return 'light'\n","\n","result = heaviness('letter', 'paper weight')\n","print(result)\n","\n","result = heaviness('feather', 0.01)\n","print(result)\n","\n","result = heaviness('bowling ball', 8)\n","print(result)"]},{"cell_type":"markdown","metadata":{"id":"kOlF8tonzwZr"},"source":["Functions should **not** have side effects unless that is the very purpose of the function (like with `print` and `input`). Most functions should return a value rather than printing something to the screen!"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"EHX1V-E71EkI"},"outputs":[],"source":["# GOOD: function returns a value\n","def exclaim(text):\n"," return text + '!'\n","\n","value = exclaim('Julian')\n","print(value)\n","\n","# BAD: function prints to the screen\n","def exclaim(text):\n"," print(text + '!')\n","\n","value = exclaim('Julian')\n","print(value)\n","\n","# OK: function is intended as a special print wrapper\n","def fancy_print(text):\n"," print('>>>', text, '<<<')\n","\n","fancy_print('Julian')"]},{"cell_type":"markdown","metadata":{"id":"spLYBkqtvDmF"},"source":["It is a good habit to start a function with a *documentation string*, **docstring** for short. A docstring should explain the parameters and possible return values of the function, as well as any rules or side effects that apply.\n","\n","A docstring should start with a short description of the function that fits on a single line. If there is more to explain, add the rest after a blank line.\n","\n","By convention, docstrings use three double quotes."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"DR6t3PCCvSEX"},"outputs":[],"source":["def heaviness(item, weight):\n"," \"\"\"\n"," Given the name of an item and its weight in kg, classify its heaviness.\n","\n"," If the weight is not a number, it cannot be classified. Otherwise, the\n"," classification is returned as a string: either 'light' or 'heavy'.\n"," \"\"\"\n"," if type(weight) not in (int, float):\n"," return\n"," if weight >= 5:\n"," return 'heavy'\n"," return 'light'\n","\n","print(heaviness.__doc__)\n","\n","print(len.__doc__)\n","print(ord.__doc__)\n","print(print.__doc__)"]},{"cell_type":"markdown","metadata":{"id":"aPFGhEVz40JP"},"source":["## Exercise 7.1: functions"]},{"cell_type":"markdown","metadata":{"id":"hfcz-cSEKZWW"},"source":["1. We have seen several standard functions during the course, such as `ord` and `len`. List as many as you can from memory. Try to list the types of the parameters and return values of each function as well. Do any of these functions have side effects?"]},{"cell_type":"markdown","metadata":{"id":"BUnMsiUzKbws"},"source":["2. For exercise purposes, code in the following blocks may call `print` inside a function and omit a docstring. Predict the output, then run the code to see whether your prediction was right. Try to explain any discrepancies."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"_3va9jT5O0H7"},"outputs":[],"source":["def greet(name):\n"," return 'Hello, ' + name + '!'\n","\n","print(greet('Berit'))"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"vdTIwoGxM_JV"},"outputs":[],"source":["name = 'Luka'\n","\n","def exclaim(name):\n"," print(name + '!')"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"30fv8SAMOblV"},"outputs":[],"source":["def false():\n"," return True\n","\n","print(False)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"ongrNaZFNNmV"},"outputs":[],"source":["length = 5\n","width = 2\n","\n","def calculate_area():\n"," area = length * width\n","\n","print(calculate_area())"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"MSkOCMMyNoUO"},"outputs":[],"source":["def question(name):\n"," return 'Who is ' + name + '?'\n","\n","question('Sheean')"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"72DDRhD5OQ0g"},"outputs":[],"source":["def add(left, right):\n"," return left + right\n","\n","print(add('sweet', 'addition') * add(1, 1))"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"L0GlJecf8ntf"},"outputs":[],"source":["name = 'Julian'\n","age = 36\n","\n","print(name)\n","\n","def example(name):\n"," print(name)\n"," name = 'Berit'\n"," print(name)\n"," print(age)\n","\n","print(name)\n","print(example)\n","print(name)\n","example('Jelte')\n","print(name)\n","print(age)"]},{"cell_type":"markdown","metadata":{"id":"Rwvwlpp0-Hrt"},"source":["3. In each of the following code blocks, write a docstring for the function, then add assertions and run the block to check that your documentation is correct."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"ajcRnvzQQ9c5"},"outputs":[],"source":["def odd(number):\n"," return number % 2 == 1"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"giU_bIKrRME4"},"outputs":[],"source":["def magic(word):\n"," if not word or type(word) != str:\n"," return False\n"," if 'A' <= word[0] <= 'Z':\n"," return True\n"," return False"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"pAChbRWn-SKS"},"outputs":[],"source":["def join_commas(words):\n"," first, *rest = list(words)\n"," text = first\n"," for word in rest:\n"," text = text + ', ' + word\n"," return text"]},{"cell_type":"markdown","metadata":{"id":"mc9RtAeATiHw"},"source":["4. Write a function `is_number` that takes one argument. If the argument is a number, it should return `True`; otherwise, it should return `False`. Make sure to include a docstring!"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"Ap33-rF-UbsB"},"outputs":[],"source":["# Your definition of is_number here\n","\n","# The following lines will check your solution (no output is good)\n","assert is_number(0)\n","assert is_number(10)\n","assert is_number(0.5)\n","assert is_number(8 / 5)\n","assert not is_number(None)\n","assert not is_number('dear')\n","assert not is_number('123')"]},{"cell_type":"markdown","metadata":{"id":"apA7o120TYRl"},"source":["## Exercise 7.2: bonus"]},{"cell_type":"markdown","metadata":{"id":"nM43w3VlB3-O"},"source":["1. Write a function `last_a_index` that, given a string, returns the **last** index of the letter `'a'` in the string. If there is no `'a'` in the string, the function should return `None`."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"I5s4_a53ENJC"},"outputs":[],"source":["# Define last_a_index here\n","\n","assert last_a_index('banana') == 5\n","assert last_a_index('cherry') == None\n","assert last_a_index('Once upon a time, there was a dragon') == 32"]},{"cell_type":"markdown","metadata":{"id":"z4z3-dOaVROx"},"source":["2. Write a function `replace` with three string arguments: `text`, `query` and `replacement`. It should return a copy of `text` in which all occurrences of `query` are replaced by `replacement`. For example, `replace('small', 'a', 'e')` should return `'smell'`. You may assume that `query` is always a single character. For an additional challenge, you can try to also handle multi-character query strings. For ultimate challenge, try to think of a way to handle the empty string!"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"kyQwfz-mYvfW"},"outputs":[],"source":["# Define replace here\n","\n","assert replace('small', 'a', 'e') == 'smell'\n","assert replace('banana', 'a', 'o') == 'bonono'\n","assert replace('cherry', 'a', 'x') == 'cherry'"]},{"cell_type":"markdown","metadata":{"id":"3Lx61L5B0Zqe"},"source":["## Refining parameters and return values"]},{"cell_type":"markdown","metadata":{"id":"gRae5PaguhV9"},"source":["### Returning multiple values (in a tuple)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"MJqHyAI31FLZ"},"outputs":[],"source":["def word_statistics(word):\n"," \"\"\" Given a string, return its length and the case of its first letter. \"\"\"\n"," if 'A' <= word[0] <= 'Z':\n"," casing = 'upper'\n"," else:\n"," casing = 'lower'\n"," return len(word), casing\n","\n","size, initial = word_statistics('Sheean')\n","print(size)\n","print(initial)"]},{"cell_type":"markdown","metadata":{"id":"1ORLASkU2Uga"},"source":["### Optional parameters\n","\n","Parameters can be made *optional* by using the notation `=None` in the parameter list."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"IuDsXgrK3rK8"},"outputs":[],"source":["def emphasize(word, emphasis=None):\n"," \"\"\"\n"," Given a string, return a version with an exclamation mark appended.\n","\n"," The exclamation mark can be replaced by passing the override in a\n"," second argument.\n"," \"\"\"\n"," if emphasis == None:\n"," return word + '!'\n"," return word + emphasis\n","\n","print(emphasize('Julian'))\n","print(emphasize('Julian', '!!!'))"]},{"cell_type":"markdown","metadata":{"id":"2gPmuYUe1nyO"},"source":["### Parameters with default values\n","\n","The notation `=None` is actually a special case. You can use any value as a default for a parameter. However, it is easy to make mistakes in this way. In case of doubt, just use `None` and add a check inside the function body, as demonstrated above."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"IqabfNzi2-XV"},"outputs":[],"source":["# Literal strings, numbers and booleans are safe\n","def default_string(value='hello'):\n"," return value\n","\n","def default_int(value=10):\n"," return value\n","\n","def default_bool(value=True):\n"," return value\n","\n","# Do not use variables as default values!\n","name = 'Julian'\n","def default_name(value=name):\n"," return value\n","\n","name = 'Sheean'\n","print(default_name())\n","\n","# Do not use data structures as default values!\n","def append_list(container=[], value=1):\n"," container.append(value)\n"," return container\n","\n","print(append_list())\n","print(append_list())"]},{"cell_type":"markdown","metadata":{"id":"_mTHAoog6glc"},"source":["### Mixing positional and named arguments\n","\n","So far, we demonstrated functions where every parameter has its own unique name. With such parameters, the caller is free to choose between positional arguments and named arguments. The order of named arguments can be freely chosen, but they must come after all positional arguments."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"ETGRDi8J7cyP"},"outputs":[],"source":["def tax_rate(income, entrepreneur):\n"," \"\"\" Determine tax rate, given income and whether payer is entrepreneur. \"\"\"\n"," if entrepreneur:\n"," return 20\n"," first_bracket = min(income, 50000)\n"," second_bracket = income - first_bracket\n"," return (first_bracket * 30 + second_bracket * 40) / income\n","\n","print(tax_rate(40000, True))\n","print(tax_rate(40000, entrepreneur=True))\n","print(tax_rate(income=40000, entrepreneur=True))\n","print(tax_rate(entrepreneur=True, income=40000))\n","print(tax_rate(True, income=40000))\n","print(tax_rate(income=40000, True))"]},{"cell_type":"markdown","metadata":{"id":"1jWzKu5omWUV"},"source":["## Functions as first-class citizens\n","\n","As mentioned previously, a function is just a variable. That means you can do the same things with it as with other values. For example, you can pass a function as an argument to another function:"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"ukVglqcwsAdT"},"outputs":[],"source":["def exclaim(text):\n"," return text + '!'\n","\n","fruits = ['apricot', 'banana', 'cherry']\n","\n","exclaimed_fruits = map(exclaim, fruits)\n","\n","print(type(exclaimed_fruits), exclaimed_fruits)\n","\n","list(exclaimed_fruits)"]},{"cell_type":"markdown","metadata":{"id":"l4dAFklEvdhZ"},"source":["What is going on inside `map`? Something like this:"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"V2-kY5O6xHRo"},"outputs":[],"source":["def my_map(function, values):\n"," \"\"\" Repeat `function` for every value in `values`. \"\"\"\n"," for value in values:\n"," result = function(value)\n"," # there is an additional step that we will cover later"]},{"cell_type":"markdown","metadata":{"id":"TZXA2q46taZi"},"source":["Also, a function can define another function inside its body and then use and/or return it:"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"fG6-q8EatpRz"},"outputs":[],"source":["def make_greeter(prefix, suffix):\n"," \"\"\" Create a function that greets a person with the given phrasing. \"\"\"\n"," def greeter(name):\n"," \"\"\" Wrap the given name in a greeting. \"\"\"\n"," return prefix + ', ' + name + suffix\n"," return greeter\n","\n","greet_british = make_greeter('How do you do', '?')\n","greet_american = make_greeter('Hi there', '!')\n","\n","print(greet_british('Sheean'))\n","print(greet_british('Julian'))\n","print(greet_american('Sheean'))\n","print(greet_american('Julian'))"]},{"cell_type":"markdown","metadata":{"id":"1xUD2ZNjcqEZ"},"source":["## Exercise 7.3: function tricks"]},{"cell_type":"markdown","metadata":{"id":"hxRC5Nx-cyM4"},"source":["1. In each of the code blocks below, predict the output, check your prediction by running the code, and try to explain any surprises."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"HPxio0q6c-M9"},"outputs":[],"source":["def welcome(name=None):\n"," if name is None:\n"," return 'Welcome to my office. How can I help you?'\n"," return 'Please make yourself at home, ' + name + '!'\n","\n","print(welcome())\n","print(welcome('Sheean'))"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"NxXqK4ROdxva"},"outputs":[],"source":["def table(number):\n"," return number, number * 2, number * 3\n","\n","first, second, third = table(5)\n","\n","print(third)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"EVCf3w9qfBCU"},"outputs":[],"source":["def contact(name, job):\n"," return 'For ' + job + ', contact ' + name + '.'\n","\n","contact_line = contact(job='naming', name='Job')\n","print(contact_line)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"OUSpe-hPl6-G"},"outputs":[],"source":["def exclaim(name):\n"," return name + '!'\n","\n","def request(name):\n"," return name + '?'\n","\n","def tell(how):\n"," return 'As my mother would say: ' + how('Julian')\n","\n","print(tell(request))"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"uSN17qnvmoWv"},"outputs":[],"source":["def make_enumerate(separator):\n"," def enumerate(items):\n"," if not len(items):\n"," return ''\n"," result = items[0]\n"," for item in items[1:]:\n"," result = result + separator + item\n"," return result\n"," return enumerate\n","\n","with_semicolon = make_enumerate('; ')\n","fruits = with_semicolon(['apricot', 'banana', 'cherry'])\n","print(fruits)"]},{"cell_type":"markdown","metadata":{"id":"CKma4p6Egkwb"},"source":["2. In each of the following code blocks, something is missing in the function definition. Add the missing element."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"SaygnjMygwNF"},"outputs":[],"source":["def combine(left, right):\n"," \"\"\" Compute the sum and the product of the arguments. \"\"\"\n"," sum = left + right\n"," product = left * right\n"," return product\n","\n","assert combine(2, 3) == (5, 6)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"GqIgTcG6hMuG"},"outputs":[],"source":["def announce_time(hour, minute):\n"," \"\"\" Announce the time in a speaking clock manner. \"\"\"\n"," if minute < 10:\n"," minute = '0' + str(minute)\n"," time = str(hour) + ':' + str(minute)\n"," return 'At the next beep, the time will be ' + time + '. BEEP!'\n","\n","assert announce_time(12) == 'At the next beep, the time will be 12:00. BEEP!'"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"nH77gJdOkS5b"},"outputs":[],"source":["def echo(value):\n"," print(value)\n"," return value\n","\n","assert echo('holiday') == 'holiday'"]},{"cell_type":"markdown","metadata":{"id":"YG5XrO1PoIJx"},"source":["3. In the following code block, write a function `month`, which returns a string name for given numeric month. Month `1` is `'January'` and month `12` is `December`. When no argument is passed, it should default to the current month. **Tip:** you can avoid writing a big `if`/`elif`/`else` tree by using a list of month names and using the month number as a list index."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"TnuU_I0Tq9wQ"},"outputs":[],"source":["# You may pretend that it is forever April\n","current_month = 4\n","\n","# Your definition of month here\n","\n","assert month(3) == 'March'\n","assert month(4) == 'April'\n","assert month(11) == 'November'\n","assert month() == 'April'"]},{"cell_type":"markdown","metadata":{"id":"WuRrElhUsD40"},"source":["4. In the following code block, write a function `weekday`, which is analogous to `month` in the previous exercise. Day `1` is `'Monday'` while day `0` and day `7` are both `'Sunday'`. Can you avoid writing the string `'Sunday'` twice in your code?"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"WUGQqmJysrqS"},"outputs":[],"source":["# You may pretend it is forever Tuesday\n","current_weekday = 2\n","\n","# Your definition of weekday here\n","\n","assert weekday() == 'Tuesday'\n","assert weekday(0) == 'Sunday'\n","assert weekday(7) == 'Sunday'\n","assert weekday(4) == 'Thursday'"]},{"cell_type":"markdown","metadata":{"id":"ZvfEq3NctoOo"},"source":["## Exercise 7.4: bonus"]},{"cell_type":"markdown","metadata":{"id":"8au2fNRutw8i"},"source":["1. In exercises 7.3.4 and 7.3.5, where you just implemented `month` and `weekday`, your code was likely very similar. How could you implement both functions without repeating yourself? Check your solution with assertions."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"JFhOX_Z5uVfC"},"outputs":[],"source":[]},{"cell_type":"markdown","metadata":{"id":"Gx54DrgJuWKg"},"source":["2. Write a function `create_date_format`, which takes two arguments that determine how a date should be displayed. The first argument is function that transforms a weekday number to a string suitable for display, the second argument is a function that transforms a month number. Both arguments default to just displaying the number as-is. `create_date_format` returns a new function. The latter function takes any date defined by a year, month, day and weekday, and returns a string suitable for displaying that date as a whole."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"0KyXFHEWwZ45"},"outputs":[],"source":["# For this code block, you need to have `month` and `weekday`\n","# in your runtime from previous exercises.\n","\n","# Your definition of create_date_format here\n","\n","wordy_format = create_date_format(weekday, month)\n","cryptic_format = create_date_format()\n","\n","assert wordy_format(2023, 4, 11, 2) == 'Tuesday, April 11, 2023'\n","assert cryptic_format(2023, 4, 11, 2) == '2, 4 11, 2023'"]},{"cell_type":"markdown","metadata":{"id":"b-p6Ct5_0I-x"},"source":["## Exercise 7.5: ultimate FizzBuzz (more bonus)\n","\n","1. Write two functions `fizz(number)` and `buzz(number)`. If `number` is divisible by 3, `fizz` should return the string `'Fizz'`; otherwise, it should return the empty string `''`. `buzz` should work like `fizz`, but for divisibility by 5 and the string `'Buzz'`."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"407dPPK966R9"},"outputs":[],"source":["# Your solution here\n","\n","for number in [1, 2, 4, 5, 7, 8, 10, 11, 13, 14]:\n"," assert fizz(number) == '', str(number) + ' is not divisible by 3'\n","for number in [3, 6, 9, 12, 15, 18, 21, 24, 27]:\n"," assert fizz(number) == 'Fizz', str(number) + ' is divisible by 3'\n","for number in [1, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14]:\n"," assert buzz(number) == '', str(number) + ' is not divisible by 5'\n","for number in [5, 10, 15, 20, 25, 30, 35, 40]:\n"," assert buzz(number) == 'Buzz', str(number) + ' is divisible by 5'"]},{"cell_type":"markdown","metadata":{"id":"QCKc52-r9DrX"},"source":["2. Using your functions `fizz` and `buzz` from step 1, write the function `fizzbuzz(number)`, which implements the well-known rules from before, but with a slight **twist**:\n"," - If the number is divisible by 3 and 5, return `'FizzBuzz'`.\n"," - If the number is divisible by 3 but not by 5, return `'Fizz'`.\n"," - If the number is divisible by 5 but not by 3, return `'Buzz'`.\n"," - In all other cases, return the number itself **as a string**."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"NFADyIW3-7qt"},"outputs":[],"source":["# Your solution here\n","\n","for number in [1, 2, 4, 7, 8, 11, 13, 14]:\n"," assert fizzbuzz(number) == str(number), str(number)\n","for number in [3, 6, 9, 12, 18, 21, 24, 27]:\n"," assert fizzbuzz(number) == 'Fizz', str(number)\n","for number in [5, 10, 20, 25, 35, 40]:\n"," assert fizzbuzz(number) == 'Buzz', str(number)\n","for number in [15, 30, 45, 60, 75]:\n"," assert fizzbuzz(number) == 'FizzBuzz', str(number)"]},{"cell_type":"markdown","metadata":{"id":"o_3wq4agCCZH"},"source":["3. Write a function `chunk10(sequence)` that takes an iterable `sequence` and returns a list of lists: the first sublist contains the first ten elements of `sequence`, the second sublist contains the next ten elements of `sequence`, and so on. You may assume that the number of values in `sequence` is an exact multiple of 10."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"4c0A4kMfDdvt"},"outputs":[],"source":["# Your solution here\n","\n","assert chunk10('Sheean!!!!!') == [list('Sheean!!!!!')]\n","assert chunk10('Hey Julian, let us have lunch!') == [\n"," list('Hey Julian'),\n"," list(', let us h'),\n"," list('ave lunch!'),\n","]\n","assert chunk10(range(20)) == [list(range(10)), list(range(10, 20))]"]},{"cell_type":"markdown","metadata":{"id":"HBA4z4yVIhsn"},"source":["4. In step 1, you may have written definitions for `fizz` and `buzz` that look very similar to each other. Can you think of a way to avoid repeating the common elements?"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"5g4BRdpLJLIc"},"outputs":[],"source":["# Your solution here\n","\n","for number in [1, 2, 4, 5, 7, 8, 10, 11, 13, 14]:\n"," assert fizz(number) == '', str(number) + ' is not divisible by 3'\n","for number in [3, 6, 9, 12, 15, 18, 21, 24, 27]:\n"," assert fizz(number) == 'Fizz', str(number) + ' is divisible by 3'\n","for number in [1, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14]:\n"," assert buzz(number) == '', str(number) + ' is not divisible by 5'\n","for number in [5, 10, 15, 20, 25, 30, 35, 40]:\n"," assert buzz(number) == 'Buzz', str(number) + ' is divisible by 5'"]},{"cell_type":"markdown","metadata":{"id":"BXzFYNGmPeO6"},"source":["5. In exercise 7.1.3, you documented the function `join_commas`. We did not actually need to write that function, because it is built into Python, although it goes by the name `', '.join`. That notation also works with other strings, as we demonstrate below. We also remind you of `map`, which appeared in the lecture.

\n","Using these functions, as well as your own `fizzbuzz` from step 2 and your own `chunk10` from step 3, try to recreate (roughly) the same `FizzBuzz` output for the numbers 1 through 100 as in [exercise 6.2.4](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV#scrollTo=uyqbuhKsUlhG), in as little code as you can."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"nDGUS26IMApD"},"outputs":[],"source":["# The following code is just for illustration.\n","# You do not need it in your solution.\n","fruits = ['apricot', 'banana', 'cherry']\n","print(', '.join(fruits))\n","print('\\n'.join(fruits))\n","numbers = list(range(10))\n","print(numbers)\n","strings = map(str, numbers)\n","print(list(strings))"]},{"cell_type":"markdown","metadata":{"id":"Dntbbioh29xm"},"source":["## Next module\n","\n","[8. Debugging](https://colab.research.google.com/drive/1yQskT6SyKvXtXewx5kCla2NOmasgP8Vi)"]}],"metadata":{"colab":{"provenance":[],"toc_visible":true},"kernelspec":{"display_name":"Python 3","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.10.12"}},"nbformat":4,"nbformat_minor":0} +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "fqMJHzNk5yXQ" + }, + "source": [ + "# Module 7: Functions\n", + "\n", + "### CDH course \"Programming in Python\"\n", + "\n", + "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "\n", + "Previous module: [6. Loops](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV)\n", + "\n", + "### This module\n", + "\n", + "- Storing code in a variable so you can reuse it.\n", + "- Being explicit about the purpose of your code." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "mnc8HshNPLte" + }, + "source": [ + "## (Re)Introducing functions\n", + "\n", + "A function is a reusable piece of code, stored in a variable. You *call* a function by passing it a list of *arguments* between parentheses. The function *returns* a value, which you can then use in an expression or store in another variable. We have already seen several functions:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "IP2mOWYgR-QA" + }, + "outputs": [], + "source": [ + "result = ord('🎵')\n", + "print(type(result), result)\n", + "\n", + "result = len([1, 2, 3])\n", + "print(type(result), result)\n", + "\n", + "result = str(ord)\n", + "print(type(result), result)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "u_YUDiccZKwh" + }, + "source": [ + "The fact that functions are variables, is illustrated by the fact that you can store them in variables and even reassign them:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Mct6Hy3HZa5H" + }, + "outputs": [], + "source": [ + "my_ord = ord\n", + "print(my_ord('a'))\n", + "ord = chr\n", + "print(ord(97))\n", + "\n", + "# don't do this with 'list'!!!!!\n", + "\n", + "# back to sanity!\n", + "ord = my_ord\n", + "\n", + "# this can also restore sanity\n", + "%reset_selective ord\n", + "\n", + "# reset resets everything\n", + "%reset\n", + "\n", + "ord('a')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "hBgqVL7sSyFx" + }, + "source": [ + "The list of arguments may be empty. For example, if you pass no arguments to `list`, it returns an empty list:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "wb98z1kBS9tm" + }, + "outputs": [], + "source": [ + "result = list()\n", + "print(result)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Byutz190TEH-" + }, + "source": [ + "You can also pass multiple arguments (depending on the function)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "N7YW9_X_UOuB" + }, + "outputs": [], + "source": [ + "result = max(3, 4, 5)\n", + "print(result)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "H0SocqBbXQ6f" + }, + "source": [ + "A *named argument* or *keyword argument*, prefixed with `name=`, makes the purpose of the argument explicit. Below, the argument with the name `end` has the value `' '`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "uTk4InjdXg5g" + }, + "outputs": [], + "source": [ + "print('one', end=' ')\n", + "print('two')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "dCUHGgepctcp" + }, + "source": [ + "Arguments *without* a name are called *positional* arguments, because their roles are determined by the order in which they are supplied.\n", + "\n", + "In our example of `max(3, 4, 5)` above, `3`, `4`, and `5` are positional arguments. In this case, the order of the numbers does not actually matter (the maximum is the same, no matter what).\n", + "\n", + "However, for most functions, this is not the case! An example you have already seen is `range`: the first argument is the _start_ and the second argument is the _stop_." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "yCyPbsvAd5PB" + }, + "outputs": [], + "source": [ + "assert max(3, 4, 5) is max(4, 3, 5)\n", + "\n", + "assert range(1, 5) is not range(5, 1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ECGJttnRUX_J" + }, + "source": [ + "A function may return \"nothing\", i.e., `None`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "gAMO-QD4Uuxe" + }, + "outputs": [], + "source": [ + "result = print('hello')\n", + "print(result)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Bj4diwbcU1Q7" + }, + "source": [ + "A function may even return multiple results, i.e., a tuple. We can *unpack* those directly into multiple variables, but we can also store the whole tuple in a single variable." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "yYRQIGnoVHWA" + }, + "outputs": [], + "source": [ + "my_tuple = tuple(['Jelte', 'Julian'])\n", + "result1, result2 = my_tuple\n", + "print(my_tuple)\n", + "print(result1)\n", + "print(result2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xMHYhvPhVXet" + }, + "source": [ + "Some functions have *side effects*: apart from taking arguments and returning results, they also have some other external effect." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "OKEha-rwVo99" + }, + "outputs": [], + "source": [ + "result1 = input('Please enter PIN: ') # returns input from user\n", + "result2 = print(result1) # returns nothing\n", + "print(result2)\n", + "print(result1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "emAs76qOagVF" + }, + "source": [ + "## Functions are awesome!\n", + "\n", + "The following code, which does *not* use enough functions, is **bad**:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "n0UOjklcbEHQ" + }, + "outputs": [], + "source": [ + "print('\"banana\" has 6 characters and starts with a lowercase')\n", + "print('\"Ada\" has 3 characters and starts with an uppercase')\n", + "print('\"epigraph\" has 8 characters and starts with a lowercase')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "vAt_0PfecAGK" + }, + "source": [ + "Problems:\n", + "\n", + "- repetitive\n", + "- not explicit\n", + "- unmaintainable\n", + "\n", + "To some extent, we can address these problems with a loop:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "MeUnr3KNcaVZ" + }, + "outputs": [], + "source": [ + "words = ['banana', 'Ada', 'epigraph']\n", + "\n", + "for word in words:\n", + " quoted = '\"' + word + '\"'\n", + " length = len(word)\n", + " first_character = word[0]\n", + " if 'A' <= first_character <= 'Z':\n", + " initial = 'an uppercase'\n", + " else:\n", + " initial = 'a lowercase'\n", + " print(quoted, 'has', length, 'characters and starts with', initial)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "FL0w8VMkeLm7" + }, + "source": [ + "BUT\n", + "\n", + "- still not explicit\n", + "- still repetitive, need to repeat the loop if I want to do this elsewhere" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "bBiz8La8jv81" + }, + "source": [ + "Functions let us solve all problems!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "HOjpPJBNiSvr" + }, + "outputs": [], + "source": [ + "def describe(word):\n", + " quoted = '\"' + word + '\"'\n", + " length = str(len(word))\n", + " first_character = word[0]\n", + " if 'A' <= first_character <= 'Z':\n", + " initial = 'an uppercase'\n", + " else:\n", + " initial = 'a lowercase'\n", + " return quoted + ' has ' + length + ' characters and starts with ' + initial\n", + "\n", + "words = ['banana', 'Ada', 'epigraph']\n", + "\n", + "for word in words:\n", + " print(describe(word))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "y77LwKKjk-2D" + }, + "source": [ + "## Basic function definitions\n", + "\n", + "At a minimum, you need the keyword `def`, a name, a pair of parentheses, a colon, and an indented block. The indented block is called the *function body*. It can contain anything!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Ph3RsaBbllvB" + }, + "outputs": [], + "source": [ + "# Defining the function with the name \"example\"\n", + "def example():\n", + " pass # ah, that placeholder again\n", + "\n", + "# Calling the function with the name \"example\"\n", + "result = example()\n", + "print(result)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6Esxqtn0y1Ai" + }, + "source": [ + "Keep in mind that a function *definition* only stores the code of the function body inside a variable with the name of the function. The code **does not run** until you *call* the function." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "vBfiX0-WzWQg" + }, + "outputs": [], + "source": [ + "def example():\n", + " # Don't call print inside a function!\n", + " # We only do this here for demonstration purposes.\n", + " print('Hello, world!')\n", + "\n", + "print(type(example), example)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "S3cY2mRgl9z8" + }, + "source": [ + "Between the parentheses, we list the *parameters*, the \"slots\" that can be filled by passing arguments when calling the function.\n", + "\n", + "The parameters (and any new variables that you make within the function body) have limited _scope_: they only live within the body of the function." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "fWAzMGqfnC8p" + }, + "outputs": [], + "source": [ + "def example(name, age):\n", + " print(name)\n", + " print(age)\n", + "\n", + "name = 'Sheean'\n", + "age = 36\n", + "\n", + "print(name)\n", + "print(age)\n", + "\n", + "result = example('Julian', 35)\n", + "print(result)\n", + "\n", + "print(name)\n", + "print(age)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "J5Pd_TjZqnwj" + }, + "source": [ + "At the end of the function body, a function will implicitly return `None`. The `return` keyword lets you return a different value, return before the end of the body, or both. You can use the keyword multiple times, but **a function can return only once**, so an early return will skip the remainder of the function body." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "48ARWWmvrgjz" + }, + "outputs": [], + "source": [ + "def heaviness(item, weight):\n", + " if type(weight) not in (int, float):\n", + " # early return, implicit None\n", + " return\n", + " if weight >= 5:\n", + " # early return, different value\n", + " return 'heavy'\n", + " # final return, different value\n", + " return 'light'\n", + "\n", + "result = heaviness('letter', 'paper weight')\n", + "print(result)\n", + "\n", + "result = heaviness('feather', 0.01)\n", + "print(result)\n", + "\n", + "result = heaviness('bowling ball', 8)\n", + "print(result)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kOlF8tonzwZr" + }, + "source": [ + "Functions should **not** have side effects unless that is the very purpose of the function (like with `print` and `input`). Most functions should return a value rather than printing something to the screen!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "EHX1V-E71EkI" + }, + "outputs": [], + "source": [ + "# GOOD: function returns a value\n", + "def exclaim(text):\n", + " return text + '!'\n", + "\n", + "value = exclaim('Julian')\n", + "print(value)\n", + "\n", + "# BAD: function prints to the screen\n", + "def exclaim(text):\n", + " print(text + '!')\n", + "\n", + "value = exclaim('Julian')\n", + "print(value)\n", + "\n", + "# OK: function is intended as a special print wrapper\n", + "def fancy_print(text):\n", + " print('>>>', text, '<<<')\n", + "\n", + "fancy_print('Julian')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "spLYBkqtvDmF" + }, + "source": [ + "It is a good habit to start a function with a *documentation string*, **docstring** for short. A docstring should explain the parameters and possible return values of the function, as well as any rules or side effects that apply.\n", + "\n", + "A docstring should start with a short description of the function that fits on a single line. If there is more to explain, add the rest after a blank line.\n", + "\n", + "By convention, docstrings use three double quotes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "DR6t3PCCvSEX" + }, + "outputs": [], + "source": [ + "def heaviness(item, weight):\n", + " \"\"\"\n", + " Given the name of an item and its weight in kg, classify its heaviness.\n", + "\n", + " If the weight is not a number, it cannot be classified. Otherwise, the\n", + " classification is returned as a string: either 'light' or 'heavy'.\n", + " \"\"\"\n", + " if type(weight) not in (int, float):\n", + " return\n", + " if weight >= 5:\n", + " return 'heavy'\n", + " return 'light'\n", + "\n", + "print(heaviness.__doc__)\n", + "\n", + "print(len.__doc__)\n", + "print(ord.__doc__)\n", + "print(print.__doc__)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "aPFGhEVz40JP" + }, + "source": [ + "## Exercise 7.1: functions" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "hfcz-cSEKZWW" + }, + "source": [ + "1. We have seen several standard functions during the course, such as `ord` and `len`. List as many as you can from memory. Try to list the types of the parameters and return values of each function as well. Do any of these functions have side effects?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BUnMsiUzKbws" + }, + "source": [ + "2. For exercise purposes, code in the following blocks may call `print` inside a function and omit a docstring. Predict the output, then run the code to see whether your prediction was right. Try to explain any discrepancies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "_3va9jT5O0H7" + }, + "outputs": [], + "source": [ + "def greet(name):\n", + " return 'Hello, ' + name + '!'\n", + "\n", + "print(greet('Berit'))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "vdTIwoGxM_JV" + }, + "outputs": [], + "source": [ + "name = 'Luka'\n", + "\n", + "def exclaim(name):\n", + " print(name + '!')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "30fv8SAMOblV" + }, + "outputs": [], + "source": [ + "def false():\n", + " return True\n", + "\n", + "print(False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ongrNaZFNNmV" + }, + "outputs": [], + "source": [ + "length = 5\n", + "width = 2\n", + "\n", + "def calculate_area():\n", + " area = length * width\n", + "\n", + "print(calculate_area())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "MSkOCMMyNoUO" + }, + "outputs": [], + "source": [ + "def question(name):\n", + " return 'Who is ' + name + '?'\n", + "\n", + "question('Sheean')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "72DDRhD5OQ0g" + }, + "outputs": [], + "source": [ + "def add(left, right):\n", + " return left + right\n", + "\n", + "print(add('sweet', 'addition') * add(1, 1))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "L0GlJecf8ntf" + }, + "outputs": [], + "source": [ + "name = 'Julian'\n", + "age = 36\n", + "\n", + "print(name)\n", + "\n", + "def example(name):\n", + " print(name)\n", + " name = 'Berit'\n", + " print(name)\n", + " print(age)\n", + "\n", + "print(name)\n", + "print(example)\n", + "print(name)\n", + "example('Jelte')\n", + "print(name)\n", + "print(age)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Rwvwlpp0-Hrt" + }, + "source": [ + "3. In each of the following code blocks, write a docstring for the function, then add assertions and run the block to check that your documentation is correct." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ajcRnvzQQ9c5" + }, + "outputs": [], + "source": [ + "def odd(number):\n", + " return number % 2 == 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "giU_bIKrRME4" + }, + "outputs": [], + "source": [ + "def magic(word):\n", + " if not word or type(word) != str:\n", + " return False\n", + " if 'A' <= word[0] <= 'Z':\n", + " return True\n", + " return False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "pAChbRWn-SKS" + }, + "outputs": [], + "source": [ + "def join_commas(words):\n", + " first, *rest = list(words)\n", + " text = first\n", + " for word in rest:\n", + " text = text + ', ' + word\n", + " return text" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "mc9RtAeATiHw" + }, + "source": [ + "4. Write a function `is_number` that takes one argument. If the argument is a number, it should return `True`; otherwise, it should return `False`. Make sure to include a docstring!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Ap33-rF-UbsB" + }, + "outputs": [], + "source": [ + "# Your definition of is_number here\n", + "\n", + "# The following lines will check your solution (no output is good)\n", + "assert is_number(0)\n", + "assert is_number(10)\n", + "assert is_number(0.5)\n", + "assert is_number(8 / 5)\n", + "assert not is_number(None)\n", + "assert not is_number('dear')\n", + "assert not is_number('123')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "apA7o120TYRl" + }, + "source": [ + "## Exercise 7.2: bonus" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "nM43w3VlB3-O" + }, + "source": [ + "1. Write a function `last_a_index` that, given a string, returns the **last** index of the letter `'a'` in the string. If there is no `'a'` in the string, the function should return `None`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "I5s4_a53ENJC" + }, + "outputs": [], + "source": [ + "# Define last_a_index here\n", + "\n", + "assert last_a_index('banana') == 5\n", + "assert last_a_index('cherry') == None\n", + "assert last_a_index('Once upon a time, there was a dragon') == 32" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "z4z3-dOaVROx" + }, + "source": [ + "2. Write a function `replace` with three string arguments: `text`, `query` and `replacement`. It should return a copy of `text` in which all occurrences of `query` are replaced by `replacement`. For example, `replace('small', 'a', 'e')` should return `'smell'`. You may assume that `query` is always a single character. For an additional challenge, you can try to also handle multi-character query strings. For ultimate challenge, try to think of a way to handle the empty string!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "kyQwfz-mYvfW" + }, + "outputs": [], + "source": [ + "# Define replace here\n", + "\n", + "assert replace('small', 'a', 'e') == 'smell'\n", + "assert replace('banana', 'a', 'o') == 'bonono'\n", + "assert replace('cherry', 'a', 'x') == 'cherry'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3Lx61L5B0Zqe" + }, + "source": [ + "## Refining parameters and return values" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gRae5PaguhV9" + }, + "source": [ + "### Returning multiple values (in a tuple)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "MJqHyAI31FLZ" + }, + "outputs": [], + "source": [ + "def word_statistics(word):\n", + " \"\"\" Given a string, return its length and the case of its first letter. \"\"\"\n", + " if 'A' <= word[0] <= 'Z':\n", + " casing = 'upper'\n", + " else:\n", + " casing = 'lower'\n", + " return len(word), casing\n", + "\n", + "size, initial = word_statistics('Sheean')\n", + "print(size)\n", + "print(initial)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1ORLASkU2Uga" + }, + "source": [ + "### Optional parameters\n", + "\n", + "Parameters can be made *optional* by using the notation `=None` in the parameter list." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "IuDsXgrK3rK8" + }, + "outputs": [], + "source": [ + "def emphasize(word, emphasis=None):\n", + " \"\"\"\n", + " Given a string, return a version with an exclamation mark appended.\n", + "\n", + " The exclamation mark can be replaced by passing the override in a\n", + " second argument.\n", + " \"\"\"\n", + " if emphasis == None:\n", + " return word + '!'\n", + " return word + emphasis\n", + "\n", + "print(emphasize('Julian'))\n", + "print(emphasize('Julian', '!!!'))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2gPmuYUe1nyO" + }, + "source": [ + "### Parameters with default values\n", + "\n", + "The notation `=None` is actually a special case. You can use any value as a default for a parameter. However, it is easy to make mistakes in this way. In case of doubt, just use `None` and add a check inside the function body, as demonstrated above." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "IqabfNzi2-XV" + }, + "outputs": [], + "source": [ + "# Literal strings, numbers and booleans are safe\n", + "def default_string(value='hello'):\n", + " return value\n", + "\n", + "def default_int(value=10):\n", + " return value\n", + "\n", + "def default_bool(value=True):\n", + " return value\n", + "\n", + "# Do not use variables as default values!\n", + "name = 'Julian'\n", + "def default_name(value=name):\n", + " return value\n", + "\n", + "name = 'Sheean'\n", + "print(default_name())\n", + "\n", + "# Do not use data structures as default values!\n", + "def append_list(container=[], value=1):\n", + " container.append(value)\n", + " return container\n", + "\n", + "print(append_list())\n", + "print(append_list())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "_mTHAoog6glc" + }, + "source": [ + "### Mixing positional and named arguments\n", + "\n", + "So far, we demonstrated functions where every parameter has its own unique name. With such parameters, the caller is free to choose between positional arguments and named arguments. The order of named arguments can be freely chosen, but they must come after all positional arguments." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ETGRDi8J7cyP" + }, + "outputs": [], + "source": [ + "def tax_rate(income, entrepreneur):\n", + " \"\"\" Determine tax rate, given income and whether payer is entrepreneur. \"\"\"\n", + " if entrepreneur:\n", + " return 20\n", + " first_bracket = min(income, 50000)\n", + " second_bracket = income - first_bracket\n", + " return (first_bracket * 30 + second_bracket * 40) / income\n", + "\n", + "print(tax_rate(40000, True))\n", + "print(tax_rate(40000, entrepreneur=True))\n", + "print(tax_rate(income=40000, entrepreneur=True))\n", + "print(tax_rate(entrepreneur=True, income=40000))\n", + "print(tax_rate(True, income=40000))\n", + "print(tax_rate(income=40000, True))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1jWzKu5omWUV" + }, + "source": [ + "## Functions as first-class citizens\n", + "\n", + "As mentioned previously, a function is just a variable. That means you can do the same things with it as with other values. For example, you can pass a function as an argument to another function:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ukVglqcwsAdT" + }, + "outputs": [], + "source": [ + "def exclaim(text):\n", + " return text + '!'\n", + "\n", + "fruits = ['apricot', 'banana', 'cherry']\n", + "\n", + "exclaimed_fruits = map(exclaim, fruits)\n", + "\n", + "print(type(exclaimed_fruits), exclaimed_fruits)\n", + "\n", + "list(exclaimed_fruits)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "l4dAFklEvdhZ" + }, + "source": [ + "What is going on inside `map`? Something like this:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "V2-kY5O6xHRo" + }, + "outputs": [], + "source": [ + "def my_map(function, values):\n", + " \"\"\" Repeat `function` for every value in `values`. \"\"\"\n", + " for value in values:\n", + " result = function(value)\n", + " # there is an additional step that we will cover later" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "TZXA2q46taZi" + }, + "source": [ + "Also, a function can define another function inside its body and then use and/or return it:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "fG6-q8EatpRz" + }, + "outputs": [], + "source": [ + "def make_greeter(prefix, suffix):\n", + " \"\"\" Create a function that greets a person with the given phrasing. \"\"\"\n", + " def greeter(name):\n", + " \"\"\" Wrap the given name in a greeting. \"\"\"\n", + " return prefix + ', ' + name + suffix\n", + " return greeter\n", + "\n", + "greet_british = make_greeter('How do you do', '?')\n", + "greet_american = make_greeter('Hi there', '!')\n", + "\n", + "print(greet_british('Sheean'))\n", + "print(greet_british('Julian'))\n", + "print(greet_american('Sheean'))\n", + "print(greet_american('Julian'))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1xUD2ZNjcqEZ" + }, + "source": [ + "## Exercise 7.3: function tricks" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "hxRC5Nx-cyM4" + }, + "source": [ + "1. In each of the code blocks below, predict the output, check your prediction by running the code, and try to explain any surprises." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "HPxio0q6c-M9" + }, + "outputs": [], + "source": [ + "def welcome(name=None):\n", + " if name is None:\n", + " return 'Welcome to my office. How can I help you?'\n", + " return 'Please make yourself at home, ' + name + '!'\n", + "\n", + "print(welcome())\n", + "print(welcome('Sheean'))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "NxXqK4ROdxva" + }, + "outputs": [], + "source": [ + "def table(number):\n", + " return number, number * 2, number * 3\n", + "\n", + "first, second, third = table(5)\n", + "\n", + "print(third)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "EVCf3w9qfBCU" + }, + "outputs": [], + "source": [ + "def contact(name, job):\n", + " return 'For ' + job + ', contact ' + name + '.'\n", + "\n", + "contact_line = contact(job='naming', name='Job')\n", + "print(contact_line)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "OUSpe-hPl6-G" + }, + "outputs": [], + "source": [ + "def exclaim(name):\n", + " return name + '!'\n", + "\n", + "def request(name):\n", + " return name + '?'\n", + "\n", + "def tell(how):\n", + " return 'As my mother would say: ' + how('Julian')\n", + "\n", + "print(tell(request))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "uSN17qnvmoWv" + }, + "outputs": [], + "source": [ + "def make_enumerate(separator):\n", + " def enumerate(items):\n", + " if not len(items):\n", + " return ''\n", + " result = items[0]\n", + " for item in items[1:]:\n", + " result = result + separator + item\n", + " return result\n", + " return enumerate\n", + "\n", + "with_semicolon = make_enumerate('; ')\n", + "fruits = with_semicolon(['apricot', 'banana', 'cherry'])\n", + "print(fruits)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "CKma4p6Egkwb" + }, + "source": [ + "2. In each of the following code blocks, something is missing in the function definition. Add the missing element." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "SaygnjMygwNF" + }, + "outputs": [], + "source": [ + "def combine(left, right):\n", + " \"\"\" Compute the sum and the product of the arguments. \"\"\"\n", + " sum = left + right\n", + " product = left * right\n", + " return product\n", + "\n", + "assert combine(2, 3) == (5, 6)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "GqIgTcG6hMuG" + }, + "outputs": [], + "source": [ + "def announce_time(hour, minute):\n", + " \"\"\" Announce the time in a speaking clock manner. \"\"\"\n", + " if minute < 10:\n", + " minute = '0' + str(minute)\n", + " time = str(hour) + ':' + str(minute)\n", + " return 'At the next beep, the time will be ' + time + '. BEEP!'\n", + "\n", + "assert announce_time(12) == 'At the next beep, the time will be 12:00. BEEP!'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "nH77gJdOkS5b" + }, + "outputs": [], + "source": [ + "def echo(value):\n", + " print(value)\n", + " return value\n", + "\n", + "assert echo('holiday') == 'holiday'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "YG5XrO1PoIJx" + }, + "source": [ + "3. In the following code block, write a function `month`, which returns a string name for given numeric month. Month `1` is `'January'` and month `12` is `December`. When no argument is passed, it should default to the current month. **Tip:** you can avoid writing a big `if`/`elif`/`else` tree by using a list of month names and using the month number as a list index." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "TnuU_I0Tq9wQ" + }, + "outputs": [], + "source": [ + "# You may pretend that it is forever April\n", + "current_month = 4\n", + "\n", + "# Your definition of month here\n", + "\n", + "assert month(3) == 'March'\n", + "assert month(4) == 'April'\n", + "assert month(11) == 'November'\n", + "assert month() == 'April'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "WuRrElhUsD40" + }, + "source": [ + "4. In the following code block, write a function `weekday`, which is analogous to `month` in the previous exercise. Day `1` is `'Monday'` while day `0` and day `7` are both `'Sunday'`. Can you avoid writing the string `'Sunday'` twice in your code?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "WUGQqmJysrqS" + }, + "outputs": [], + "source": [ + "# You may pretend it is forever Tuesday\n", + "current_weekday = 2\n", + "\n", + "# Your definition of weekday here\n", + "\n", + "assert weekday() == 'Tuesday'\n", + "assert weekday(0) == 'Sunday'\n", + "assert weekday(7) == 'Sunday'\n", + "assert weekday(4) == 'Thursday'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ZvfEq3NctoOo" + }, + "source": [ + "## Exercise 7.4: bonus" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8au2fNRutw8i" + }, + "source": [ + "1. In exercises 7.3.4 and 7.3.5, where you just implemented `month` and `weekday`, your code was likely very similar. How could you implement both functions without repeating yourself? Check your solution with assertions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "JFhOX_Z5uVfC" + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Gx54DrgJuWKg" + }, + "source": [ + "2. Write a function `create_date_format`, which takes two arguments that determine how a date should be displayed. The first argument is function that transforms a weekday number to a string suitable for display, the second argument is a function that transforms a month number. Both arguments default to just displaying the number as-is. `create_date_format` returns a new function. The latter function takes any date defined by a year, month, day and weekday, and returns a string suitable for displaying that date as a whole." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "0KyXFHEWwZ45" + }, + "outputs": [], + "source": [ + "# For this code block, you need to have `month` and `weekday`\n", + "# in your runtime from previous exercises.\n", + "\n", + "# Your definition of create_date_format here\n", + "\n", + "wordy_format = create_date_format(weekday, month)\n", + "cryptic_format = create_date_format()\n", + "\n", + "assert wordy_format(2023, 4, 11, 2) == 'Tuesday, April 11, 2023'\n", + "assert cryptic_format(2023, 4, 11, 2) == '2, 4 11, 2023'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "b-p6Ct5_0I-x" + }, + "source": [ + "## Exercise 7.5: ultimate FizzBuzz (more bonus)\n", + "\n", + "1. Write two functions `fizz(number)` and `buzz(number)`. If `number` is divisible by 3, `fizz` should return the string `'Fizz'`; otherwise, it should return the empty string `''`. `buzz` should work like `fizz`, but for divisibility by 5 and the string `'Buzz'`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "407dPPK966R9" + }, + "outputs": [], + "source": [ + "# Your solution here\n", + "\n", + "for number in [1, 2, 4, 5, 7, 8, 10, 11, 13, 14]:\n", + " assert fizz(number) == '', str(number) + ' is not divisible by 3'\n", + "for number in [3, 6, 9, 12, 15, 18, 21, 24, 27]:\n", + " assert fizz(number) == 'Fizz', str(number) + ' is divisible by 3'\n", + "for number in [1, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14]:\n", + " assert buzz(number) == '', str(number) + ' is not divisible by 5'\n", + "for number in [5, 10, 15, 20, 25, 30, 35, 40]:\n", + " assert buzz(number) == 'Buzz', str(number) + ' is divisible by 5'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "QCKc52-r9DrX" + }, + "source": [ + "2. Using your functions `fizz` and `buzz` from step 1, write the function `fizzbuzz(number)`, which implements the well-known rules from before, but with a slight **twist**:\n", + " - If the number is divisible by 3 and 5, return `'FizzBuzz'`.\n", + " - If the number is divisible by 3 but not by 5, return `'Fizz'`.\n", + " - If the number is divisible by 5 but not by 3, return `'Buzz'`.\n", + " - In all other cases, return the number itself **as a string**." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "NFADyIW3-7qt" + }, + "outputs": [], + "source": [ + "# Your solution here\n", + "\n", + "for number in [1, 2, 4, 7, 8, 11, 13, 14]:\n", + " assert fizzbuzz(number) == str(number), str(number)\n", + "for number in [3, 6, 9, 12, 18, 21, 24, 27]:\n", + " assert fizzbuzz(number) == 'Fizz', str(number)\n", + "for number in [5, 10, 20, 25, 35, 40]:\n", + " assert fizzbuzz(number) == 'Buzz', str(number)\n", + "for number in [15, 30, 45, 60, 75]:\n", + " assert fizzbuzz(number) == 'FizzBuzz', str(number)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "o_3wq4agCCZH" + }, + "source": [ + "3. Write a function `chunk10(sequence)` that takes an iterable `sequence` and returns a list of lists: the first sublist contains the first ten elements of `sequence`, the second sublist contains the next ten elements of `sequence`, and so on. You may assume that the number of values in `sequence` is an exact multiple of 10." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "4c0A4kMfDdvt" + }, + "outputs": [], + "source": [ + "# Your solution here\n", + "\n", + "assert chunk10('Sheean!!!!!') == [list('Sheean!!!!!')]\n", + "assert chunk10('Hey Julian, let us have lunch!') == [\n", + " list('Hey Julian'),\n", + " list(', let us h'),\n", + " list('ave lunch!'),\n", + "]\n", + "assert chunk10(range(20)) == [list(range(10)), list(range(10, 20))]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HBA4z4yVIhsn" + }, + "source": [ + "4. In step 1, you may have written definitions for `fizz` and `buzz` that look very similar to each other. Can you think of a way to avoid repeating the common elements?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "5g4BRdpLJLIc" + }, + "outputs": [], + "source": [ + "# Your solution here\n", + "\n", + "for number in [1, 2, 4, 5, 7, 8, 10, 11, 13, 14]:\n", + " assert fizz(number) == '', str(number) + ' is not divisible by 3'\n", + "for number in [3, 6, 9, 12, 15, 18, 21, 24, 27]:\n", + " assert fizz(number) == 'Fizz', str(number) + ' is divisible by 3'\n", + "for number in [1, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14]:\n", + " assert buzz(number) == '', str(number) + ' is not divisible by 5'\n", + "for number in [5, 10, 15, 20, 25, 30, 35, 40]:\n", + " assert buzz(number) == 'Buzz', str(number) + ' is divisible by 5'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BXzFYNGmPeO6" + }, + "source": [ + "5. In exercise 7.1.3, you documented the function `join_commas`. We did not actually need to write that function, because it is built into Python, although it goes by the name `', '.join`. That notation also works with other strings, as we demonstrate below. We also remind you of `map`, which appeared in the lecture.

\n", + "Using these functions, as well as your own `fizzbuzz` from step 2 and your own `chunk10` from step 3, try to recreate (roughly) the same `FizzBuzz` output for the numbers 1 through 100 as in [exercise 6.2.4](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV#scrollTo=uyqbuhKsUlhG), in as little code as you can." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "nDGUS26IMApD" + }, + "outputs": [], + "source": [ + "# The following code is just for illustration.\n", + "# You do not need it in your solution.\n", + "fruits = ['apricot', 'banana', 'cherry']\n", + "print(', '.join(fruits))\n", + "print('\\n'.join(fruits))\n", + "numbers = list(range(10))\n", + "print(numbers)\n", + "strings = map(str, numbers)\n", + "print(list(strings))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Dntbbioh29xm" + }, + "source": [ + "## Next module\n", + "\n", + "[8. Debugging](https://colab.research.google.com/drive/1yQskT6SyKvXtXewx5kCla2NOmasgP8Vi)" + ] + } + ], + "metadata": { + "colab": { + "provenance": [], + "toc_visible": true + }, + "jupytext": { + "main_language": "python" + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/lessons/07 Functions.py b/lessons/07 Functions.py new file mode 100644 index 0000000..bc261b6 --- /dev/null +++ b/lessons/07 Functions.py @@ -0,0 +1,818 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# name: python3 +# --- + +# %% [markdown] id="fqMJHzNk5yXQ" +# # Module 7: Functions +# +# ### CDH course "Programming in Python" +# +# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# +# Previous module: [6. Loops](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV) +# +# ### This module +# +# - Storing code in a variable so you can reuse it. +# - Being explicit about the purpose of your code. + +# %% [markdown] id="mnc8HshNPLte" +# ## (Re)Introducing functions +# +# A function is a reusable piece of code, stored in a variable. You *call* a function by passing it a list of *arguments* between parentheses. The function *returns* a value, which you can then use in an expression or store in another variable. We have already seen several functions: + +# %% id="IP2mOWYgR-QA" +result = ord('🎵') +print(type(result), result) + +result = len([1, 2, 3]) +print(type(result), result) + +result = str(ord) +print(type(result), result) + +# %% [markdown] id="u_YUDiccZKwh" +# The fact that functions are variables, is illustrated by the fact that you can store them in variables and even reassign them: + +# %% id="Mct6Hy3HZa5H" +my_ord = ord +print(my_ord('a')) +ord = chr +print(ord(97)) + +# don't do this with 'list'!!!!! + +# back to sanity! +ord = my_ord + +# this can also restore sanity +# %reset_selective ord + +# reset resets everything +# %reset + +ord('a') + +# %% [markdown] id="hBgqVL7sSyFx" +# The list of arguments may be empty. For example, if you pass no arguments to `list`, it returns an empty list: + +# %% id="wb98z1kBS9tm" +result = list() +print(result) + +# %% [markdown] id="Byutz190TEH-" +# You can also pass multiple arguments (depending on the function). + +# %% id="N7YW9_X_UOuB" +result = max(3, 4, 5) +print(result) + +# %% [markdown] id="H0SocqBbXQ6f" +# A *named argument* or *keyword argument*, prefixed with `name=`, makes the purpose of the argument explicit. Below, the argument with the name `end` has the value `' '`. + +# %% id="uTk4InjdXg5g" +print('one', end=' ') +print('two') + +# %% [markdown] id="dCUHGgepctcp" +# Arguments *without* a name are called *positional* arguments, because their roles are determined by the order in which they are supplied. +# +# In our example of `max(3, 4, 5)` above, `3`, `4`, and `5` are positional arguments. In this case, the order of the numbers does not actually matter (the maximum is the same, no matter what). +# +# However, for most functions, this is not the case! An example you have already seen is `range`: the first argument is the _start_ and the second argument is the _stop_. + +# %% id="yCyPbsvAd5PB" +assert max(3, 4, 5) is max(4, 3, 5) + +assert range(1, 5) is not range(5, 1) + +# %% [markdown] id="ECGJttnRUX_J" +# A function may return "nothing", i.e., `None`. + +# %% id="gAMO-QD4Uuxe" +result = print('hello') +print(result) + +# %% [markdown] id="Bj4diwbcU1Q7" +# A function may even return multiple results, i.e., a tuple. We can *unpack* those directly into multiple variables, but we can also store the whole tuple in a single variable. + +# %% id="yYRQIGnoVHWA" +my_tuple = tuple(['Jelte', 'Julian']) +result1, result2 = my_tuple +print(my_tuple) +print(result1) +print(result2) + +# %% [markdown] id="xMHYhvPhVXet" +# Some functions have *side effects*: apart from taking arguments and returning results, they also have some other external effect. + +# %% id="OKEha-rwVo99" +result1 = input('Please enter PIN: ') # returns input from user +result2 = print(result1) # returns nothing +print(result2) +print(result1) + +# %% [markdown] id="emAs76qOagVF" +# ## Functions are awesome! +# +# The following code, which does *not* use enough functions, is **bad**: + +# %% id="n0UOjklcbEHQ" +print('"banana" has 6 characters and starts with a lowercase') +print('"Ada" has 3 characters and starts with an uppercase') +print('"epigraph" has 8 characters and starts with a lowercase') + +# %% [markdown] id="vAt_0PfecAGK" +# Problems: +# +# - repetitive +# - not explicit +# - unmaintainable +# +# To some extent, we can address these problems with a loop: + +# %% id="MeUnr3KNcaVZ" +words = ['banana', 'Ada', 'epigraph'] + +for word in words: + quoted = '"' + word + '"' + length = len(word) + first_character = word[0] + if 'A' <= first_character <= 'Z': + initial = 'an uppercase' + else: + initial = 'a lowercase' + print(quoted, 'has', length, 'characters and starts with', initial) + + +# %% [markdown] id="FL0w8VMkeLm7" +# BUT +# +# - still not explicit +# - still repetitive, need to repeat the loop if I want to do this elsewhere + +# %% [markdown] id="bBiz8La8jv81" +# Functions let us solve all problems! + +# %% id="HOjpPJBNiSvr" +def describe(word): + quoted = '"' + word + '"' + length = str(len(word)) + first_character = word[0] + if 'A' <= first_character <= 'Z': + initial = 'an uppercase' + else: + initial = 'a lowercase' + return quoted + ' has ' + length + ' characters and starts with ' + initial + +words = ['banana', 'Ada', 'epigraph'] + +for word in words: + print(describe(word)) + + +# %% [markdown] id="y77LwKKjk-2D" +# ## Basic function definitions +# +# At a minimum, you need the keyword `def`, a name, a pair of parentheses, a colon, and an indented block. The indented block is called the *function body*. It can contain anything! + +# %% id="Ph3RsaBbllvB" +# Defining the function with the name "example" +def example(): + pass # ah, that placeholder again + +# Calling the function with the name "example" +result = example() +print(result) + + +# %% [markdown] id="6Esxqtn0y1Ai" +# Keep in mind that a function *definition* only stores the code of the function body inside a variable with the name of the function. The code **does not run** until you *call* the function. + +# %% id="vBfiX0-WzWQg" +def example(): + # Don't call print inside a function! + # We only do this here for demonstration purposes. + print('Hello, world!') + +print(type(example), example) + + +# %% [markdown] id="S3cY2mRgl9z8" +# Between the parentheses, we list the *parameters*, the "slots" that can be filled by passing arguments when calling the function. +# +# The parameters (and any new variables that you make within the function body) have limited _scope_: they only live within the body of the function. + +# %% id="fWAzMGqfnC8p" +def example(name, age): + print(name) + print(age) + +name = 'Sheean' +age = 36 + +print(name) +print(age) + +result = example('Julian', 35) +print(result) + +print(name) +print(age) + + +# %% [markdown] id="J5Pd_TjZqnwj" +# At the end of the function body, a function will implicitly return `None`. The `return` keyword lets you return a different value, return before the end of the body, or both. You can use the keyword multiple times, but **a function can return only once**, so an early return will skip the remainder of the function body. + +# %% id="48ARWWmvrgjz" +def heaviness(item, weight): + if type(weight) not in (int, float): + # early return, implicit None + return + if weight >= 5: + # early return, different value + return 'heavy' + # final return, different value + return 'light' + +result = heaviness('letter', 'paper weight') +print(result) + +result = heaviness('feather', 0.01) +print(result) + +result = heaviness('bowling ball', 8) +print(result) + + +# %% [markdown] id="kOlF8tonzwZr" +# Functions should **not** have side effects unless that is the very purpose of the function (like with `print` and `input`). Most functions should return a value rather than printing something to the screen! + +# %% id="EHX1V-E71EkI" +# GOOD: function returns a value +def exclaim(text): + return text + '!' + +value = exclaim('Julian') +print(value) + +# BAD: function prints to the screen +def exclaim(text): + print(text + '!') + +value = exclaim('Julian') +print(value) + +# OK: function is intended as a special print wrapper +def fancy_print(text): + print('>>>', text, '<<<') + +fancy_print('Julian') + + +# %% [markdown] id="spLYBkqtvDmF" +# It is a good habit to start a function with a *documentation string*, **docstring** for short. A docstring should explain the parameters and possible return values of the function, as well as any rules or side effects that apply. +# +# A docstring should start with a short description of the function that fits on a single line. If there is more to explain, add the rest after a blank line. +# +# By convention, docstrings use three double quotes. + +# %% id="DR6t3PCCvSEX" +def heaviness(item, weight): + """ + Given the name of an item and its weight in kg, classify its heaviness. + + If the weight is not a number, it cannot be classified. Otherwise, the + classification is returned as a string: either 'light' or 'heavy'. + """ + if type(weight) not in (int, float): + return + if weight >= 5: + return 'heavy' + return 'light' + +print(heaviness.__doc__) + +print(len.__doc__) +print(ord.__doc__) +print(print.__doc__) + + +# %% [markdown] id="aPFGhEVz40JP" +# ## Exercise 7.1: functions + +# %% [markdown] id="hfcz-cSEKZWW" +# 1. We have seen several standard functions during the course, such as `ord` and `len`. List as many as you can from memory. Try to list the types of the parameters and return values of each function as well. Do any of these functions have side effects? + +# %% [markdown] id="BUnMsiUzKbws" +# 2. For exercise purposes, code in the following blocks may call `print` inside a function and omit a docstring. Predict the output, then run the code to see whether your prediction was right. Try to explain any discrepancies. + +# %% id="_3va9jT5O0H7" +def greet(name): + return 'Hello, ' + name + '!' + +print(greet('Berit')) + +# %% id="vdTIwoGxM_JV" +name = 'Luka' + +def exclaim(name): + print(name + '!') + + +# %% id="30fv8SAMOblV" +def false(): + return True + +print(False) + +# %% id="ongrNaZFNNmV" +length = 5 +width = 2 + +def calculate_area(): + area = length * width + +print(calculate_area()) + + +# %% id="MSkOCMMyNoUO" +def question(name): + return 'Who is ' + name + '?' + +question('Sheean') + + +# %% id="72DDRhD5OQ0g" +def add(left, right): + return left + right + +print(add('sweet', 'addition') * add(1, 1)) + +# %% id="L0GlJecf8ntf" +name = 'Julian' +age = 36 + +print(name) + +def example(name): + print(name) + name = 'Berit' + print(name) + print(age) + +print(name) +print(example) +print(name) +example('Jelte') +print(name) +print(age) + + +# %% [markdown] id="Rwvwlpp0-Hrt" +# 3. In each of the following code blocks, write a docstring for the function, then add assertions and run the block to check that your documentation is correct. + +# %% id="ajcRnvzQQ9c5" +def odd(number): + return number % 2 == 1 + + +# %% id="giU_bIKrRME4" +def magic(word): + if not word or type(word) != str: + return False + if 'A' <= word[0] <= 'Z': + return True + return False + + +# %% id="pAChbRWn-SKS" +def join_commas(words): + first, *rest = list(words) + text = first + for word in rest: + text = text + ', ' + word + return text + + +# %% [markdown] id="mc9RtAeATiHw" +# 4. Write a function `is_number` that takes one argument. If the argument is a number, it should return `True`; otherwise, it should return `False`. Make sure to include a docstring! + +# %% id="Ap33-rF-UbsB" +# Your definition of is_number here + +# The following lines will check your solution (no output is good) +assert is_number(0) +assert is_number(10) +assert is_number(0.5) +assert is_number(8 / 5) +assert not is_number(None) +assert not is_number('dear') +assert not is_number('123') + +# %% [markdown] id="apA7o120TYRl" +# ## Exercise 7.2: bonus + +# %% [markdown] id="nM43w3VlB3-O" +# 1. Write a function `last_a_index` that, given a string, returns the **last** index of the letter `'a'` in the string. If there is no `'a'` in the string, the function should return `None`. + +# %% id="I5s4_a53ENJC" +# Define last_a_index here + +assert last_a_index('banana') == 5 +assert last_a_index('cherry') == None +assert last_a_index('Once upon a time, there was a dragon') == 32 + +# %% [markdown] id="z4z3-dOaVROx" +# 2. Write a function `replace` with three string arguments: `text`, `query` and `replacement`. It should return a copy of `text` in which all occurrences of `query` are replaced by `replacement`. For example, `replace('small', 'a', 'e')` should return `'smell'`. You may assume that `query` is always a single character. For an additional challenge, you can try to also handle multi-character query strings. For ultimate challenge, try to think of a way to handle the empty string! + +# %% id="kyQwfz-mYvfW" +# Define replace here + +assert replace('small', 'a', 'e') == 'smell' +assert replace('banana', 'a', 'o') == 'bonono' +assert replace('cherry', 'a', 'x') == 'cherry' + + +# %% [markdown] id="3Lx61L5B0Zqe" +# ## Refining parameters and return values + +# %% [markdown] id="gRae5PaguhV9" +# ### Returning multiple values (in a tuple) + +# %% id="MJqHyAI31FLZ" +def word_statistics(word): + """ Given a string, return its length and the case of its first letter. """ + if 'A' <= word[0] <= 'Z': + casing = 'upper' + else: + casing = 'lower' + return len(word), casing + +size, initial = word_statistics('Sheean') +print(size) +print(initial) + + +# %% [markdown] id="1ORLASkU2Uga" +# ### Optional parameters +# +# Parameters can be made *optional* by using the notation `=None` in the parameter list. + +# %% id="IuDsXgrK3rK8" +def emphasize(word, emphasis=None): + """ + Given a string, return a version with an exclamation mark appended. + + The exclamation mark can be replaced by passing the override in a + second argument. + """ + if emphasis == None: + return word + '!' + return word + emphasis + +print(emphasize('Julian')) +print(emphasize('Julian', '!!!')) + + +# %% [markdown] id="2gPmuYUe1nyO" +# ### Parameters with default values +# +# The notation `=None` is actually a special case. You can use any value as a default for a parameter. However, it is easy to make mistakes in this way. In case of doubt, just use `None` and add a check inside the function body, as demonstrated above. + +# %% id="IqabfNzi2-XV" +# Literal strings, numbers and booleans are safe +def default_string(value='hello'): + return value + +def default_int(value=10): + return value + +def default_bool(value=True): + return value + +# Do not use variables as default values! +name = 'Julian' +def default_name(value=name): + return value + +name = 'Sheean' +print(default_name()) + +# Do not use data structures as default values! +def append_list(container=[], value=1): + container.append(value) + return container + +print(append_list()) +print(append_list()) + + +# %% [markdown] id="_mTHAoog6glc" +# ### Mixing positional and named arguments +# +# So far, we demonstrated functions where every parameter has its own unique name. With such parameters, the caller is free to choose between positional arguments and named arguments. The order of named arguments can be freely chosen, but they must come after all positional arguments. + +# %% id="ETGRDi8J7cyP" +def tax_rate(income, entrepreneur): + """ Determine tax rate, given income and whether payer is entrepreneur. """ + if entrepreneur: + return 20 + first_bracket = min(income, 50000) + second_bracket = income - first_bracket + return (first_bracket * 30 + second_bracket * 40) / income + +print(tax_rate(40000, True)) +print(tax_rate(40000, entrepreneur=True)) +print(tax_rate(income=40000, entrepreneur=True)) +print(tax_rate(entrepreneur=True, income=40000)) +print(tax_rate(True, income=40000)) +print(tax_rate(income=40000, True)) + + +# %% [markdown] id="1jWzKu5omWUV" +# ## Functions as first-class citizens +# +# As mentioned previously, a function is just a variable. That means you can do the same things with it as with other values. For example, you can pass a function as an argument to another function: + +# %% id="ukVglqcwsAdT" +def exclaim(text): + return text + '!' + +fruits = ['apricot', 'banana', 'cherry'] + +exclaimed_fruits = map(exclaim, fruits) + +print(type(exclaimed_fruits), exclaimed_fruits) + +list(exclaimed_fruits) + + +# %% [markdown] id="l4dAFklEvdhZ" +# What is going on inside `map`? Something like this: + +# %% id="V2-kY5O6xHRo" +def my_map(function, values): + """ Repeat `function` for every value in `values`. """ + for value in values: + result = function(value) + # there is an additional step that we will cover later + + +# %% [markdown] id="TZXA2q46taZi" +# Also, a function can define another function inside its body and then use and/or return it: + +# %% id="fG6-q8EatpRz" +def make_greeter(prefix, suffix): + """ Create a function that greets a person with the given phrasing. """ + def greeter(name): + """ Wrap the given name in a greeting. """ + return prefix + ', ' + name + suffix + return greeter + +greet_british = make_greeter('How do you do', '?') +greet_american = make_greeter('Hi there', '!') + +print(greet_british('Sheean')) +print(greet_british('Julian')) +print(greet_american('Sheean')) +print(greet_american('Julian')) + + +# %% [markdown] id="1xUD2ZNjcqEZ" +# ## Exercise 7.3: function tricks + +# %% [markdown] id="hxRC5Nx-cyM4" +# 1. In each of the code blocks below, predict the output, check your prediction by running the code, and try to explain any surprises. + +# %% id="HPxio0q6c-M9" +def welcome(name=None): + if name is None: + return 'Welcome to my office. How can I help you?' + return 'Please make yourself at home, ' + name + '!' + +print(welcome()) +print(welcome('Sheean')) + + +# %% id="NxXqK4ROdxva" +def table(number): + return number, number * 2, number * 3 + +first, second, third = table(5) + +print(third) + + +# %% id="EVCf3w9qfBCU" +def contact(name, job): + return 'For ' + job + ', contact ' + name + '.' + +contact_line = contact(job='naming', name='Job') +print(contact_line) + + +# %% id="OUSpe-hPl6-G" +def exclaim(name): + return name + '!' + +def request(name): + return name + '?' + +def tell(how): + return 'As my mother would say: ' + how('Julian') + +print(tell(request)) + + +# %% id="uSN17qnvmoWv" +def make_enumerate(separator): + def enumerate(items): + if not len(items): + return '' + result = items[0] + for item in items[1:]: + result = result + separator + item + return result + return enumerate + +with_semicolon = make_enumerate('; ') +fruits = with_semicolon(['apricot', 'banana', 'cherry']) +print(fruits) + + +# %% [markdown] id="CKma4p6Egkwb" +# 2. In each of the following code blocks, something is missing in the function definition. Add the missing element. + +# %% id="SaygnjMygwNF" +def combine(left, right): + """ Compute the sum and the product of the arguments. """ + sum = left + right + product = left * right + return product + +assert combine(2, 3) == (5, 6) + + +# %% id="GqIgTcG6hMuG" +def announce_time(hour, minute): + """ Announce the time in a speaking clock manner. """ + if minute < 10: + minute = '0' + str(minute) + time = str(hour) + ':' + str(minute) + return 'At the next beep, the time will be ' + time + '. BEEP!' + +assert announce_time(12) == 'At the next beep, the time will be 12:00. BEEP!' + + +# %% id="nH77gJdOkS5b" +def echo(value): + print(value) + return value + +assert echo('holiday') == 'holiday' + +# %% [markdown] id="YG5XrO1PoIJx" +# 3. In the following code block, write a function `month`, which returns a string name for given numeric month. Month `1` is `'January'` and month `12` is `December`. When no argument is passed, it should default to the current month. **Tip:** you can avoid writing a big `if`/`elif`/`else` tree by using a list of month names and using the month number as a list index. + +# %% id="TnuU_I0Tq9wQ" +# You may pretend that it is forever April +current_month = 4 + +# Your definition of month here + +assert month(3) == 'March' +assert month(4) == 'April' +assert month(11) == 'November' +assert month() == 'April' + +# %% [markdown] id="WuRrElhUsD40" +# 4. In the following code block, write a function `weekday`, which is analogous to `month` in the previous exercise. Day `1` is `'Monday'` while day `0` and day `7` are both `'Sunday'`. Can you avoid writing the string `'Sunday'` twice in your code? + +# %% id="WUGQqmJysrqS" +# You may pretend it is forever Tuesday +current_weekday = 2 + +# Your definition of weekday here + +assert weekday() == 'Tuesday' +assert weekday(0) == 'Sunday' +assert weekday(7) == 'Sunday' +assert weekday(4) == 'Thursday' + +# %% [markdown] id="ZvfEq3NctoOo" +# ## Exercise 7.4: bonus + +# %% [markdown] id="8au2fNRutw8i" +# 1. In exercises 7.3.4 and 7.3.5, where you just implemented `month` and `weekday`, your code was likely very similar. How could you implement both functions without repeating yourself? Check your solution with assertions. + +# %% id="JFhOX_Z5uVfC" + +# %% [markdown] id="Gx54DrgJuWKg" +# 2. Write a function `create_date_format`, which takes two arguments that determine how a date should be displayed. The first argument is function that transforms a weekday number to a string suitable for display, the second argument is a function that transforms a month number. Both arguments default to just displaying the number as-is. `create_date_format` returns a new function. The latter function takes any date defined by a year, month, day and weekday, and returns a string suitable for displaying that date as a whole. + +# %% id="0KyXFHEWwZ45" +# For this code block, you need to have `month` and `weekday` +# in your runtime from previous exercises. + +# Your definition of create_date_format here + +wordy_format = create_date_format(weekday, month) +cryptic_format = create_date_format() + +assert wordy_format(2023, 4, 11, 2) == 'Tuesday, April 11, 2023' +assert cryptic_format(2023, 4, 11, 2) == '2, 4 11, 2023' + +# %% [markdown] id="b-p6Ct5_0I-x" +# ## Exercise 7.5: ultimate FizzBuzz (more bonus) +# +# 1. Write two functions `fizz(number)` and `buzz(number)`. If `number` is divisible by 3, `fizz` should return the string `'Fizz'`; otherwise, it should return the empty string `''`. `buzz` should work like `fizz`, but for divisibility by 5 and the string `'Buzz'`. + +# %% id="407dPPK966R9" +# Your solution here + +for number in [1, 2, 4, 5, 7, 8, 10, 11, 13, 14]: + assert fizz(number) == '', str(number) + ' is not divisible by 3' +for number in [3, 6, 9, 12, 15, 18, 21, 24, 27]: + assert fizz(number) == 'Fizz', str(number) + ' is divisible by 3' +for number in [1, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14]: + assert buzz(number) == '', str(number) + ' is not divisible by 5' +for number in [5, 10, 15, 20, 25, 30, 35, 40]: + assert buzz(number) == 'Buzz', str(number) + ' is divisible by 5' + +# %% [markdown] id="QCKc52-r9DrX" +# 2. Using your functions `fizz` and `buzz` from step 1, write the function `fizzbuzz(number)`, which implements the well-known rules from before, but with a slight **twist**: +# - If the number is divisible by 3 and 5, return `'FizzBuzz'`. +# - If the number is divisible by 3 but not by 5, return `'Fizz'`. +# - If the number is divisible by 5 but not by 3, return `'Buzz'`. +# - In all other cases, return the number itself **as a string**. + +# %% id="NFADyIW3-7qt" +# Your solution here + +for number in [1, 2, 4, 7, 8, 11, 13, 14]: + assert fizzbuzz(number) == str(number), str(number) +for number in [3, 6, 9, 12, 18, 21, 24, 27]: + assert fizzbuzz(number) == 'Fizz', str(number) +for number in [5, 10, 20, 25, 35, 40]: + assert fizzbuzz(number) == 'Buzz', str(number) +for number in [15, 30, 45, 60, 75]: + assert fizzbuzz(number) == 'FizzBuzz', str(number) + +# %% [markdown] id="o_3wq4agCCZH" +# 3. Write a function `chunk10(sequence)` that takes an iterable `sequence` and returns a list of lists: the first sublist contains the first ten elements of `sequence`, the second sublist contains the next ten elements of `sequence`, and so on. You may assume that the number of values in `sequence` is an exact multiple of 10. + +# %% id="4c0A4kMfDdvt" +# Your solution here + +assert chunk10('Sheean!!!!!') == [list('Sheean!!!!!')] +assert chunk10('Hey Julian, let us have lunch!') == [ + list('Hey Julian'), + list(', let us h'), + list('ave lunch!'), +] +assert chunk10(range(20)) == [list(range(10)), list(range(10, 20))] + +# %% [markdown] id="HBA4z4yVIhsn" +# 4. In step 1, you may have written definitions for `fizz` and `buzz` that look very similar to each other. Can you think of a way to avoid repeating the common elements? + +# %% id="5g4BRdpLJLIc" +# Your solution here + +for number in [1, 2, 4, 5, 7, 8, 10, 11, 13, 14]: + assert fizz(number) == '', str(number) + ' is not divisible by 3' +for number in [3, 6, 9, 12, 15, 18, 21, 24, 27]: + assert fizz(number) == 'Fizz', str(number) + ' is divisible by 3' +for number in [1, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14]: + assert buzz(number) == '', str(number) + ' is not divisible by 5' +for number in [5, 10, 15, 20, 25, 30, 35, 40]: + assert buzz(number) == 'Buzz', str(number) + ' is divisible by 5' + +# %% [markdown] id="BXzFYNGmPeO6" +# 5. In exercise 7.1.3, you documented the function `join_commas`. We did not actually need to write that function, because it is built into Python, although it goes by the name `', '.join`. That notation also works with other strings, as we demonstrate below. We also remind you of `map`, which appeared in the lecture.

+# Using these functions, as well as your own `fizzbuzz` from step 2 and your own `chunk10` from step 3, try to recreate (roughly) the same `FizzBuzz` output for the numbers 1 through 100 as in [exercise 6.2.4](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV#scrollTo=uyqbuhKsUlhG), in as little code as you can. + +# %% id="nDGUS26IMApD" +# The following code is just for illustration. +# You do not need it in your solution. +fruits = ['apricot', 'banana', 'cherry'] +print(', '.join(fruits)) +print('\n'.join(fruits)) +numbers = list(range(10)) +print(numbers) +strings = map(str, numbers) +print(list(strings)) + +# %% [markdown] id="Dntbbioh29xm" +# ## Next module +# +# [8. Debugging](https://colab.research.google.com/drive/1yQskT6SyKvXtXewx5kCla2NOmasgP8Vi) diff --git a/lessons/07a Functions (extra exercises).ipynb b/lessons/07a Functions (extra exercises).ipynb index 241627f..78090a8 100644 --- a/lessons/07a Functions (extra exercises).ipynb +++ b/lessons/07a Functions (extra exercises).ipynb @@ -1 +1,788 @@ -{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"provenance":[],"authorship_tag":"ABX9TyMaLbZUxFMzVIjab/8j3smI"},"kernelspec":{"name":"python3","display_name":"Python 3"},"language_info":{"name":"python"}},"cells":[{"cell_type":"markdown","source":["# 7a - Functions (extra exercises)\n","\n","This notebook contains extra exercises to help you get more familiar with writing python code, and especially making functions. It contains no new theory.\n","\n","There are a lot of exercises, and they can be very similar. Don't feel like you have to do everything: if the exercises start to feel repetitive, you're ready to move on to the next section."],"metadata":{"id":"q2_7oxCuwonl"}},{"cell_type":"markdown","source":["## 1. Writing functions\n","\n","In these exercises, the instructions describe a function that you need to make. Each code block also has some assert statements that try to use your function, which you can use to verify your solution."],"metadata":{"id":"KoEzBrcFxk8T"}},{"cell_type":"markdown","source":["1.1: Write a function `first`, which returns the first item in a list. The function should take one argument (a list).\n","\n","If the list if empty, `first` should return `None`."],"metadata":{"id":"DxKNUR11yfvL"}},{"cell_type":"code","execution_count":null,"metadata":{"id":"YtJmU-K0woBC"},"outputs":[],"source":["# your code here...\n","\n","# check your function\n","assert first(['apple', 'banana', 'orange']) == 'apple'\n","assert first(['just one item']) == 'just one item'\n","assert first([]) == None"]},{"cell_type":"markdown","source":["1.2: Write a function `check_wallet` which takes a list of items as its argument. If the list includes `'wallet'`, it should return the list as-is. If it doesn't, it should add `'wallet'` to the end of list."],"metadata":{"id":"jgxg9ecn0GkZ"}},{"cell_type":"code","source":["# your code here...\n","\n","# check your function\n","assert check_wallet(['keys', 'wallet', 'phone', 'gloves']) == ['keys', 'wallet', 'phone', 'gloves']\n","assert check_wallet(['keys', 'phone']) == ['keys', 'phone', 'wallet']\n","assert check_wallet([]) == ['wallet']"],"metadata":{"id":"Z2Xa8C6z0BrI"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["1.3: Write a function `round_to_10` which takes a number as its argument and returns a new number: the input rounded to the nearest multiple of 10."],"metadata":{"id":"Zx9RsFoY2o5M"}},{"cell_type":"code","source":["# your code here...\n","\n","# check your function\n","assert round_to_10(8) == 10\n","assert round_to_10(22.1) == 20\n","assert round_to_10(40) == 40\n","assert round_to_10(55) == 60"],"metadata":{"id":"Y8M_CCuCcVZm"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["1.4: Let's make our rounding function a bit more generic. Make a new function `round_to_n` which takes two arguments: the number that you want to round, and the unit that you want to round to. `round_to_n(35, 50)` will round 35 to the nearest multiple of 50."],"metadata":{"id":"YKRA2X94dmfm"}},{"cell_type":"code","source":["# your code here\n","\n","\n","# check your function\n","assert round_to_n(8, 10) == 10\n","assert round_to_n(8, 20) == 0\n","assert round_to_n(24.3, 20) == 20"],"metadata":{"id":"CFukYZRFeMMr"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["1.5: Write a function `in_alphabet` that takes a character (a string of length 1) as a parameter. It should return `True` if the character is one of the 26 letters in the latin alphabet. Uppercase and lowercase characters should both return `True`. Otherwise, the function should return `False`."],"metadata":{"id":"9C8cDTS3e62p"}},{"cell_type":"code","source":["# your code here\n","\n","# check your function\n","assert in_alphabet('a') == True\n","assert in_alphabet('f') == True\n","assert in_alphabet('A') == True\n","assert in_alphabet('1') == False\n","assert in_alphabet('✨') == False"],"metadata":{"id":"X9kXxwe3fD7P"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["1.6: Use your function `in_alphabet` to write a new function `clean_text`. It should take one parameter, which is a string. It should return a new string, which is like the old one but leaves out any characters that are not in the latin alphabet or the space `' '`."],"metadata":{"id":"lXc43yNgicoA"}},{"cell_type":"code","source":["# your code here...\n","\n","# check your function\n","assert clean_text('Hello, how are you?') == 'Hello how are you'\n","assert clean_text('Hooray! Hooray! Hooray!') == 'Hooray Hooray Hooray'\n","assert clean_text('Yummy!! 😋') == 'Yummy '"],"metadata":{"id":"YOqKnf7rjpvW"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["1.7: Write a function `travel_time` that estimates the travel time for a journey. It takes 3 named arguments:\n","- `car`: the distance traversed by car\n","- `bike`: the distance traversed by bike\n","- `walk`: the distance traversed on foot\n","\n","Each of these will be a number (distance in km), and the default value is `0`. Calculate the travel time, assuming that:\n","\n","- Cars have an average speed of 80 km/h\n","- Bikes have an average speed of 16 km/h\n","- Walking has an average speed of 4 km/h\n","\n","Your function should return the travel time in hours."],"metadata":{"id":"mzKQAJxNLUgV"}},{"cell_type":"code","source":["# your code here...\n","\n","# check your function\n","assert travel_time(car=10, walk=0.1) == 0.15\n","assert travel_time(bike=10, walk=0.1) == 0.65\n","assert travel_time(car=55, bike=3, walk=0.3) == 0.95"],"metadata":{"id":"XDVlkfHyMIfE"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["1.8: Write a function `to_time` which takes a decimal number of hours (like the output of `travel time`) and converts it to hours, minutes, and seconds. For example, 1.5 hours is 1 hours, 30 minutes and 0 seconds.\n","\n","The input should be a single `float` or `int`, and the output should be three numbers: hours, minutes, and seconds. The output should be given in whole numbers (`int`, not `float`)."],"metadata":{"id":"SQHPePrYxdnG"}},{"cell_type":"code","source":["# your code here...\n","\n","# check your function\n","assert to_time(1.5) == (1, 30, 0)\n","assert to_time(3.6532) == (3, 39, 55)\n","assert to_time(0) == (0, 0, 0)"],"metadata":{"id":"5cdwImkNUGcc"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["1.9: Write a function `mean` which takes one argument, a list of numbers, as input. It should return the mean value of the list (a single number)."],"metadata":{"id":"shtLjnOGxen9"}},{"cell_type":"code","source":["# your code here...\n","\n","# check your function\n","assert mean([1, 2, 7 , 2]) == 3.0\n","assert mean([3.2, 4.5, 0.0, 2.5]) == 2.55\n","assert mean([0.0]) == 0.0"],"metadata":{"id":"xJ_XTebzxfCs"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["1.10: Write a function `is_number` that checks if a string describes a number. It takes one argument (a string).\n","\n","It should return `True` if:\n","- Every character in the string is either a digit (0-9) or a decimal point `'.'`)\n","- If there is a decimal point, it should not be more than one.\n","\n","Otherwise, the function should return `False`."],"metadata":{"id":"p1NF-Tbpxfs1"}},{"cell_type":"code","source":["# your code here...\n"," \n","# check your function.\n","assert is_number('12') == True\n","assert is_number('0.5') == True\n","assert is_number('two') == False\n","assert is_number('1.2.3') == False"],"metadata":{"id":"LsA_B5iPxgEE"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["## 2. Writing `assert` checks\n","\n","The code blocks below contain a function definition, but are missing some `assert` statements to check if everything works. Read the docstring and try to think of some assertions you could use to check the function.\n","\n","Not all of these functions work as intended! For some functions, you should be able to find assert statements that should succeed, but fail.\n","\n","You do not have to correct the functions (but you can if you want to)."],"metadata":{"id":"o55Hp9SomntG"}},{"cell_type":"code","source":["def distance(a, b):\n"," '''\n"," Gives the distance between two numbers.\n","\n"," Distance is absolute, i.e. always positive.\n"," '''\n","\n"," if a <= b:\n"," return b - a\n"," else:\n"," return a - b\n","\n","# write some checks!"],"metadata":{"id":"aqngxSucow39"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["def reverse(word):\n"," '''\n"," Reverses a string\n","\n"," Input must be a string. Output is the same string in reverse order.\n"," '''\n","\n"," new_word = ''\n","\n"," for character in word:\n"," new_word = character + new_word\n"," \n"," return new_word\n","\n","# write some checks!"],"metadata":{"id":"khwUSdMWn1CR"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["def exclude_short_words(words, min_length=5):\n"," '''\n"," Filters all short words (shorter than min_length) out of a list\n","\n"," Input should be a list of words (strings). Returns a new list\n"," with only the words longer than min_length.\n"," '''\n","\n"," long_words = []\n","\n"," for word in words:\n"," if len(word) < 5:\n"," long_words.append(word)\n"," \n"," return long_words\n","\n","# write some checks!"],"metadata":{"id":"nkpyPnDgoxKf"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["def every_third_item(items):\n"," '''\n"," Returns every third item in a list.\n","\n"," Input should be a list, the output is a new list. Always returns the first\n"," item - after that, returns every third item.\n"," '''\n","\n"," result = []\n","\n"," for index in range(0, len(items), 3):\n"," result.append(items[index])\n"," \n"," return result\n","\n","# write some checks!"],"metadata":{"id":"yQ_-_xnuoyZ9"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["def grade(score):\n"," '''\n"," Gives a letter grade (F, D, C, B, A) based on a score from 0-100.\n","\n"," Scores over 60 get grade D, scores over 70 get grade C, etc.\n"," '''\n"," if score >= 60:\n"," return 'D'\n"," elif score >= 70:\n"," return 'C'\n"," elif score >= 80:\n"," return 'B'\n"," elif score >= 90:\n"," return 'A'\n"," else:\n"," return 'F'\n","\n","# write some checks!"],"metadata":{"id":"ArwDSoguox2j"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["def is_prime(number):\n"," '''\n"," Checks if a int is a prime number.\n","\n"," Prime numbers are numbers that are NOT the product\n"," of two smaller numbers (2, 3, 5, 7, 11, are prime numbers)\n"," '''\n","\n"," for smaller_number in range(2, number):\n"," if number % smaller_number == 0:\n"," return False\n","\n"," return True \n","\n","# write some checks!"],"metadata":{"id":"hMIpfW_lMK0l"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["## 3. Writing functions with checks\n","\n","For the functions in section 1, we provided some assert statements, but when you write your own programs, you usually don't have those. Instead, you have _something you want to do_, and it is up to you to make your own `assert` checks.\n","\n","For the exercises below, we describe what your function should do. It is up to you to make both the function and the assert statements.\n","\n","For each exercise, consider the following:\n","\n","- What should the function be callled?\n","- What are the input parameters? What type should they be?\n","- What is the output? What type should the output be?\n","- What are some checks you could write?\n","- What should be in the body of the function? How does it work?"],"metadata":{"id":"aKgZVXZXoF3g"}},{"cell_type":"markdown","source":["3.1: Write a function that turns a sentence into a question. The input should be a string. If the last character in the string is a `.` or `!`, it should be replaced with a `?`. Otherwise, we just want to add `?` to the end of the string (without replacing anything)."],"metadata":{"id":"EXgk0_mjoukf"}},{"cell_type":"code","source":[],"metadata":{"id":"ExH64Sbgovw0"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["3.2: Write a function that takes a year as input and tells you whether or not that year is a leap year.\n","\n","From [wikipedia](https://en.wikipedia.org/wiki/Leap_year):\n","\n","> Every year that is exactly divisible by four is a leap year, except for years that are exactly divisible by 100, but these centurial years are leap years if they are exactly divisible by 400. For example, the years 1700, 1800, and 1900 are not leap years, but the years 1600 and 2000 are."],"metadata":{"id":"uA4PQxchxUrT"}},{"cell_type":"code","source":[],"metadata":{"id":"VIYiW1OCxVEo"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["3.3: Write a function to calculate the area of a circle based on the diameter.\n","\n","- The _area_ is equal the _radius_ of the circle, squared, multiplied by pi. In a formula: $A = r^2 \\times \\pi$. \n","- The radius of a circle is half its diameter.\n","- You can assume that pi is equal to 3.14"],"metadata":{"id":"QzewFsCJomVB"}},{"cell_type":"code","source":[],"metadata":{"id":"oCID8txRotb-"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["3.4: We want to calculate prices for an online store. Prices are calculated as follows:\n","- An order will include one or more items, each with their own price. You can just represent the order as a list of prices, e.g. `[15.00, 4.40, 31.50]`\n","- Customers may have a coupon that gives them 10% off all the items in their basket.\n","- If the total of the order (including the coupon discount) is under € 20.00, customers need to pay € 2.00 shipping.\n","\n","Write a function that calculates the total of an order."],"metadata":{"id":"SoO0C4pyxSjO"}},{"cell_type":"code","source":[],"metadata":{"id":"ddWh7EWCxS6M"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["3.5: Write a function that counts how often a string occurs within another string."],"metadata":{"id":"m1bBl0RvxXe-"}},{"cell_type":"code","source":[],"metadata":{"id":"GKRKIpKgxXwV"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["## 4. Don't repeat yourself\n","\n","An important use for functions is to avoid unnecessary repetition in code. When you write two pieces of very similar code, it's often convenient to make a single function.\n","\n","Each of the code blocks below contains two things:\n","- A section of code that has some repetition\n","- Some assert statements that check if the code works as intended.\n","\n","For each block:\n","- Try to understand what the code does, and identify the repetition.\n","- At the top of the code block, add a definition for a function (or several), that can handle the shared functionality.\n","- Use your functions(s) in the code, and check that everything still works."],"metadata":{"id":"NPXFJd95o3Ei"}},{"cell_type":"code","source":["small_pizza_price = 9.00\n","small_pizza_slices = 6\n","\n","small_pizza_price_per_slice = small_pizza_price / small_pizza_slices\n","print('The small pizza costs €', small_pizza_price_per_slice, 'per slice')\n","\n","medium_pizza_price = 12.00\n","medium_pizza_slices = 8\n","\n","medium_pizza_price_per_slice = medium_pizza_price / medium_pizza_slices\n","print('The medium pizza costs €', medium_pizza_price_per_slice, 'per slice')\n","\n","large_pizza_price = 16.00\n","large_pizza_slices = 8\n","\n","large_pizza_price_per_slice = large_pizza_price / large_pizza_slices\n","print('The large pizza costs €', large_pizza_price_per_slice, 'per slice')\n","\n","# check if everything works\n","assert small_pizza_price_per_slice == 1.5\n","assert medium_pizza_price_per_slice == 1.5\n","assert large_pizza_price_per_slice == 2.0"],"metadata":{"id":"JFc_E5sF5rt1"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["fruit_basket = ['apple', 'banana', 'banana', 'banana', 'apple', 'orange',\n"," 'orange', 'grape', 'grape', 'grape', 'grape', 'grape', 'grape',\n"," 'grape', 'grape', 'grape', 'pear', 'apple', 'strawberry',\n"," 'strawberry', 'strawberry', 'orange']\n","\n","apples = 0\n","for fruit in fruit_basket:\n"," if fruit == 'apple':\n"," apples += 1\n","\n","oranges = 0\n","for fruit in fruit_basket:\n"," if fruit == 'orange':\n"," oranges += 1\n","\n","apples_to_oranges = apples / oranges\n","\n","# check if everything works\n","assert apples_to_oranges == 1"],"metadata":{"id":"lirYtOcWpJ6m"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["weather_this_month = ['rain', 'rain', 'sun', 'sun', 'cloudy', 'cloudy', 'rain',\n"," 'cloudy', 'rain', 'rain', 'cloudy', 'sun', 'cloudy', 'sun', 'sun', 'sun',\n"," 'rain', 'cloudy', 'cloudy', 'sun', 'cloudy', 'rain', 'rain', 'cloudy',\n"," 'cloudy', 'cloudy', 'sun', 'cloudy', 'sun', 'sun']\n","\n","rain_followed_by_sun = 0\n","rain_followed_by_cloudy = 0\n","rain_followed_by_rain = 0\n","\n","for index, weather in enumerate(weather_this_month):\n"," not_last = index < len(weather_this_month) - 1\n"," if weather == 'rain' and not_last:\n"," weather_next_day = weather_this_month[index + 1]\n"," if weather_next_day == 'sun':\n"," rain_followed_by_sun += 1\n"," elif weather_next_day == 'cloudy':\n"," rain_followed_by_cloudy += 1\n"," else:\n"," rain_followed_by_rain += 1\n","\n","print('Days with rain were followed by:')\n","print('Sun:', rain_followed_by_sun, 'times')\n","print('Clouds:', rain_followed_by_cloudy, 'times')\n","print('Rain:', rain_followed_by_rain, 'times')\n","print()\n","\n","sun_followed_by_sun = 0\n","sun_followed_by_cloudy = 0\n","sun_followed_by_rain = 0\n","\n","for index, weather in enumerate(weather_this_month):\n"," not_last = index < len(weather_this_month) - 1\n"," if weather == 'sun' and not_last:\n"," weather_next_day = weather_this_month[index + 1]\n"," if weather_next_day == 'sun':\n"," sun_followed_by_sun += 1\n"," elif weather_next_day == 'cloudy':\n"," sun_followed_by_cloudy += 1\n"," else:\n"," sun_followed_by_rain += 1\n","\n","print('Days with sun were followed by:')\n","print('Sun:', sun_followed_by_sun, 'times')\n","print('Clouds:', sun_followed_by_cloudy, 'times')\n","print('Rain:', sun_followed_by_rain, 'times')\n","print()\n","\n","sun_preceded_by_sun = 0\n","sun_preceded_by_cloudy = 0\n","sun_preceded_by_rain = 0\n","\n","\n","for index, weather in enumerate(weather_this_month):\n"," not_first = index > 0\n"," if weather == 'sun' and not_first:\n"," weather_prev_day = weather_this_month[index - 1]\n"," if weather_prev_day == 'sun':\n"," sun_preceded_by_sun += 1\n"," elif weather_prev_day == 'cloudy':\n"," sun_preceded_by_cloudy += 1\n"," else:\n"," sun_preceded_by_rain += 1\n","\n","print('Days with sun were preceded by:')\n","print('Sun:', sun_preceded_by_sun, 'times')\n","print('Clouds:', sun_preceded_by_cloudy, 'times')\n","print('Rain:', sun_preceded_by_rain, 'times')\n","print()\n","\n","# check if everything works\n","assert rain_followed_by_sun == 1\n","assert rain_followed_by_cloudy == 4\n","assert rain_followed_by_rain\n","\n","assert sun_followed_by_sun == 4\n","assert sun_followed_by_cloudy == 4\n","assert sun_followed_by_rain == 1\n","\n","assert sun_preceded_by_sun == 4\n","assert sun_preceded_by_cloudy == 5\n","assert sun_preceded_by_rain == 1\n"],"metadata":{"id":"mdNug4ct5645"},"execution_count":null,"outputs":[]}]} \ No newline at end of file +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "q2_7oxCuwonl" + }, + "source": [ + "# 7a - Functions (extra exercises)\n", + "\n", + "This notebook contains extra exercises to help you get more familiar with writing python code, and especially making functions. It contains no new theory.\n", + "\n", + "There are a lot of exercises, and they can be very similar. Don't feel like you have to do everything: if the exercises start to feel repetitive, you're ready to move on to the next section." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "KoEzBrcFxk8T" + }, + "source": [ + "## 1. Writing functions\n", + "\n", + "In these exercises, the instructions describe a function that you need to make. Each code block also has some assert statements that try to use your function, which you can use to verify your solution." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "DxKNUR11yfvL" + }, + "source": [ + "1.1: Write a function `first`, which returns the first item in a list. The function should take one argument (a list).\n", + "\n", + "If the list if empty, `first` should return `None`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "YtJmU-K0woBC" + }, + "outputs": [], + "source": [ + "# your code here...\n", + "\n", + "# check your function\n", + "assert first(['apple', 'banana', 'orange']) == 'apple'\n", + "assert first(['just one item']) == 'just one item'\n", + "assert first([]) == None" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "jgxg9ecn0GkZ" + }, + "source": [ + "1.2: Write a function `check_wallet` which takes a list of items as its argument. If the list includes `'wallet'`, it should return the list as-is. If it doesn't, it should add `'wallet'` to the end of list." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Z2Xa8C6z0BrI" + }, + "outputs": [], + "source": [ + "# your code here...\n", + "\n", + "# check your function\n", + "assert check_wallet(['keys', 'wallet', 'phone', 'gloves']) == ['keys', 'wallet', 'phone', 'gloves']\n", + "assert check_wallet(['keys', 'phone']) == ['keys', 'phone', 'wallet']\n", + "assert check_wallet([]) == ['wallet']" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Zx9RsFoY2o5M" + }, + "source": [ + "1.3: Write a function `round_to_10` which takes a number as its argument and returns a new number: the input rounded to the nearest multiple of 10." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Y8M_CCuCcVZm" + }, + "outputs": [], + "source": [ + "# your code here...\n", + "\n", + "# check your function\n", + "assert round_to_10(8) == 10\n", + "assert round_to_10(22.1) == 20\n", + "assert round_to_10(40) == 40\n", + "assert round_to_10(55) == 60" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "YKRA2X94dmfm" + }, + "source": [ + "1.4: Let's make our rounding function a bit more generic. Make a new function `round_to_n` which takes two arguments: the number that you want to round, and the unit that you want to round to. `round_to_n(35, 50)` will round 35 to the nearest multiple of 50." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "CFukYZRFeMMr" + }, + "outputs": [], + "source": [ + "# your code here\n", + "\n", + "\n", + "# check your function\n", + "assert round_to_n(8, 10) == 10\n", + "assert round_to_n(8, 20) == 0\n", + "assert round_to_n(24.3, 20) == 20" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9C8cDTS3e62p" + }, + "source": [ + "1.5: Write a function `in_alphabet` that takes a character (a string of length 1) as a parameter. It should return `True` if the character is one of the 26 letters in the latin alphabet. Uppercase and lowercase characters should both return `True`. Otherwise, the function should return `False`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "X9kXxwe3fD7P" + }, + "outputs": [], + "source": [ + "# your code here\n", + "\n", + "# check your function\n", + "assert in_alphabet('a') == True\n", + "assert in_alphabet('f') == True\n", + "assert in_alphabet('A') == True\n", + "assert in_alphabet('1') == False\n", + "assert in_alphabet('✨') == False" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "lXc43yNgicoA" + }, + "source": [ + "1.6: Use your function `in_alphabet` to write a new function `clean_text`. It should take one parameter, which is a string. It should return a new string, which is like the old one but leaves out any characters that are not in the latin alphabet or the space `' '`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "YOqKnf7rjpvW" + }, + "outputs": [], + "source": [ + "# your code here...\n", + "\n", + "# check your function\n", + "assert clean_text('Hello, how are you?') == 'Hello how are you'\n", + "assert clean_text('Hooray! Hooray! Hooray!') == 'Hooray Hooray Hooray'\n", + "assert clean_text('Yummy!! 😋') == 'Yummy '" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "mzKQAJxNLUgV" + }, + "source": [ + "1.7: Write a function `travel_time` that estimates the travel time for a journey. It takes 3 named arguments:\n", + "- `car`: the distance traversed by car\n", + "- `bike`: the distance traversed by bike\n", + "- `walk`: the distance traversed on foot\n", + "\n", + "Each of these will be a number (distance in km), and the default value is `0`. Calculate the travel time, assuming that:\n", + "\n", + "- Cars have an average speed of 80 km/h\n", + "- Bikes have an average speed of 16 km/h\n", + "- Walking has an average speed of 4 km/h\n", + "\n", + "Your function should return the travel time in hours." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "XDVlkfHyMIfE" + }, + "outputs": [], + "source": [ + "# your code here...\n", + "\n", + "# check your function\n", + "assert travel_time(car=10, walk=0.1) == 0.15\n", + "assert travel_time(bike=10, walk=0.1) == 0.65\n", + "assert travel_time(car=55, bike=3, walk=0.3) == 0.95" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SQHPePrYxdnG" + }, + "source": [ + "1.8: Write a function `to_time` which takes a decimal number of hours (like the output of `travel time`) and converts it to hours, minutes, and seconds. For example, 1.5 hours is 1 hours, 30 minutes and 0 seconds.\n", + "\n", + "The input should be a single `float` or `int`, and the output should be three numbers: hours, minutes, and seconds. The output should be given in whole numbers (`int`, not `float`)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "5cdwImkNUGcc" + }, + "outputs": [], + "source": [ + "# your code here...\n", + "\n", + "# check your function\n", + "assert to_time(1.5) == (1, 30, 0)\n", + "assert to_time(3.6532) == (3, 39, 55)\n", + "assert to_time(0) == (0, 0, 0)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "shtLjnOGxen9" + }, + "source": [ + "1.9: Write a function `mean` which takes one argument, a list of numbers, as input. It should return the mean value of the list (a single number)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "xJ_XTebzxfCs" + }, + "outputs": [], + "source": [ + "# your code here...\n", + "\n", + "# check your function\n", + "assert mean([1, 2, 7 , 2]) == 3.0\n", + "assert mean([3.2, 4.5, 0.0, 2.5]) == 2.55\n", + "assert mean([0.0]) == 0.0" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "p1NF-Tbpxfs1" + }, + "source": [ + "1.10: Write a function `is_number` that checks if a string describes a number. It takes one argument (a string).\n", + "\n", + "It should return `True` if:\n", + "- Every character in the string is either a digit (0-9) or a decimal point `'.'`)\n", + "- If there is a decimal point, it should not be more than one.\n", + "\n", + "Otherwise, the function should return `False`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "LsA_B5iPxgEE" + }, + "outputs": [], + "source": [ + "# your code here...\n", + " \n", + "# check your function.\n", + "assert is_number('12') == True\n", + "assert is_number('0.5') == True\n", + "assert is_number('two') == False\n", + "assert is_number('1.2.3') == False" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "o55Hp9SomntG" + }, + "source": [ + "## 2. Writing `assert` checks\n", + "\n", + "The code blocks below contain a function definition, but are missing some `assert` statements to check if everything works. Read the docstring and try to think of some assertions you could use to check the function.\n", + "\n", + "Not all of these functions work as intended! For some functions, you should be able to find assert statements that should succeed, but fail.\n", + "\n", + "You do not have to correct the functions (but you can if you want to)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "aqngxSucow39" + }, + "outputs": [], + "source": [ + "def distance(a, b):\n", + " '''\n", + " Gives the distance between two numbers.\n", + "\n", + " Distance is absolute, i.e. always positive.\n", + " '''\n", + "\n", + " if a <= b:\n", + " return b - a\n", + " else:\n", + " return a - b\n", + "\n", + "# write some checks!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "khwUSdMWn1CR" + }, + "outputs": [], + "source": [ + "def reverse(word):\n", + " '''\n", + " Reverses a string\n", + "\n", + " Input must be a string. Output is the same string in reverse order.\n", + " '''\n", + "\n", + " new_word = ''\n", + "\n", + " for character in word:\n", + " new_word = character + new_word\n", + " \n", + " return new_word\n", + "\n", + "# write some checks!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "nkpyPnDgoxKf" + }, + "outputs": [], + "source": [ + "def exclude_short_words(words, min_length=5):\n", + " '''\n", + " Filters all short words (shorter than min_length) out of a list\n", + "\n", + " Input should be a list of words (strings). Returns a new list\n", + " with only the words longer than min_length.\n", + " '''\n", + "\n", + " long_words = []\n", + "\n", + " for word in words:\n", + " if len(word) < 5:\n", + " long_words.append(word)\n", + " \n", + " return long_words\n", + "\n", + "# write some checks!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "yQ_-_xnuoyZ9" + }, + "outputs": [], + "source": [ + "def every_third_item(items):\n", + " '''\n", + " Returns every third item in a list.\n", + "\n", + " Input should be a list, the output is a new list. Always returns the first\n", + " item - after that, returns every third item.\n", + " '''\n", + "\n", + " result = []\n", + "\n", + " for index in range(0, len(items), 3):\n", + " result.append(items[index])\n", + " \n", + " return result\n", + "\n", + "# write some checks!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ArwDSoguox2j" + }, + "outputs": [], + "source": [ + "def grade(score):\n", + " '''\n", + " Gives a letter grade (F, D, C, B, A) based on a score from 0-100.\n", + "\n", + " Scores over 60 get grade D, scores over 70 get grade C, etc.\n", + " '''\n", + " if score >= 60:\n", + " return 'D'\n", + " elif score >= 70:\n", + " return 'C'\n", + " elif score >= 80:\n", + " return 'B'\n", + " elif score >= 90:\n", + " return 'A'\n", + " else:\n", + " return 'F'\n", + "\n", + "# write some checks!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "hMIpfW_lMK0l" + }, + "outputs": [], + "source": [ + "def is_prime(number):\n", + " '''\n", + " Checks if a int is a prime number.\n", + "\n", + " Prime numbers are numbers that are NOT the product\n", + " of two smaller numbers (2, 3, 5, 7, 11, are prime numbers)\n", + " '''\n", + "\n", + " for smaller_number in range(2, number):\n", + " if number % smaller_number == 0:\n", + " return False\n", + "\n", + " return True \n", + "\n", + "# write some checks!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "aKgZVXZXoF3g" + }, + "source": [ + "## 3. Writing functions with checks\n", + "\n", + "For the functions in section 1, we provided some assert statements, but when you write your own programs, you usually don't have those. Instead, you have _something you want to do_, and it is up to you to make your own `assert` checks.\n", + "\n", + "For the exercises below, we describe what your function should do. It is up to you to make both the function and the assert statements.\n", + "\n", + "For each exercise, consider the following:\n", + "\n", + "- What should the function be callled?\n", + "- What are the input parameters? What type should they be?\n", + "- What is the output? What type should the output be?\n", + "- What are some checks you could write?\n", + "- What should be in the body of the function? How does it work?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "EXgk0_mjoukf" + }, + "source": [ + "3.1: Write a function that turns a sentence into a question. The input should be a string. If the last character in the string is a `.` or `!`, it should be replaced with a `?`. Otherwise, we just want to add `?` to the end of the string (without replacing anything)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ExH64Sbgovw0" + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "uA4PQxchxUrT" + }, + "source": [ + "3.2: Write a function that takes a year as input and tells you whether or not that year is a leap year.\n", + "\n", + "From [wikipedia](https://en.wikipedia.org/wiki/Leap_year):\n", + "\n", + "> Every year that is exactly divisible by four is a leap year, except for years that are exactly divisible by 100, but these centurial years are leap years if they are exactly divisible by 400. For example, the years 1700, 1800, and 1900 are not leap years, but the years 1600 and 2000 are." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "VIYiW1OCxVEo" + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "QzewFsCJomVB" + }, + "source": [ + "3.3: Write a function to calculate the area of a circle based on the diameter.\n", + "\n", + "- The _area_ is equal the _radius_ of the circle, squared, multiplied by pi. In a formula: $A = r^2 \\times \\pi$. \n", + "- The radius of a circle is half its diameter.\n", + "- You can assume that pi is equal to 3.14" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "oCID8txRotb-" + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SoO0C4pyxSjO" + }, + "source": [ + "3.4: We want to calculate prices for an online store. Prices are calculated as follows:\n", + "- An order will include one or more items, each with their own price. You can just represent the order as a list of prices, e.g. `[15.00, 4.40, 31.50]`\n", + "- Customers may have a coupon that gives them 10% off all the items in their basket.\n", + "- If the total of the order (including the coupon discount) is under € 20.00, customers need to pay € 2.00 shipping.\n", + "\n", + "Write a function that calculates the total of an order." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ddWh7EWCxS6M" + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "m1bBl0RvxXe-" + }, + "source": [ + "3.5: Write a function that counts how often a string occurs within another string." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "GKRKIpKgxXwV" + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "NPXFJd95o3Ei" + }, + "source": [ + "## 4. Don't repeat yourself\n", + "\n", + "An important use for functions is to avoid unnecessary repetition in code. When you write two pieces of very similar code, it's often convenient to make a single function.\n", + "\n", + "Each of the code blocks below contains two things:\n", + "- A section of code that has some repetition\n", + "- Some assert statements that check if the code works as intended.\n", + "\n", + "For each block:\n", + "- Try to understand what the code does, and identify the repetition.\n", + "- At the top of the code block, add a definition for a function (or several), that can handle the shared functionality.\n", + "- Use your functions(s) in the code, and check that everything still works." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "JFc_E5sF5rt1" + }, + "outputs": [], + "source": [ + "small_pizza_price = 9.00\n", + "small_pizza_slices = 6\n", + "\n", + "small_pizza_price_per_slice = small_pizza_price / small_pizza_slices\n", + "print('The small pizza costs €', small_pizza_price_per_slice, 'per slice')\n", + "\n", + "medium_pizza_price = 12.00\n", + "medium_pizza_slices = 8\n", + "\n", + "medium_pizza_price_per_slice = medium_pizza_price / medium_pizza_slices\n", + "print('The medium pizza costs €', medium_pizza_price_per_slice, 'per slice')\n", + "\n", + "large_pizza_price = 16.00\n", + "large_pizza_slices = 8\n", + "\n", + "large_pizza_price_per_slice = large_pizza_price / large_pizza_slices\n", + "print('The large pizza costs €', large_pizza_price_per_slice, 'per slice')\n", + "\n", + "# check if everything works\n", + "assert small_pizza_price_per_slice == 1.5\n", + "assert medium_pizza_price_per_slice == 1.5\n", + "assert large_pizza_price_per_slice == 2.0" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "lirYtOcWpJ6m" + }, + "outputs": [], + "source": [ + "fruit_basket = ['apple', 'banana', 'banana', 'banana', 'apple', 'orange',\n", + " 'orange', 'grape', 'grape', 'grape', 'grape', 'grape', 'grape',\n", + " 'grape', 'grape', 'grape', 'pear', 'apple', 'strawberry',\n", + " 'strawberry', 'strawberry', 'orange']\n", + "\n", + "apples = 0\n", + "for fruit in fruit_basket:\n", + " if fruit == 'apple':\n", + " apples += 1\n", + "\n", + "oranges = 0\n", + "for fruit in fruit_basket:\n", + " if fruit == 'orange':\n", + " oranges += 1\n", + "\n", + "apples_to_oranges = apples / oranges\n", + "\n", + "# check if everything works\n", + "assert apples_to_oranges == 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "mdNug4ct5645", + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "weather_this_month = ['rain', 'rain', 'sun', 'sun', 'cloudy', 'cloudy', 'rain',\n", + " 'cloudy', 'rain', 'rain', 'cloudy', 'sun', 'cloudy', 'sun', 'sun', 'sun',\n", + " 'rain', 'cloudy', 'cloudy', 'sun', 'cloudy', 'rain', 'rain', 'cloudy',\n", + " 'cloudy', 'cloudy', 'sun', 'cloudy', 'sun', 'sun']\n", + "\n", + "rain_followed_by_sun = 0\n", + "rain_followed_by_cloudy = 0\n", + "rain_followed_by_rain = 0\n", + "\n", + "for index, weather in enumerate(weather_this_month):\n", + " not_last = index < len(weather_this_month) - 1\n", + " if weather == 'rain' and not_last:\n", + " weather_next_day = weather_this_month[index + 1]\n", + " if weather_next_day == 'sun':\n", + " rain_followed_by_sun += 1\n", + " elif weather_next_day == 'cloudy':\n", + " rain_followed_by_cloudy += 1\n", + " else:\n", + " rain_followed_by_rain += 1\n", + "\n", + "print('Days with rain were followed by:')\n", + "print('Sun:', rain_followed_by_sun, 'times')\n", + "print('Clouds:', rain_followed_by_cloudy, 'times')\n", + "print('Rain:', rain_followed_by_rain, 'times')\n", + "print()\n", + "\n", + "sun_followed_by_sun = 0\n", + "sun_followed_by_cloudy = 0\n", + "sun_followed_by_rain = 0\n", + "\n", + "for index, weather in enumerate(weather_this_month):\n", + " not_last = index < len(weather_this_month) - 1\n", + " if weather == 'sun' and not_last:\n", + " weather_next_day = weather_this_month[index + 1]\n", + " if weather_next_day == 'sun':\n", + " sun_followed_by_sun += 1\n", + " elif weather_next_day == 'cloudy':\n", + " sun_followed_by_cloudy += 1\n", + " else:\n", + " sun_followed_by_rain += 1\n", + "\n", + "print('Days with sun were followed by:')\n", + "print('Sun:', sun_followed_by_sun, 'times')\n", + "print('Clouds:', sun_followed_by_cloudy, 'times')\n", + "print('Rain:', sun_followed_by_rain, 'times')\n", + "print()\n", + "\n", + "sun_preceded_by_sun = 0\n", + "sun_preceded_by_cloudy = 0\n", + "sun_preceded_by_rain = 0\n", + "\n", + "\n", + "for index, weather in enumerate(weather_this_month):\n", + " not_first = index > 0\n", + " if weather == 'sun' and not_first:\n", + " weather_prev_day = weather_this_month[index - 1]\n", + " if weather_prev_day == 'sun':\n", + " sun_preceded_by_sun += 1\n", + " elif weather_prev_day == 'cloudy':\n", + " sun_preceded_by_cloudy += 1\n", + " else:\n", + " sun_preceded_by_rain += 1\n", + "\n", + "print('Days with sun were preceded by:')\n", + "print('Sun:', sun_preceded_by_sun, 'times')\n", + "print('Clouds:', sun_preceded_by_cloudy, 'times')\n", + "print('Rain:', sun_preceded_by_rain, 'times')\n", + "print()\n", + "\n", + "# check if everything works\n", + "assert rain_followed_by_sun == 1\n", + "assert rain_followed_by_cloudy == 4\n", + "assert rain_followed_by_rain\n", + "\n", + "assert sun_followed_by_sun == 4\n", + "assert sun_followed_by_cloudy == 4\n", + "assert sun_followed_by_rain == 1\n", + "\n", + "assert sun_preceded_by_sun == 4\n", + "assert sun_preceded_by_cloudy == 5\n", + "assert sun_preceded_by_rain == 1" + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyMaLbZUxFMzVIjab/8j3smI", + "provenance": [] + }, + "jupytext": { + "main_language": "python" + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/lessons/07a Functions (extra exercises).py b/lessons/07a Functions (extra exercises).py new file mode 100644 index 0000000..e950be3 --- /dev/null +++ b/lessons/07a Functions (extra exercises).py @@ -0,0 +1,478 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# name: python3 +# --- + +# %% [markdown] id="q2_7oxCuwonl" +# # 7a - Functions (extra exercises) +# +# This notebook contains extra exercises to help you get more familiar with writing python code, and especially making functions. It contains no new theory. +# +# There are a lot of exercises, and they can be very similar. Don't feel like you have to do everything: if the exercises start to feel repetitive, you're ready to move on to the next section. + +# %% [markdown] id="KoEzBrcFxk8T" +# ## 1. Writing functions +# +# In these exercises, the instructions describe a function that you need to make. Each code block also has some assert statements that try to use your function, which you can use to verify your solution. + +# %% [markdown] id="DxKNUR11yfvL" +# 1.1: Write a function `first`, which returns the first item in a list. The function should take one argument (a list). +# +# If the list if empty, `first` should return `None`. + +# %% id="YtJmU-K0woBC" +# your code here... + +# check your function +assert first(['apple', 'banana', 'orange']) == 'apple' +assert first(['just one item']) == 'just one item' +assert first([]) == None + +# %% [markdown] id="jgxg9ecn0GkZ" +# 1.2: Write a function `check_wallet` which takes a list of items as its argument. If the list includes `'wallet'`, it should return the list as-is. If it doesn't, it should add `'wallet'` to the end of list. + +# %% id="Z2Xa8C6z0BrI" +# your code here... + +# check your function +assert check_wallet(['keys', 'wallet', 'phone', 'gloves']) == ['keys', 'wallet', 'phone', 'gloves'] +assert check_wallet(['keys', 'phone']) == ['keys', 'phone', 'wallet'] +assert check_wallet([]) == ['wallet'] + +# %% [markdown] id="Zx9RsFoY2o5M" +# 1.3: Write a function `round_to_10` which takes a number as its argument and returns a new number: the input rounded to the nearest multiple of 10. + +# %% id="Y8M_CCuCcVZm" +# your code here... + +# check your function +assert round_to_10(8) == 10 +assert round_to_10(22.1) == 20 +assert round_to_10(40) == 40 +assert round_to_10(55) == 60 + +# %% [markdown] id="YKRA2X94dmfm" +# 1.4: Let's make our rounding function a bit more generic. Make a new function `round_to_n` which takes two arguments: the number that you want to round, and the unit that you want to round to. `round_to_n(35, 50)` will round 35 to the nearest multiple of 50. + +# %% id="CFukYZRFeMMr" +# your code here + + +# check your function +assert round_to_n(8, 10) == 10 +assert round_to_n(8, 20) == 0 +assert round_to_n(24.3, 20) == 20 + +# %% [markdown] id="9C8cDTS3e62p" +# 1.5: Write a function `in_alphabet` that takes a character (a string of length 1) as a parameter. It should return `True` if the character is one of the 26 letters in the latin alphabet. Uppercase and lowercase characters should both return `True`. Otherwise, the function should return `False`. + +# %% id="X9kXxwe3fD7P" +# your code here + +# check your function +assert in_alphabet('a') == True +assert in_alphabet('f') == True +assert in_alphabet('A') == True +assert in_alphabet('1') == False +assert in_alphabet('✨') == False + +# %% [markdown] id="lXc43yNgicoA" +# 1.6: Use your function `in_alphabet` to write a new function `clean_text`. It should take one parameter, which is a string. It should return a new string, which is like the old one but leaves out any characters that are not in the latin alphabet or the space `' '`. + +# %% id="YOqKnf7rjpvW" +# your code here... + +# check your function +assert clean_text('Hello, how are you?') == 'Hello how are you' +assert clean_text('Hooray! Hooray! Hooray!') == 'Hooray Hooray Hooray' +assert clean_text('Yummy!! 😋') == 'Yummy ' + +# %% [markdown] id="mzKQAJxNLUgV" +# 1.7: Write a function `travel_time` that estimates the travel time for a journey. It takes 3 named arguments: +# - `car`: the distance traversed by car +# - `bike`: the distance traversed by bike +# - `walk`: the distance traversed on foot +# +# Each of these will be a number (distance in km), and the default value is `0`. Calculate the travel time, assuming that: +# +# - Cars have an average speed of 80 km/h +# - Bikes have an average speed of 16 km/h +# - Walking has an average speed of 4 km/h +# +# Your function should return the travel time in hours. + +# %% id="XDVlkfHyMIfE" +# your code here... + +# check your function +assert travel_time(car=10, walk=0.1) == 0.15 +assert travel_time(bike=10, walk=0.1) == 0.65 +assert travel_time(car=55, bike=3, walk=0.3) == 0.95 + +# %% [markdown] id="SQHPePrYxdnG" +# 1.8: Write a function `to_time` which takes a decimal number of hours (like the output of `travel time`) and converts it to hours, minutes, and seconds. For example, 1.5 hours is 1 hours, 30 minutes and 0 seconds. +# +# The input should be a single `float` or `int`, and the output should be three numbers: hours, minutes, and seconds. The output should be given in whole numbers (`int`, not `float`). + +# %% id="5cdwImkNUGcc" +# your code here... + +# check your function +assert to_time(1.5) == (1, 30, 0) +assert to_time(3.6532) == (3, 39, 55) +assert to_time(0) == (0, 0, 0) + +# %% [markdown] id="shtLjnOGxen9" +# 1.9: Write a function `mean` which takes one argument, a list of numbers, as input. It should return the mean value of the list (a single number). + +# %% id="xJ_XTebzxfCs" +# your code here... + +# check your function +assert mean([1, 2, 7 , 2]) == 3.0 +assert mean([3.2, 4.5, 0.0, 2.5]) == 2.55 +assert mean([0.0]) == 0.0 + +# %% [markdown] id="p1NF-Tbpxfs1" +# 1.10: Write a function `is_number` that checks if a string describes a number. It takes one argument (a string). +# +# It should return `True` if: +# - Every character in the string is either a digit (0-9) or a decimal point `'.'`) +# - If there is a decimal point, it should not be more than one. +# +# Otherwise, the function should return `False`. + +# %% id="LsA_B5iPxgEE" +# your code here... + +# check your function. +assert is_number('12') == True +assert is_number('0.5') == True +assert is_number('two') == False +assert is_number('1.2.3') == False + + +# %% [markdown] id="o55Hp9SomntG" +# ## 2. Writing `assert` checks +# +# The code blocks below contain a function definition, but are missing some `assert` statements to check if everything works. Read the docstring and try to think of some assertions you could use to check the function. +# +# Not all of these functions work as intended! For some functions, you should be able to find assert statements that should succeed, but fail. +# +# You do not have to correct the functions (but you can if you want to). + +# %% id="aqngxSucow39" +def distance(a, b): + ''' + Gives the distance between two numbers. + + Distance is absolute, i.e. always positive. + ''' + + if a <= b: + return b - a + else: + return a - b + +# write some checks! + + +# %% id="khwUSdMWn1CR" +def reverse(word): + ''' + Reverses a string + + Input must be a string. Output is the same string in reverse order. + ''' + + new_word = '' + + for character in word: + new_word = character + new_word + + return new_word + +# write some checks! + + +# %% id="nkpyPnDgoxKf" +def exclude_short_words(words, min_length=5): + ''' + Filters all short words (shorter than min_length) out of a list + + Input should be a list of words (strings). Returns a new list + with only the words longer than min_length. + ''' + + long_words = [] + + for word in words: + if len(word) < 5: + long_words.append(word) + + return long_words + +# write some checks! + + +# %% id="yQ_-_xnuoyZ9" +def every_third_item(items): + ''' + Returns every third item in a list. + + Input should be a list, the output is a new list. Always returns the first + item - after that, returns every third item. + ''' + + result = [] + + for index in range(0, len(items), 3): + result.append(items[index]) + + return result + +# write some checks! + + +# %% id="ArwDSoguox2j" +def grade(score): + ''' + Gives a letter grade (F, D, C, B, A) based on a score from 0-100. + + Scores over 60 get grade D, scores over 70 get grade C, etc. + ''' + if score >= 60: + return 'D' + elif score >= 70: + return 'C' + elif score >= 80: + return 'B' + elif score >= 90: + return 'A' + else: + return 'F' + +# write some checks! + + +# %% id="hMIpfW_lMK0l" +def is_prime(number): + ''' + Checks if a int is a prime number. + + Prime numbers are numbers that are NOT the product + of two smaller numbers (2, 3, 5, 7, 11, are prime numbers) + ''' + + for smaller_number in range(2, number): + if number % smaller_number == 0: + return False + + return True + +# write some checks! + + +# %% [markdown] id="aKgZVXZXoF3g" +# ## 3. Writing functions with checks +# +# For the functions in section 1, we provided some assert statements, but when you write your own programs, you usually don't have those. Instead, you have _something you want to do_, and it is up to you to make your own `assert` checks. +# +# For the exercises below, we describe what your function should do. It is up to you to make both the function and the assert statements. +# +# For each exercise, consider the following: +# +# - What should the function be callled? +# - What are the input parameters? What type should they be? +# - What is the output? What type should the output be? +# - What are some checks you could write? +# - What should be in the body of the function? How does it work? + +# %% [markdown] id="EXgk0_mjoukf" +# 3.1: Write a function that turns a sentence into a question. The input should be a string. If the last character in the string is a `.` or `!`, it should be replaced with a `?`. Otherwise, we just want to add `?` to the end of the string (without replacing anything). + +# %% id="ExH64Sbgovw0" + +# %% [markdown] id="uA4PQxchxUrT" +# 3.2: Write a function that takes a year as input and tells you whether or not that year is a leap year. +# +# From [wikipedia](https://en.wikipedia.org/wiki/Leap_year): +# +# > Every year that is exactly divisible by four is a leap year, except for years that are exactly divisible by 100, but these centurial years are leap years if they are exactly divisible by 400. For example, the years 1700, 1800, and 1900 are not leap years, but the years 1600 and 2000 are. + +# %% id="VIYiW1OCxVEo" + +# %% [markdown] id="QzewFsCJomVB" +# 3.3: Write a function to calculate the area of a circle based on the diameter. +# +# - The _area_ is equal the _radius_ of the circle, squared, multiplied by pi. In a formula: $A = r^2 \times \pi$. +# - The radius of a circle is half its diameter. +# - You can assume that pi is equal to 3.14 + +# %% id="oCID8txRotb-" + +# %% [markdown] id="SoO0C4pyxSjO" +# 3.4: We want to calculate prices for an online store. Prices are calculated as follows: +# - An order will include one or more items, each with their own price. You can just represent the order as a list of prices, e.g. `[15.00, 4.40, 31.50]` +# - Customers may have a coupon that gives them 10% off all the items in their basket. +# - If the total of the order (including the coupon discount) is under € 20.00, customers need to pay € 2.00 shipping. +# +# Write a function that calculates the total of an order. + +# %% id="ddWh7EWCxS6M" + +# %% [markdown] id="m1bBl0RvxXe-" +# 3.5: Write a function that counts how often a string occurs within another string. + +# %% id="GKRKIpKgxXwV" + +# %% [markdown] id="NPXFJd95o3Ei" +# ## 4. Don't repeat yourself +# +# An important use for functions is to avoid unnecessary repetition in code. When you write two pieces of very similar code, it's often convenient to make a single function. +# +# Each of the code blocks below contains two things: +# - A section of code that has some repetition +# - Some assert statements that check if the code works as intended. +# +# For each block: +# - Try to understand what the code does, and identify the repetition. +# - At the top of the code block, add a definition for a function (or several), that can handle the shared functionality. +# - Use your functions(s) in the code, and check that everything still works. + +# %% id="JFc_E5sF5rt1" +small_pizza_price = 9.00 +small_pizza_slices = 6 + +small_pizza_price_per_slice = small_pizza_price / small_pizza_slices +print('The small pizza costs €', small_pizza_price_per_slice, 'per slice') + +medium_pizza_price = 12.00 +medium_pizza_slices = 8 + +medium_pizza_price_per_slice = medium_pizza_price / medium_pizza_slices +print('The medium pizza costs €', medium_pizza_price_per_slice, 'per slice') + +large_pizza_price = 16.00 +large_pizza_slices = 8 + +large_pizza_price_per_slice = large_pizza_price / large_pizza_slices +print('The large pizza costs €', large_pizza_price_per_slice, 'per slice') + +# check if everything works +assert small_pizza_price_per_slice == 1.5 +assert medium_pizza_price_per_slice == 1.5 +assert large_pizza_price_per_slice == 2.0 + +# %% id="lirYtOcWpJ6m" +fruit_basket = ['apple', 'banana', 'banana', 'banana', 'apple', 'orange', + 'orange', 'grape', 'grape', 'grape', 'grape', 'grape', 'grape', + 'grape', 'grape', 'grape', 'pear', 'apple', 'strawberry', + 'strawberry', 'strawberry', 'orange'] + +apples = 0 +for fruit in fruit_basket: + if fruit == 'apple': + apples += 1 + +oranges = 0 +for fruit in fruit_basket: + if fruit == 'orange': + oranges += 1 + +apples_to_oranges = apples / oranges + +# check if everything works +assert apples_to_oranges == 1 + +# %% id="mdNug4ct5645" +weather_this_month = ['rain', 'rain', 'sun', 'sun', 'cloudy', 'cloudy', 'rain', + 'cloudy', 'rain', 'rain', 'cloudy', 'sun', 'cloudy', 'sun', 'sun', 'sun', + 'rain', 'cloudy', 'cloudy', 'sun', 'cloudy', 'rain', 'rain', 'cloudy', + 'cloudy', 'cloudy', 'sun', 'cloudy', 'sun', 'sun'] + +rain_followed_by_sun = 0 +rain_followed_by_cloudy = 0 +rain_followed_by_rain = 0 + +for index, weather in enumerate(weather_this_month): + not_last = index < len(weather_this_month) - 1 + if weather == 'rain' and not_last: + weather_next_day = weather_this_month[index + 1] + if weather_next_day == 'sun': + rain_followed_by_sun += 1 + elif weather_next_day == 'cloudy': + rain_followed_by_cloudy += 1 + else: + rain_followed_by_rain += 1 + +print('Days with rain were followed by:') +print('Sun:', rain_followed_by_sun, 'times') +print('Clouds:', rain_followed_by_cloudy, 'times') +print('Rain:', rain_followed_by_rain, 'times') +print() + +sun_followed_by_sun = 0 +sun_followed_by_cloudy = 0 +sun_followed_by_rain = 0 + +for index, weather in enumerate(weather_this_month): + not_last = index < len(weather_this_month) - 1 + if weather == 'sun' and not_last: + weather_next_day = weather_this_month[index + 1] + if weather_next_day == 'sun': + sun_followed_by_sun += 1 + elif weather_next_day == 'cloudy': + sun_followed_by_cloudy += 1 + else: + sun_followed_by_rain += 1 + +print('Days with sun were followed by:') +print('Sun:', sun_followed_by_sun, 'times') +print('Clouds:', sun_followed_by_cloudy, 'times') +print('Rain:', sun_followed_by_rain, 'times') +print() + +sun_preceded_by_sun = 0 +sun_preceded_by_cloudy = 0 +sun_preceded_by_rain = 0 + + +for index, weather in enumerate(weather_this_month): + not_first = index > 0 + if weather == 'sun' and not_first: + weather_prev_day = weather_this_month[index - 1] + if weather_prev_day == 'sun': + sun_preceded_by_sun += 1 + elif weather_prev_day == 'cloudy': + sun_preceded_by_cloudy += 1 + else: + sun_preceded_by_rain += 1 + +print('Days with sun were preceded by:') +print('Sun:', sun_preceded_by_sun, 'times') +print('Clouds:', sun_preceded_by_cloudy, 'times') +print('Rain:', sun_preceded_by_rain, 'times') +print() + +# check if everything works +assert rain_followed_by_sun == 1 +assert rain_followed_by_cloudy == 4 +assert rain_followed_by_rain + +assert sun_followed_by_sun == 4 +assert sun_followed_by_cloudy == 4 +assert sun_followed_by_rain == 1 + +assert sun_preceded_by_sun == 4 +assert sun_preceded_by_cloudy == 5 +assert sun_preceded_by_rain == 1 + diff --git a/lessons/08 debugging.ipynb b/lessons/08 debugging.ipynb index eb22fa0..60287bf 100644 --- a/lessons/08 debugging.ipynb +++ b/lessons/08 debugging.ipynb @@ -1 +1,401 @@ -{"cells":[{"cell_type":"markdown","metadata":{"id":"fBBVyNmi8Fo0"},"source":["# Module 8: Debugging\n","\n","### CDH course \"Programming in Python\"\n","\n","[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n","\n","Previous module: [7. Functions](https://colab.research.google.com/drive/1APhegD_KpLRFn5bC6Ater2wFpT8_Opfr)\n","\n","### This module\n","\n","- How to see exactly what is going on in your program.\n","- Using this to solve problems with code."]},{"cell_type":"markdown","metadata":{"id":"y0FX-JxubjAb"},"source":["## Stepping with the debugger\n","\n","The following \"magic line\", which you can insert anywhere in your code (preferably inside a function), will create a *breakpoint*:\n","\n","```py\n","import pdb; pdb.set_trace()\n","```\n","\n","> ⚠️ In notebooks, you *need* to put the breakpoint inside a function. Standalone Python also allows them outside of functions.\n","\n","> 💡 There exist other ways to set breakpoints, some of which look less like magic. The one shown here works in all situations.\n","\n","When Python encounters a breakpoint, it pauses execution of the program and hands control to you. You \"enter the debugger\". This enables you to see what is going on inside the program. Some useful commands to remember:\n","\n","- `l` or `list`: show the code around the current line.\n","- `p ` or `print `: show the value of the variable with the given ``.\n","- `pp `: *pretty* print a value (nice for long strings and for data structures).\n","- `n` or `next`: execute the current line and stop at the next line.\n","- `s` or `step`: step into the next function call and stop at its first line.\n","- `r` or `return`: finish executing the current function and stop at the line after its call.\n","- `c` or `cont` or `continue`: resume the program until the next breakpoint, or until the end.\n","- `help`: show the list with available commands.\n","\n","Demonstration below!"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"rDx9b5MJhR7X"},"outputs":[],"source":["def scream(name):\n"," \"\"\" Using at least one bang (!), make `name` at least 10 chars long. \"\"\"\n"," # import pdb; pdb.set_trace()\n"," name = name + '!'\n"," while len(name) < 10:\n"," name = name + '!'\n"," return name\n","\n","name = 'Julian'\n","exclamation = scream(name)\n","print(exclamation)"]},{"cell_type":"markdown","metadata":{"id":"V5a4AtnAAMc2"},"source":["## Exercise 8.1: Debugging"]},{"cell_type":"markdown","metadata":{"id":"5fI43xswHUXJ"},"source":["### 1. Building muscle memory\n","\n","In the definition of `scream` above, uncomment the breakpoint and step through the code. Make the debugger print two different values of `name`, then allow it to complete the program until the end."]},{"cell_type":"markdown","metadata":{"id":"6OXyDh8tASN_"},"source":["### 2. Known bugs\n","\n","Each of the code blocks below has a function, a `print` statement and an assertion. The assertion should pass, but it fails because of a bug in the function. For each code block, take the following steps:\n","\n","1. Read the code. Can you predict what will be printed and why the assertion will fail?\n","2. Run the code. Are you surprised? Can you explain the result?\n","3. Uncomment the breakpoint. Step through the code and make very sure that you understand what is going wrong.\n","4. Fix the broken function and check that the assertion now passes."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"Z_oXfbLeI6mK"},"outputs":[],"source":["def echo(word):\n"," \"\"\" Make the given word sound like an echo. \"\"\"\n"," # import pdb; pdb.set_trace()\n"," repeated = word + ',' + word\n"," tail_off = repeated + '...'\n"," return tail_off\n","\n","my_echo = echo('Mees')\n","print(my_echo)\n","assert my_echo == 'Mees, Mees...'"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"mxcdaX6SNFdl"},"outputs":[],"source":["# Remember that letters have a numeric ordinal?\n","# We can use these to convert a lowercase to\n","# uppercase and vice versa!\n","uppercase_offset = ord('A') - ord('a')\n","\n","def uppercase(lowercase_letter):\n"," \"\"\" Convert a lowercase letter to uppercase. \"\"\"\n"," # import pdb; pdb.set_trace()\n"," lowercase_ordinal = ord(lowercase_letter)\n"," uppercase_ordinal = lowercase_ordinal - uppercase_offset\n"," uppercase_letter = chr(uppercase_ordinal)\n"," return uppercase_letter\n","\n","upper_a = uppercase('a')\n","print(upper_a)\n","assert upper_a == 'A'"]},{"cell_type":"markdown","metadata":{"id":"CqsSkFb_H7Sk"},"source":["### 3. Unknown bugs\n","\n","Like in the previous exercises, the following code blocks have a function with a bug and an assertion. However, the assertion passes, because it does not demonstrate the bug. For each code block, take the following steps:\n","\n","1. Run the code and see for yourself that the assertion passes.\n","2. Read the code. Do you have any suspicion of what might be wrong? Do not change the code yet.\n","3. Add more assertions until you discover one that should pass, but doesn't. If you had a suspicion in step 2, try to confirm it with your new assertions. If you had no suspicion yet (or if you cannot comfirm it), it is also fine to \"blind guess\".\n","4. Once you have found an assertion that fails, comment out all other assertions, so only the problematic one will run.\n","5. By your own judgment, insert a breakpoint in the code that should help you to diagnose the bug. Step through the code with the debugger until you fully understand the bug.\n","6. Outcomment your breakpoint and fix the bug. **Tip:** you may reuse code from previous exercises, if you think this would be useful.\n","7. Uncomment all assertions that you previously outcommented, so that all assertions are checked when you run the code block.\n","8. Run the code block and verify that your fixed version of the function passes all the assertions."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"6Nm0bcBINyNk"},"outputs":[],"source":["# Python has a built-in function for reversing strings,\n","# so normally you would not need to write it yourself.\n","\n","def reverse_string(text):\n"," \"\"\" Given any string, return a reversed copy. \"\"\"\n"," reversed = text[0]\n"," for next_letter in text[1:]:\n"," reversed = next_letter + reversed\n"," return reversed\n","\n","assert reverse_string('abc') == 'cba'\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"vooRS00TQNSP"},"outputs":[],"source":["vowels = 'aeiouy'\n","\n","def count_vowels(text):\n"," \"\"\" Given any string, return the number of vowels. \"\"\"\n"," count = 0\n"," for character in text:\n"," if character in vowels:\n"," count = count + 1\n"," return count\n","\n","assert count_vowels('The quick fox jumps over the lazy dog.') == 11"]},{"cell_type":"markdown","metadata":{"id":"O0Piet3Y-T6B"},"source":["## Exercise 8.2: Bonus"]},{"cell_type":"markdown","metadata":{"id":"833NNleFrsro"},"source":["### 1. Gnome sort\n","\n","Gnome sort is possibly the simplest way to sort a list. It can be understood by imagining that a gnome (indicated below by the `^`) is standing next to the list.\n","\n","```\n","-------------\n","| 2 | 3 | 1 |\n","-------------\n"," ^\n","```\n","\n","The gnome looks at the element closest to herself, as well as at the element to the right of it (here `2` and `3`). When those elements are already sorted, the gnome walks one element to the right and looks again:\n","\n","```\n","-------------\n","| 2 | 3 | 1 |\n","-------------\n"," ^\n","```\n","\n","When the current element and the one right of it are *not* sorted, like here, the gnome swaps those elements and walks one place to the left:\n","\n","```\n","-------------\n","| 2 | 1 | 3 |\n","-------------\n"," ^\n","```\n","\n","The gnome continues doing this, taking care not to walk off the left edge of the list:\n","\n","```\n","-------------\n","| 1 | 2 | 3 |\n","-------------\n"," ^\n","```\n","\n","```\n","-------------\n","| 1 | 2 | 3 |\n","-------------\n"," ^\n","```\n","\n","```\n","-------------\n","| 1 | 2 | 3 |\n","-------------\n"," ^\n","```\n","\n","When there is no more element to the right, the gnome stops. If we make sure to start the gnome at the leftmost element of the list, this will sort the entire list.\n","\n","Below, we implemented gnome sort in Python, but we made one mistake. The program never finishes. Step with the debugger to find the mistake.\n","\n","> ⚠️ If you need to sort something \"for real\", Python has ready-to-use solutions, so you do not normally need to write your own sorting code!"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"TcfxzJ_Bn971"},"outputs":[],"source":["shuffled = ['banana', 'apricot', 'date', 'cherry', 'date']\n","alphabetical = ['apricot', 'banana', 'cherry', 'date', 'date']\n","\n","def gnome_sort(items):\n"," \"\"\" Sort `items` by changing the list in place. Not efficient. \"\"\"\n"," stop = len(items) - 1\n"," index = 0\n"," while index < stop:\n"," here = items[index]\n"," right = items[index + 1]\n"," if here < right:\n"," index = index + 1\n"," else:\n"," items[index:(index + 2)] = [right, here]\n"," index = max(0, index - 1)\n","\n","gnome_sort(shuffled)\n","\n","print(shuffled)\n","assert shuffled == alphabetical"]},{"cell_type":"markdown","metadata":{"id":"bDpSj4jmTe-G"},"source":["### 2. Debugging your own algorithm\n","\n","Write a completely new function (or multiple) that does something useful. For inspiration, here are some suggestions:\n","\n","- Compute the average word length in a text.\n","- Given a string, return `True` if it is a palindrome and `False` otherwise. A palindrome is a string that reads the same if you reverse it. For example, `'abcba'` and `'ab cc ba'` are palindromes, `'abcabc'` is not.\n","- In a list of strings, find the first one that does not contain the word `'the'`.\n","\n","If you feel these examples are too complicated, a simpler algorithm is also fine.\n","\n","Once you have written your function(s), add multiple assertions to check that your code works as you intended. Run your code and see whether you have any failing assertions.\n","\n","If all of your assertions pass, try adding more assertions and see whether you can still find a bug. It is very uncommon for new code to work correctly on the first try! If it seems you have written perfect code on the first try, give yourself a pat on the shoulder, then pick a more complicated algorithm. You need to create code with a bug!\n","\n","Once you have written code with a failing assertion, step with the debugger to understand why the assertion is failing. Fix the bug only after you fully understand the cause. Finally, check that all your assertions pass after the fix. If you still have failing assertions, step with the debugger again. Repeat until all assertions pass."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"TjsG6HmVanqq"},"outputs":[],"source":["# maybe some test data here\n","\n","# your own function(s) here\n","\n","# your own assertions here"]},{"cell_type":"markdown","metadata":{"id":"QBPQVbY_aoLt"},"source":["## Next module\n","\n","[9. String manipulation](https://colab.research.google.com/drive/1Z7NNMxqHTSMoUadH3pVL6Bg6oii4qUoQ)"]}],"metadata":{"colab":{"provenance":[]},"kernelspec":{"display_name":"Python 3","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.10.12"}},"nbformat":4,"nbformat_minor":0} +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "fBBVyNmi8Fo0" + }, + "source": [ + "# Module 8: Debugging\n", + "\n", + "### CDH course \"Programming in Python\"\n", + "\n", + "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "\n", + "Previous module: [7. Functions](https://colab.research.google.com/drive/1APhegD_KpLRFn5bC6Ater2wFpT8_Opfr)\n", + "\n", + "### This module\n", + "\n", + "- How to see exactly what is going on in your program.\n", + "- Using this to solve problems with code." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "y0FX-JxubjAb" + }, + "source": [ + "## Stepping with the debugger\n", + "\n", + "The following \"magic line\", which you can insert anywhere in your code (preferably inside a function), will create a *breakpoint*:\n", + "\n", + "```py\n", + "import pdb; pdb.set_trace()\n", + "```\n", + "\n", + "> ⚠️ In notebooks, you *need* to put the breakpoint inside a function. Standalone Python also allows them outside of functions.\n", + "\n", + "> 💡 There exist other ways to set breakpoints, some of which look less like magic. The one shown here works in all situations.\n", + "\n", + "When Python encounters a breakpoint, it pauses execution of the program and hands control to you. You \"enter the debugger\". This enables you to see what is going on inside the program. Some useful commands to remember:\n", + "\n", + "- `l` or `list`: show the code around the current line.\n", + "- `p ` or `print `: show the value of the variable with the given ``.\n", + "- `pp `: *pretty* print a value (nice for long strings and for data structures).\n", + "- `n` or `next`: execute the current line and stop at the next line.\n", + "- `s` or `step`: step into the next function call and stop at its first line.\n", + "- `r` or `return`: finish executing the current function and stop at the line after its call.\n", + "- `c` or `cont` or `continue`: resume the program until the next breakpoint, or until the end.\n", + "- `help`: show the list with available commands.\n", + "\n", + "Demonstration below!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "rDx9b5MJhR7X" + }, + "outputs": [], + "source": [ + "def scream(name):\n", + " \"\"\" Using at least one bang (!), make `name` at least 10 chars long. \"\"\"\n", + " # import pdb; pdb.set_trace()\n", + " name = name + '!'\n", + " while len(name) < 10:\n", + " name = name + '!'\n", + " return name\n", + "\n", + "name = 'Julian'\n", + "exclamation = scream(name)\n", + "print(exclamation)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "V5a4AtnAAMc2" + }, + "source": [ + "## Exercise 8.1: Debugging" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5fI43xswHUXJ" + }, + "source": [ + "### 1. Building muscle memory\n", + "\n", + "In the definition of `scream` above, uncomment the breakpoint and step through the code. Make the debugger print two different values of `name`, then allow it to complete the program until the end." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6OXyDh8tASN_" + }, + "source": [ + "### 2. Known bugs\n", + "\n", + "Each of the code blocks below has a function, a `print` statement and an assertion. The assertion should pass, but it fails because of a bug in the function. For each code block, take the following steps:\n", + "\n", + "1. Read the code. Can you predict what will be printed and why the assertion will fail?\n", + "2. Run the code. Are you surprised? Can you explain the result?\n", + "3. Uncomment the breakpoint. Step through the code and make very sure that you understand what is going wrong.\n", + "4. Fix the broken function and check that the assertion now passes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Z_oXfbLeI6mK" + }, + "outputs": [], + "source": [ + "def echo(word):\n", + " \"\"\" Make the given word sound like an echo. \"\"\"\n", + " # import pdb; pdb.set_trace()\n", + " repeated = word + ',' + word\n", + " tail_off = repeated + '...'\n", + " return tail_off\n", + "\n", + "my_echo = echo('Mees')\n", + "print(my_echo)\n", + "assert my_echo == 'Mees, Mees...'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "mxcdaX6SNFdl" + }, + "outputs": [], + "source": [ + "# Remember that letters have a numeric ordinal?\n", + "# We can use these to convert a lowercase to\n", + "# uppercase and vice versa!\n", + "uppercase_offset = ord('A') - ord('a')\n", + "\n", + "def uppercase(lowercase_letter):\n", + " \"\"\" Convert a lowercase letter to uppercase. \"\"\"\n", + " # import pdb; pdb.set_trace()\n", + " lowercase_ordinal = ord(lowercase_letter)\n", + " uppercase_ordinal = lowercase_ordinal - uppercase_offset\n", + " uppercase_letter = chr(uppercase_ordinal)\n", + " return uppercase_letter\n", + "\n", + "upper_a = uppercase('a')\n", + "print(upper_a)\n", + "assert upper_a == 'A'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "CqsSkFb_H7Sk" + }, + "source": [ + "### 3. Unknown bugs\n", + "\n", + "Like in the previous exercises, the following code blocks have a function with a bug and an assertion. However, the assertion passes, because it does not demonstrate the bug. For each code block, take the following steps:\n", + "\n", + "1. Run the code and see for yourself that the assertion passes.\n", + "2. Read the code. Do you have any suspicion of what might be wrong? Do not change the code yet.\n", + "3. Add more assertions until you discover one that should pass, but doesn't. If you had a suspicion in step 2, try to confirm it with your new assertions. If you had no suspicion yet (or if you cannot comfirm it), it is also fine to \"blind guess\".\n", + "4. Once you have found an assertion that fails, comment out all other assertions, so only the problematic one will run.\n", + "5. By your own judgment, insert a breakpoint in the code that should help you to diagnose the bug. Step through the code with the debugger until you fully understand the bug.\n", + "6. Outcomment your breakpoint and fix the bug. **Tip:** you may reuse code from previous exercises, if you think this would be useful.\n", + "7. Uncomment all assertions that you previously outcommented, so that all assertions are checked when you run the code block.\n", + "8. Run the code block and verify that your fixed version of the function passes all the assertions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "6Nm0bcBINyNk", + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "# Python has a built-in function for reversing strings,\n", + "# so normally you would not need to write it yourself.\n", + "\n", + "def reverse_string(text):\n", + " \"\"\" Given any string, return a reversed copy. \"\"\"\n", + " reversed = text[0]\n", + " for next_letter in text[1:]:\n", + " reversed = next_letter + reversed\n", + " return reversed\n", + "\n", + "assert reverse_string('abc') == 'cba'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "vooRS00TQNSP" + }, + "outputs": [], + "source": [ + "vowels = 'aeiouy'\n", + "\n", + "def count_vowels(text):\n", + " \"\"\" Given any string, return the number of vowels. \"\"\"\n", + " count = 0\n", + " for character in text:\n", + " if character in vowels:\n", + " count = count + 1\n", + " return count\n", + "\n", + "assert count_vowels('The quick fox jumps over the lazy dog.') == 11" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "O0Piet3Y-T6B" + }, + "source": [ + "## Exercise 8.2: Bonus" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "833NNleFrsro" + }, + "source": [ + "### 1. Gnome sort\n", + "\n", + "Gnome sort is possibly the simplest way to sort a list. It can be understood by imagining that a gnome (indicated below by the `^`) is standing next to the list.\n", + "\n", + "```\n", + "-------------\n", + "| 2 | 3 | 1 |\n", + "-------------\n", + " ^\n", + "```\n", + "\n", + "The gnome looks at the element closest to herself, as well as at the element to the right of it (here `2` and `3`). When those elements are already sorted, the gnome walks one element to the right and looks again:\n", + "\n", + "```\n", + "-------------\n", + "| 2 | 3 | 1 |\n", + "-------------\n", + " ^\n", + "```\n", + "\n", + "When the current element and the one right of it are *not* sorted, like here, the gnome swaps those elements and walks one place to the left:\n", + "\n", + "```\n", + "-------------\n", + "| 2 | 1 | 3 |\n", + "-------------\n", + " ^\n", + "```\n", + "\n", + "The gnome continues doing this, taking care not to walk off the left edge of the list:\n", + "\n", + "```\n", + "-------------\n", + "| 1 | 2 | 3 |\n", + "-------------\n", + " ^\n", + "```\n", + "\n", + "```\n", + "-------------\n", + "| 1 | 2 | 3 |\n", + "-------------\n", + " ^\n", + "```\n", + "\n", + "```\n", + "-------------\n", + "| 1 | 2 | 3 |\n", + "-------------\n", + " ^\n", + "```\n", + "\n", + "When there is no more element to the right, the gnome stops. If we make sure to start the gnome at the leftmost element of the list, this will sort the entire list.\n", + "\n", + "Below, we implemented gnome sort in Python, but we made one mistake. The program never finishes. Step with the debugger to find the mistake.\n", + "\n", + "> ⚠️ If you need to sort something \"for real\", Python has ready-to-use solutions, so you do not normally need to write your own sorting code!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "TcfxzJ_Bn971" + }, + "outputs": [], + "source": [ + "shuffled = ['banana', 'apricot', 'date', 'cherry', 'date']\n", + "alphabetical = ['apricot', 'banana', 'cherry', 'date', 'date']\n", + "\n", + "def gnome_sort(items):\n", + " \"\"\" Sort `items` by changing the list in place. Not efficient. \"\"\"\n", + " stop = len(items) - 1\n", + " index = 0\n", + " while index < stop:\n", + " here = items[index]\n", + " right = items[index + 1]\n", + " if here < right:\n", + " index = index + 1\n", + " else:\n", + " items[index:(index + 2)] = [right, here]\n", + " index = max(0, index - 1)\n", + "\n", + "gnome_sort(shuffled)\n", + "\n", + "print(shuffled)\n", + "assert shuffled == alphabetical" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "bDpSj4jmTe-G" + }, + "source": [ + "### 2. Debugging your own algorithm\n", + "\n", + "Write a completely new function (or multiple) that does something useful. For inspiration, here are some suggestions:\n", + "\n", + "- Compute the average word length in a text.\n", + "- Given a string, return `True` if it is a palindrome and `False` otherwise. A palindrome is a string that reads the same if you reverse it. For example, `'abcba'` and `'ab cc ba'` are palindromes, `'abcabc'` is not.\n", + "- In a list of strings, find the first one that does not contain the word `'the'`.\n", + "\n", + "If you feel these examples are too complicated, a simpler algorithm is also fine.\n", + "\n", + "Once you have written your function(s), add multiple assertions to check that your code works as you intended. Run your code and see whether you have any failing assertions.\n", + "\n", + "If all of your assertions pass, try adding more assertions and see whether you can still find a bug. It is very uncommon for new code to work correctly on the first try! If it seems you have written perfect code on the first try, give yourself a pat on the shoulder, then pick a more complicated algorithm. You need to create code with a bug!\n", + "\n", + "Once you have written code with a failing assertion, step with the debugger to understand why the assertion is failing. Fix the bug only after you fully understand the cause. Finally, check that all your assertions pass after the fix. If you still have failing assertions, step with the debugger again. Repeat until all assertions pass." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "TjsG6HmVanqq" + }, + "outputs": [], + "source": [ + "# maybe some test data here\n", + "\n", + "# your own function(s) here\n", + "\n", + "# your own assertions here" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "QBPQVbY_aoLt" + }, + "source": [ + "## Next module\n", + "\n", + "[9. String manipulation](https://colab.research.google.com/drive/1Z7NNMxqHTSMoUadH3pVL6Bg6oii4qUoQ)" + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "jupytext": { + "main_language": "python" + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/lessons/08 debugging.py b/lessons/08 debugging.py new file mode 100644 index 0000000..56cc339 --- /dev/null +++ b/lessons/08 debugging.py @@ -0,0 +1,269 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# name: python3 +# --- + +# %% [markdown] id="fBBVyNmi8Fo0" +# # Module 8: Debugging +# +# ### CDH course "Programming in Python" +# +# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# +# Previous module: [7. Functions](https://colab.research.google.com/drive/1APhegD_KpLRFn5bC6Ater2wFpT8_Opfr) +# +# ### This module +# +# - How to see exactly what is going on in your program. +# - Using this to solve problems with code. + +# %% [markdown] id="y0FX-JxubjAb" +# ## Stepping with the debugger +# +# The following "magic line", which you can insert anywhere in your code (preferably inside a function), will create a *breakpoint*: +# +# ```py +# import pdb; pdb.set_trace() +# ``` +# +# > ⚠️ In notebooks, you *need* to put the breakpoint inside a function. Standalone Python also allows them outside of functions. +# +# > 💡 There exist other ways to set breakpoints, some of which look less like magic. The one shown here works in all situations. +# +# When Python encounters a breakpoint, it pauses execution of the program and hands control to you. You "enter the debugger". This enables you to see what is going on inside the program. Some useful commands to remember: +# +# - `l` or `list`: show the code around the current line. +# - `p ` or `print `: show the value of the variable with the given ``. +# - `pp `: *pretty* print a value (nice for long strings and for data structures). +# - `n` or `next`: execute the current line and stop at the next line. +# - `s` or `step`: step into the next function call and stop at its first line. +# - `r` or `return`: finish executing the current function and stop at the line after its call. +# - `c` or `cont` or `continue`: resume the program until the next breakpoint, or until the end. +# - `help`: show the list with available commands. +# +# Demonstration below! + +# %% id="rDx9b5MJhR7X" +def scream(name): + """ Using at least one bang (!), make `name` at least 10 chars long. """ + # import pdb; pdb.set_trace() + name = name + '!' + while len(name) < 10: + name = name + '!' + return name + +name = 'Julian' +exclamation = scream(name) +print(exclamation) + + +# %% [markdown] id="V5a4AtnAAMc2" +# ## Exercise 8.1: Debugging + +# %% [markdown] id="5fI43xswHUXJ" +# ### 1. Building muscle memory +# +# In the definition of `scream` above, uncomment the breakpoint and step through the code. Make the debugger print two different values of `name`, then allow it to complete the program until the end. + +# %% [markdown] id="6OXyDh8tASN_" +# ### 2. Known bugs +# +# Each of the code blocks below has a function, a `print` statement and an assertion. The assertion should pass, but it fails because of a bug in the function. For each code block, take the following steps: +# +# 1. Read the code. Can you predict what will be printed and why the assertion will fail? +# 2. Run the code. Are you surprised? Can you explain the result? +# 3. Uncomment the breakpoint. Step through the code and make very sure that you understand what is going wrong. +# 4. Fix the broken function and check that the assertion now passes. + +# %% id="Z_oXfbLeI6mK" +def echo(word): + """ Make the given word sound like an echo. """ + # import pdb; pdb.set_trace() + repeated = word + ',' + word + tail_off = repeated + '...' + return tail_off + +my_echo = echo('Mees') +print(my_echo) +assert my_echo == 'Mees, Mees...' + +# %% id="mxcdaX6SNFdl" +# Remember that letters have a numeric ordinal? +# We can use these to convert a lowercase to +# uppercase and vice versa! +uppercase_offset = ord('A') - ord('a') + +def uppercase(lowercase_letter): + """ Convert a lowercase letter to uppercase. """ + # import pdb; pdb.set_trace() + lowercase_ordinal = ord(lowercase_letter) + uppercase_ordinal = lowercase_ordinal - uppercase_offset + uppercase_letter = chr(uppercase_ordinal) + return uppercase_letter + +upper_a = uppercase('a') +print(upper_a) +assert upper_a == 'A' + + +# %% [markdown] id="CqsSkFb_H7Sk" +# ### 3. Unknown bugs +# +# Like in the previous exercises, the following code blocks have a function with a bug and an assertion. However, the assertion passes, because it does not demonstrate the bug. For each code block, take the following steps: +# +# 1. Run the code and see for yourself that the assertion passes. +# 2. Read the code. Do you have any suspicion of what might be wrong? Do not change the code yet. +# 3. Add more assertions until you discover one that should pass, but doesn't. If you had a suspicion in step 2, try to confirm it with your new assertions. If you had no suspicion yet (or if you cannot comfirm it), it is also fine to "blind guess". +# 4. Once you have found an assertion that fails, comment out all other assertions, so only the problematic one will run. +# 5. By your own judgment, insert a breakpoint in the code that should help you to diagnose the bug. Step through the code with the debugger until you fully understand the bug. +# 6. Outcomment your breakpoint and fix the bug. **Tip:** you may reuse code from previous exercises, if you think this would be useful. +# 7. Uncomment all assertions that you previously outcommented, so that all assertions are checked when you run the code block. +# 8. Run the code block and verify that your fixed version of the function passes all the assertions. + +# %% id="6Nm0bcBINyNk" +# Python has a built-in function for reversing strings, +# so normally you would not need to write it yourself. + +def reverse_string(text): + """ Given any string, return a reversed copy. """ + reversed = text[0] + for next_letter in text[1:]: + reversed = next_letter + reversed + return reversed + +assert reverse_string('abc') == 'cba' + + +# %% id="vooRS00TQNSP" +vowels = 'aeiouy' + +def count_vowels(text): + """ Given any string, return the number of vowels. """ + count = 0 + for character in text: + if character in vowels: + count = count + 1 + return count + +assert count_vowels('The quick fox jumps over the lazy dog.') == 11 + +# %% [markdown] id="O0Piet3Y-T6B" +# ## Exercise 8.2: Bonus + +# %% [markdown] id="833NNleFrsro" +# ### 1. Gnome sort +# +# Gnome sort is possibly the simplest way to sort a list. It can be understood by imagining that a gnome (indicated below by the `^`) is standing next to the list. +# +# ``` +# ------------- +# | 2 | 3 | 1 | +# ------------- +# ^ +# ``` +# +# The gnome looks at the element closest to herself, as well as at the element to the right of it (here `2` and `3`). When those elements are already sorted, the gnome walks one element to the right and looks again: +# +# ``` +# ------------- +# | 2 | 3 | 1 | +# ------------- +# ^ +# ``` +# +# When the current element and the one right of it are *not* sorted, like here, the gnome swaps those elements and walks one place to the left: +# +# ``` +# ------------- +# | 2 | 1 | 3 | +# ------------- +# ^ +# ``` +# +# The gnome continues doing this, taking care not to walk off the left edge of the list: +# +# ``` +# ------------- +# | 1 | 2 | 3 | +# ------------- +# ^ +# ``` +# +# ``` +# ------------- +# | 1 | 2 | 3 | +# ------------- +# ^ +# ``` +# +# ``` +# ------------- +# | 1 | 2 | 3 | +# ------------- +# ^ +# ``` +# +# When there is no more element to the right, the gnome stops. If we make sure to start the gnome at the leftmost element of the list, this will sort the entire list. +# +# Below, we implemented gnome sort in Python, but we made one mistake. The program never finishes. Step with the debugger to find the mistake. +# +# > ⚠️ If you need to sort something "for real", Python has ready-to-use solutions, so you do not normally need to write your own sorting code! + +# %% id="TcfxzJ_Bn971" +shuffled = ['banana', 'apricot', 'date', 'cherry', 'date'] +alphabetical = ['apricot', 'banana', 'cherry', 'date', 'date'] + +def gnome_sort(items): + """ Sort `items` by changing the list in place. Not efficient. """ + stop = len(items) - 1 + index = 0 + while index < stop: + here = items[index] + right = items[index + 1] + if here < right: + index = index + 1 + else: + items[index:(index + 2)] = [right, here] + index = max(0, index - 1) + +gnome_sort(shuffled) + +print(shuffled) +assert shuffled == alphabetical + +# %% [markdown] id="bDpSj4jmTe-G" +# ### 2. Debugging your own algorithm +# +# Write a completely new function (or multiple) that does something useful. For inspiration, here are some suggestions: +# +# - Compute the average word length in a text. +# - Given a string, return `True` if it is a palindrome and `False` otherwise. A palindrome is a string that reads the same if you reverse it. For example, `'abcba'` and `'ab cc ba'` are palindromes, `'abcabc'` is not. +# - In a list of strings, find the first one that does not contain the word `'the'`. +# +# If you feel these examples are too complicated, a simpler algorithm is also fine. +# +# Once you have written your function(s), add multiple assertions to check that your code works as you intended. Run your code and see whether you have any failing assertions. +# +# If all of your assertions pass, try adding more assertions and see whether you can still find a bug. It is very uncommon for new code to work correctly on the first try! If it seems you have written perfect code on the first try, give yourself a pat on the shoulder, then pick a more complicated algorithm. You need to create code with a bug! +# +# Once you have written code with a failing assertion, step with the debugger to understand why the assertion is failing. Fix the bug only after you fully understand the cause. Finally, check that all your assertions pass after the fix. If you still have failing assertions, step with the debugger again. Repeat until all assertions pass. + +# %% id="TjsG6HmVanqq" +# maybe some test data here + +# your own function(s) here + +# your own assertions here + +# %% [markdown] id="QBPQVbY_aoLt" +# ## Next module +# +# [9. String manipulation](https://colab.research.google.com/drive/1Z7NNMxqHTSMoUadH3pVL6Bg6oii4qUoQ) diff --git a/lessons/09 string manipulation.ipynb b/lessons/09 string manipulation.ipynb index 75b103b..6307690 100644 --- a/lessons/09 string manipulation.ipynb +++ b/lessons/09 string manipulation.ipynb @@ -1 +1,600 @@ -{"cells":[{"cell_type":"markdown","metadata":{"id":"7_SeGArbg7Ou"},"source":["# Module 9: String Manipulation\n","\n","### CDH course \"Programming in Python\"\n","\n","[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n","\n","Previous module: [8. Debugging](https://colab.research.google.com/drive/1yQskT6SyKvXtXewx5kCla2NOmasgP8Vi)\n","\n","### This module\n","\n","- String utilities\n","- Formatting strings"]},{"cell_type":"markdown","metadata":{"id":"oadzHVRYh1PN"},"source":["## String utilities\n","Python provides functions for common string operations. When working with text, these are very useful. Try to learn them by heart!"]},{"cell_type":"markdown","metadata":{"id":"3KZGnQuWiM1X"},"source":["### Counting characters\n","\n","Strings are *iterables*, just like lists. We can use this information to quickly give us the length (number of characters) of a string:\n","`len(string)`"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"u-gFos5wii5k"},"outputs":[],"source":["print(len('Matilda'))\n","print(len('The Catcher in the Rye'))"]},{"cell_type":"markdown","metadata":{"id":"r0ZiGq36ipEo"},"source":["### Check for substrings\n","Again wielding the knowledge about strings being *iterables*, we can use the `in` keyword to check if some sequence occurs in the string:\n","`substring in string`"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"yOOOzh3WjCGc"},"outputs":[],"source":["print('a' in 'Matilda')\n","print('til' in 'Matilda')\n","print('in' in 'The Catcher in the Rye')"]},{"cell_type":"markdown","metadata":{"id":"A4Wfxuj_jRAW"},"source":["### Casing\n","Convert a string to lowercase or uppercase:\n","\n","`string.lower()` and `string.upper()`"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"uErzYzBHjbND"},"outputs":[],"source":["print('Matilda'.upper())\n","print('The Catcher in the Rye'.lower())"]},{"cell_type":"markdown","metadata":{"id":"AsVvV-dtjxSD"},"source":["### Replacement\n","Replace certain sequences with another sequence:\n","\n","`string.replace(replacement)`"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"2iIwX0phj4Wt"},"outputs":[],"source":["catcher = 'The Catcher in the Rye'\n","replaced = catcher.replace('e', '3')\n","print(replaced)\n","\n","# optional parameter 'count'\n","replaced_max = catcher.replace('e', '3', 1)\n","print(replaced_max)"]},{"cell_type":"markdown","metadata":{"id":"OQ07Frl4j_H_"},"source":["### Converting to list\n","There are two ways to convert a string into a list:\n","- `list(string)`\n","- `string.split(separator)`\n","\n","The latter is more flexible, because it allows you to choose on which character the string should be split."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"xtsuwTrikrpk"},"outputs":[],"source":["catcher = 'The Catcher in the Rye'\n","tabbed_catcher = catcher.replace(' ', '\\t') # here we replace spaces by the tab character\n","sentence = 'This is some sentence that we wish to split into words.'\n","\n","# use list()\n","print(list(catcher))\n","\n","# using string.split()\n","# default: split on any whitespace\n","print(catcher.split())\n","print(tabbed_catcher.split())\n","print(sentence.split())\n","\n","# optional parameter: separator\n","list_string = 'jelte,julian,berit'\n","print(list_string.split(','))\n","\n","# any separator works, separator is not included in the result:\n","print(list_string.split('e'))"]},{"cell_type":"markdown","metadata":{"id":"UM5FtDPBlBGx"},"source":["### Join an iterable into a string\n","The reverse of splitting is ofcourse also possible: take each element of an *iterable* (such as a list), and glue them together into a string. You can choose which character to put between each element:\n","\n","`string.join(iterable)`"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"CqxglgPOlDfe"},"outputs":[],"source":["words = ['The', 'Catcher', 'in', 'the', 'Rye']\n","\n","# choose a string that should be put between the words\n","print(' '.join(words))\n","print(','.join(words))\n","print(''.join(words))"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"z7p9kwjBlakE"},"outputs":[],"source":["# split and join are opposites\n","sep = ' '\n","title = 'The Catcher in the Rye'\n","split_title = title.split(sep)\n","join_title = sep.join(split_title)\n","assert join_title == title"]},{"cell_type":"markdown","metadata":{"id":"5hIj-tbVleEq"},"source":["## Exercise 9.1: String utilities\n","In each of the code blocks below, try to predict what will be printed, then run the code. If your guess was incorrect, try to figure out why the result is different. If your guess was correct, celebrate!"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"02q-FgvVlxEj"},"outputs":[],"source":["print(len('two'))"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"cvCrnnn9l-oH"},"outputs":[],"source":["print(len(''))"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"UlWWF0k7mA62"},"outputs":[],"source":["assert 'A' in 'Matilda'"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"Ui3gmvCNmHfB"},"outputs":[],"source":["assert 'A' in 'Matilda'.upper()"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"1tDEnzrumNdO"},"outputs":[],"source":["name = 'Matilda'\n","assert name.upper() == 'MATILDA'"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"BUpf6LglmZ4n"},"outputs":[],"source":["name = 'Matilda'\n","assert name.upper().lower() == 'Matilda'"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"sgfEH2jImlwz"},"outputs":[],"source":["print('Matilda'.replace('ilda', 'ild4'))"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"BEE94VVBmf7T"},"outputs":[],"source":["print('Matilda'.replace('a', 4))"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"BVKc0bQAnGYq"},"outputs":[],"source":["list_of_words = ['I', 'ate', 'a', 'banana']\n","sentence = 'I ate a banana'\n","\n","assert sentence.split() == list_of_words"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"R4U_8kVtmpHE"},"outputs":[],"source":["list_of_words = ['I', 'ate', 'a', 'banana']\n","sentence = 'I ate a banana'\n","\n","assert ''.join(list_of_words) == sentence"]},{"cell_type":"markdown","metadata":{"id":"mRATbQdclrcX"},"source":["## Exercise 9.2: Additional utilities\n","For each of the string functions below, find out what they do. If they take any parameters, describe what these do:\n","- `string.startswith()`\n","- `string.endswith()`\n","- `string.strip()`\n","- `string.lstrip()`\n","- `string.rstrip()`\n","- `string.title()`\n","- `string.find()`\n","- `string.split(',', x)` (describe what 'x' does. It should be a number)\n","\n"]},{"cell_type":"markdown","metadata":{"id":"8YAcMdHpnuKw"},"source":["## String formatting\n","We have seen a (not very convenient) way of building strings:"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"pl5hfOYmnzF2"},"outputs":[],"source":["name = 'Julian'\n","shoutout = 'Hey ' + name + '!'\n","print(shoutout)"]},{"cell_type":"markdown","metadata":{"id":"SjWVj2R5n1XR"},"source":["Luckily, there is a better (easier, more flexible) way: `string.format()`:\n","- create a string with *placeholders*: `{}`\n","- fill these placeholders with some value"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"EQ9u1CX6n_TN"},"outputs":[],"source":["shoutout_template = 'Hey {}!'\n","print(shoutout_template)\n","\n","filled_template = shoutout_template.format('Sheean')\n","print(filled_template)\n","\n","names = ['Julian', 'Sheean', 'Jelte', 'Mees']\n","for name in names:\n"," print(shoutout_template.format(name))"]},{"cell_type":"markdown","metadata":{"id":"B1_gS-UaoEy0"},"source":["### Multiple placeholders\n","Not limited to one placeholder, you can have an arbitrary amount"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"8KMQfVXGoHq5"},"outputs":[],"source":["shoutout_template = 'Hey {}! {}'\n","name = 'Julian'\n","followup = 'How are you?'\n","\n","print(shoutout_template.format(name, followup))\n","print(shoutout_template.format(name))\n","print(shoutout_template.format(name, followup, 'Where are you going?'))"]},{"cell_type":"markdown","metadata":{"id":"3lMa3IDjoMZP"},"source":["### Named placeholders\n","Can also give placeholders names, and supply named parameters to `string.format()`."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"re9x0TNLoM4Q"},"outputs":[],"source":["shoutout_template = 'Hey {name}! {followup}'\n","julian = 'Julian'\n","question = 'How are you?'\n","\n","print(shoutout_template.format(name=julian, followup=question))\n","print(shoutout_template.format(followup=question, name=julian))\n","print(shoutout_template.format(followup=julian, name=question))"]},{"cell_type":"markdown","metadata":{},"source":["### F-strings\n","Similar to placeholders, expressions an also be directly combined within a string by putting `f` in front of a string:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["name = 'Sheean'\n","weeks = 2\n","text = f'Hi {name}, your next appointment is in {weeks * 7} days.'\n","\n","print(text)"]},{"cell_type":"markdown","metadata":{"id":"avcqgDAAoq-w"},"source":["## Exercise 9.3: String formatting\n","In each of the code blocks below, try to predict what will be printed, then run the code. If your guess was incorrect, try to figure out why the result is different. If your guess was correct, celebrate!"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"BIa0VOX_owF4"},"outputs":[],"source":["print('hey {}')"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"81GUitOZo0l2"},"outputs":[],"source":["print('hey {Julian}')"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"96Dq4eSCpAji"},"outputs":[],"source":["print('hey {}'.format('Julian'))"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"h43fjzPco4V_"},"outputs":[],"source":["print('hey {Julian}'.format('Julian'))"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"wA18AIPKpFAH"},"outputs":[],"source":["print('hey {name}'.format('Julian'))"]},{"cell_type":"markdown","metadata":{"id":"y5FcFvgypMfE"},"source":["## Next module\n","\n","[10 - Dictionaries](https://colab.research.google.com/drive/16mecEPkVGUBIFoHIALO7dE8qv7PynHL9)"]}],"metadata":{"colab":{"provenance":[],"toc_visible":true},"kernelspec":{"display_name":"Python 3","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.10.12"}},"nbformat":4,"nbformat_minor":0} +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "7_SeGArbg7Ou" + }, + "source": [ + "# Module 9: String Manipulation\n", + "\n", + "### CDH course \"Programming in Python\"\n", + "\n", + "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "\n", + "Previous module: [8. Debugging](https://colab.research.google.com/drive/1yQskT6SyKvXtXewx5kCla2NOmasgP8Vi)\n", + "\n", + "### This module\n", + "\n", + "- String utilities\n", + "- Formatting strings" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "oadzHVRYh1PN" + }, + "source": [ + "## String utilities\n", + "Python provides functions for common string operations. When working with text, these are very useful. Try to learn them by heart!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3KZGnQuWiM1X" + }, + "source": [ + "### Counting characters\n", + "\n", + "Strings are *iterables*, just like lists. We can use this information to quickly give us the length (number of characters) of a string:\n", + "`len(string)`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "u-gFos5wii5k" + }, + "outputs": [], + "source": [ + "print(len('Matilda'))\n", + "print(len('The Catcher in the Rye'))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "r0ZiGq36ipEo" + }, + "source": [ + "### Check for substrings\n", + "Again wielding the knowledge about strings being *iterables*, we can use the `in` keyword to check if some sequence occurs in the string:\n", + "`substring in string`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "yOOOzh3WjCGc" + }, + "outputs": [], + "source": [ + "print('a' in 'Matilda')\n", + "print('til' in 'Matilda')\n", + "print('in' in 'The Catcher in the Rye')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "A4Wfxuj_jRAW" + }, + "source": [ + "### Casing\n", + "Convert a string to lowercase or uppercase:\n", + "\n", + "`string.lower()` and `string.upper()`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "uErzYzBHjbND" + }, + "outputs": [], + "source": [ + "print('Matilda'.upper())\n", + "print('The Catcher in the Rye'.lower())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "AsVvV-dtjxSD" + }, + "source": [ + "### Replacement\n", + "Replace certain sequences with another sequence:\n", + "\n", + "`string.replace(replacement)`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "2iIwX0phj4Wt" + }, + "outputs": [], + "source": [ + "catcher = 'The Catcher in the Rye'\n", + "replaced = catcher.replace('e', '3')\n", + "print(replaced)\n", + "\n", + "# optional parameter 'count'\n", + "replaced_max = catcher.replace('e', '3', 1)\n", + "print(replaced_max)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "OQ07Frl4j_H_" + }, + "source": [ + "### Converting to list\n", + "There are two ways to convert a string into a list:\n", + "- `list(string)`\n", + "- `string.split(separator)`\n", + "\n", + "The latter is more flexible, because it allows you to choose on which character the string should be split." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "xtsuwTrikrpk" + }, + "outputs": [], + "source": [ + "catcher = 'The Catcher in the Rye'\n", + "tabbed_catcher = catcher.replace(' ', '\\t') # here we replace spaces by the tab character\n", + "sentence = 'This is some sentence that we wish to split into words.'\n", + "\n", + "# use list()\n", + "print(list(catcher))\n", + "\n", + "# using string.split()\n", + "# default: split on any whitespace\n", + "print(catcher.split())\n", + "print(tabbed_catcher.split())\n", + "print(sentence.split())\n", + "\n", + "# optional parameter: separator\n", + "list_string = 'jelte,julian,berit'\n", + "print(list_string.split(','))\n", + "\n", + "# any separator works, separator is not included in the result:\n", + "print(list_string.split('e'))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "UM5FtDPBlBGx" + }, + "source": [ + "### Join an iterable into a string\n", + "The reverse of splitting is ofcourse also possible: take each element of an *iterable* (such as a list), and glue them together into a string. You can choose which character to put between each element:\n", + "\n", + "`string.join(iterable)`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "CqxglgPOlDfe" + }, + "outputs": [], + "source": [ + "words = ['The', 'Catcher', 'in', 'the', 'Rye']\n", + "\n", + "# choose a string that should be put between the words\n", + "print(' '.join(words))\n", + "print(','.join(words))\n", + "print(''.join(words))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "z7p9kwjBlakE" + }, + "outputs": [], + "source": [ + "# split and join are opposites\n", + "sep = ' '\n", + "title = 'The Catcher in the Rye'\n", + "split_title = title.split(sep)\n", + "join_title = sep.join(split_title)\n", + "assert join_title == title" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5hIj-tbVleEq" + }, + "source": [ + "## Exercise 9.1: String utilities\n", + "In each of the code blocks below, try to predict what will be printed, then run the code. If your guess was incorrect, try to figure out why the result is different. If your guess was correct, celebrate!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "02q-FgvVlxEj" + }, + "outputs": [], + "source": [ + "print(len('two'))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "cvCrnnn9l-oH" + }, + "outputs": [], + "source": [ + "print(len(''))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "UlWWF0k7mA62" + }, + "outputs": [], + "source": [ + "assert 'A' in 'Matilda'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Ui3gmvCNmHfB" + }, + "outputs": [], + "source": [ + "assert 'A' in 'Matilda'.upper()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "1tDEnzrumNdO" + }, + "outputs": [], + "source": [ + "name = 'Matilda'\n", + "assert name.upper() == 'MATILDA'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "BUpf6LglmZ4n" + }, + "outputs": [], + "source": [ + "name = 'Matilda'\n", + "assert name.upper().lower() == 'Matilda'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "sgfEH2jImlwz" + }, + "outputs": [], + "source": [ + "print('Matilda'.replace('ilda', 'ild4'))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "BEE94VVBmf7T" + }, + "outputs": [], + "source": [ + "print('Matilda'.replace('a', 4))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "BVKc0bQAnGYq" + }, + "outputs": [], + "source": [ + "list_of_words = ['I', 'ate', 'a', 'banana']\n", + "sentence = 'I ate a banana'\n", + "\n", + "assert sentence.split() == list_of_words" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "R4U_8kVtmpHE" + }, + "outputs": [], + "source": [ + "list_of_words = ['I', 'ate', 'a', 'banana']\n", + "sentence = 'I ate a banana'\n", + "\n", + "assert ''.join(list_of_words) == sentence" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "mRATbQdclrcX" + }, + "source": [ + "## Exercise 9.2: Additional utilities\n", + "For each of the string functions below, find out what they do. If they take any parameters, describe what these do:\n", + "- `string.startswith()`\n", + "- `string.endswith()`\n", + "- `string.strip()`\n", + "- `string.lstrip()`\n", + "- `string.rstrip()`\n", + "- `string.title()`\n", + "- `string.find()`\n", + "- `string.split(',', x)` (describe what 'x' does. It should be a number)\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8YAcMdHpnuKw" + }, + "source": [ + "## String formatting\n", + "We have seen a (not very convenient) way of building strings:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "pl5hfOYmnzF2" + }, + "outputs": [], + "source": [ + "name = 'Julian'\n", + "shoutout = 'Hey ' + name + '!'\n", + "print(shoutout)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SjWVj2R5n1XR" + }, + "source": [ + "Luckily, there is a better (easier, more flexible) way: `string.format()`:\n", + "- create a string with *placeholders*: `{}`\n", + "- fill these placeholders with some value" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "EQ9u1CX6n_TN" + }, + "outputs": [], + "source": [ + "shoutout_template = 'Hey {}!'\n", + "print(shoutout_template)\n", + "\n", + "filled_template = shoutout_template.format('Sheean')\n", + "print(filled_template)\n", + "\n", + "names = ['Julian', 'Sheean', 'Jelte', 'Mees']\n", + "for name in names:\n", + " print(shoutout_template.format(name))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "B1_gS-UaoEy0" + }, + "source": [ + "### Multiple placeholders\n", + "Not limited to one placeholder, you can have an arbitrary amount" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "8KMQfVXGoHq5" + }, + "outputs": [], + "source": [ + "shoutout_template = 'Hey {}! {}'\n", + "name = 'Julian'\n", + "followup = 'How are you?'\n", + "\n", + "print(shoutout_template.format(name, followup))\n", + "print(shoutout_template.format(name))\n", + "print(shoutout_template.format(name, followup, 'Where are you going?'))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3lMa3IDjoMZP" + }, + "source": [ + "### Named placeholders\n", + "Can also give placeholders names, and supply named parameters to `string.format()`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "re9x0TNLoM4Q" + }, + "outputs": [], + "source": [ + "shoutout_template = 'Hey {name}! {followup}'\n", + "julian = 'Julian'\n", + "question = 'How are you?'\n", + "\n", + "print(shoutout_template.format(name=julian, followup=question))\n", + "print(shoutout_template.format(followup=question, name=julian))\n", + "print(shoutout_template.format(followup=julian, name=question))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### F-strings\n", + "Similar to placeholders, expressions an also be directly combined within a string by putting `f` in front of a string:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "name = 'Sheean'\n", + "weeks = 2\n", + "text = f'Hi {name}, your next appointment is in {weeks * 7} days.'\n", + "\n", + "print(text)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "avcqgDAAoq-w" + }, + "source": [ + "## Exercise 9.3: String formatting\n", + "In each of the code blocks below, try to predict what will be printed, then run the code. If your guess was incorrect, try to figure out why the result is different. If your guess was correct, celebrate!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "BIa0VOX_owF4" + }, + "outputs": [], + "source": [ + "print('hey {}')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "81GUitOZo0l2" + }, + "outputs": [], + "source": [ + "print('hey {Julian}')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "96Dq4eSCpAji" + }, + "outputs": [], + "source": [ + "print('hey {}'.format('Julian'))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "h43fjzPco4V_" + }, + "outputs": [], + "source": [ + "print('hey {Julian}'.format('Julian'))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "wA18AIPKpFAH" + }, + "outputs": [], + "source": [ + "print('hey {name}'.format('Julian'))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "y5FcFvgypMfE" + }, + "source": [ + "## Next module\n", + "\n", + "[10 - Dictionaries](https://colab.research.google.com/drive/16mecEPkVGUBIFoHIALO7dE8qv7PynHL9)" + ] + } + ], + "metadata": { + "colab": { + "provenance": [], + "toc_visible": true + }, + "jupytext": { + "main_language": "python" + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/lessons/09 string manipulation.py b/lessons/09 string manipulation.py new file mode 100644 index 0000000..ba0247f --- /dev/null +++ b/lessons/09 string manipulation.py @@ -0,0 +1,268 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# name: python3 +# --- + +# %% [markdown] id="7_SeGArbg7Ou" +# # Module 9: String Manipulation +# +# ### CDH course "Programming in Python" +# +# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# +# Previous module: [8. Debugging](https://colab.research.google.com/drive/1yQskT6SyKvXtXewx5kCla2NOmasgP8Vi) +# +# ### This module +# +# - String utilities +# - Formatting strings + +# %% [markdown] id="oadzHVRYh1PN" +# ## String utilities +# Python provides functions for common string operations. When working with text, these are very useful. Try to learn them by heart! + +# %% [markdown] id="3KZGnQuWiM1X" +# ### Counting characters +# +# Strings are *iterables*, just like lists. We can use this information to quickly give us the length (number of characters) of a string: +# `len(string)` + +# %% id="u-gFos5wii5k" +print(len('Matilda')) +print(len('The Catcher in the Rye')) + +# %% [markdown] id="r0ZiGq36ipEo" +# ### Check for substrings +# Again wielding the knowledge about strings being *iterables*, we can use the `in` keyword to check if some sequence occurs in the string: +# `substring in string` + +# %% id="yOOOzh3WjCGc" +print('a' in 'Matilda') +print('til' in 'Matilda') +print('in' in 'The Catcher in the Rye') + +# %% [markdown] id="A4Wfxuj_jRAW" +# ### Casing +# Convert a string to lowercase or uppercase: +# +# `string.lower()` and `string.upper()` + +# %% id="uErzYzBHjbND" +print('Matilda'.upper()) +print('The Catcher in the Rye'.lower()) + +# %% [markdown] id="AsVvV-dtjxSD" +# ### Replacement +# Replace certain sequences with another sequence: +# +# `string.replace(replacement)` + +# %% id="2iIwX0phj4Wt" +catcher = 'The Catcher in the Rye' +replaced = catcher.replace('e', '3') +print(replaced) + +# optional parameter 'count' +replaced_max = catcher.replace('e', '3', 1) +print(replaced_max) + +# %% [markdown] id="OQ07Frl4j_H_" +# ### Converting to list +# There are two ways to convert a string into a list: +# - `list(string)` +# - `string.split(separator)` +# +# The latter is more flexible, because it allows you to choose on which character the string should be split. + +# %% id="xtsuwTrikrpk" +catcher = 'The Catcher in the Rye' +tabbed_catcher = catcher.replace(' ', '\t') # here we replace spaces by the tab character +sentence = 'This is some sentence that we wish to split into words.' + +# use list() +print(list(catcher)) + +# using string.split() +# default: split on any whitespace +print(catcher.split()) +print(tabbed_catcher.split()) +print(sentence.split()) + +# optional parameter: separator +list_string = 'jelte,julian,berit' +print(list_string.split(',')) + +# any separator works, separator is not included in the result: +print(list_string.split('e')) + +# %% [markdown] id="UM5FtDPBlBGx" +# ### Join an iterable into a string +# The reverse of splitting is ofcourse also possible: take each element of an *iterable* (such as a list), and glue them together into a string. You can choose which character to put between each element: +# +# `string.join(iterable)` + +# %% id="CqxglgPOlDfe" +words = ['The', 'Catcher', 'in', 'the', 'Rye'] + +# choose a string that should be put between the words +print(' '.join(words)) +print(','.join(words)) +print(''.join(words)) + +# %% id="z7p9kwjBlakE" +# split and join are opposites +sep = ' ' +title = 'The Catcher in the Rye' +split_title = title.split(sep) +join_title = sep.join(split_title) +assert join_title == title + +# %% [markdown] id="5hIj-tbVleEq" +# ## Exercise 9.1: String utilities +# In each of the code blocks below, try to predict what will be printed, then run the code. If your guess was incorrect, try to figure out why the result is different. If your guess was correct, celebrate! + +# %% id="02q-FgvVlxEj" +print(len('two')) + +# %% id="cvCrnnn9l-oH" +print(len('')) + +# %% id="UlWWF0k7mA62" +assert 'A' in 'Matilda' + +# %% id="Ui3gmvCNmHfB" +assert 'A' in 'Matilda'.upper() + +# %% id="1tDEnzrumNdO" +name = 'Matilda' +assert name.upper() == 'MATILDA' + +# %% id="BUpf6LglmZ4n" +name = 'Matilda' +assert name.upper().lower() == 'Matilda' + +# %% id="sgfEH2jImlwz" +print('Matilda'.replace('ilda', 'ild4')) + +# %% id="BEE94VVBmf7T" +print('Matilda'.replace('a', 4)) + +# %% id="BVKc0bQAnGYq" +list_of_words = ['I', 'ate', 'a', 'banana'] +sentence = 'I ate a banana' + +assert sentence.split() == list_of_words + +# %% id="R4U_8kVtmpHE" +list_of_words = ['I', 'ate', 'a', 'banana'] +sentence = 'I ate a banana' + +assert ''.join(list_of_words) == sentence + +# %% [markdown] id="mRATbQdclrcX" +# ## Exercise 9.2: Additional utilities +# For each of the string functions below, find out what they do. If they take any parameters, describe what these do: +# - `string.startswith()` +# - `string.endswith()` +# - `string.strip()` +# - `string.lstrip()` +# - `string.rstrip()` +# - `string.title()` +# - `string.find()` +# - `string.split(',', x)` (describe what 'x' does. It should be a number) +# +# + +# %% [markdown] id="8YAcMdHpnuKw" +# ## String formatting +# We have seen a (not very convenient) way of building strings: + +# %% id="pl5hfOYmnzF2" +name = 'Julian' +shoutout = 'Hey ' + name + '!' +print(shoutout) + +# %% [markdown] id="SjWVj2R5n1XR" +# Luckily, there is a better (easier, more flexible) way: `string.format()`: +# - create a string with *placeholders*: `{}` +# - fill these placeholders with some value + +# %% id="EQ9u1CX6n_TN" +shoutout_template = 'Hey {}!' +print(shoutout_template) + +filled_template = shoutout_template.format('Sheean') +print(filled_template) + +names = ['Julian', 'Sheean', 'Jelte', 'Mees'] +for name in names: + print(shoutout_template.format(name)) + +# %% [markdown] id="B1_gS-UaoEy0" +# ### Multiple placeholders +# Not limited to one placeholder, you can have an arbitrary amount + +# %% id="8KMQfVXGoHq5" +shoutout_template = 'Hey {}! {}' +name = 'Julian' +followup = 'How are you?' + +print(shoutout_template.format(name, followup)) +print(shoutout_template.format(name)) +print(shoutout_template.format(name, followup, 'Where are you going?')) + +# %% [markdown] id="3lMa3IDjoMZP" +# ### Named placeholders +# Can also give placeholders names, and supply named parameters to `string.format()`. + +# %% id="re9x0TNLoM4Q" +shoutout_template = 'Hey {name}! {followup}' +julian = 'Julian' +question = 'How are you?' + +print(shoutout_template.format(name=julian, followup=question)) +print(shoutout_template.format(followup=question, name=julian)) +print(shoutout_template.format(followup=julian, name=question)) + +# %% [markdown] +# ### F-strings +# Similar to placeholders, expressions an also be directly combined within a string by putting `f` in front of a string: + +# %% +name = 'Sheean' +weeks = 2 +text = f'Hi {name}, your next appointment is in {weeks * 7} days.' + +print(text) + +# %% [markdown] id="avcqgDAAoq-w" +# ## Exercise 9.3: String formatting +# In each of the code blocks below, try to predict what will be printed, then run the code. If your guess was incorrect, try to figure out why the result is different. If your guess was correct, celebrate! + +# %% id="BIa0VOX_owF4" +print('hey {}') + +# %% id="81GUitOZo0l2" +print('hey {Julian}') + +# %% id="96Dq4eSCpAji" +print('hey {}'.format('Julian')) + +# %% id="h43fjzPco4V_" +print('hey {Julian}'.format('Julian')) + +# %% id="wA18AIPKpFAH" +print('hey {name}'.format('Julian')) + +# %% [markdown] id="y5FcFvgypMfE" +# ## Next module +# +# [10 - Dictionaries](https://colab.research.google.com/drive/16mecEPkVGUBIFoHIALO7dE8qv7PynHL9) diff --git a/lessons/10 - Dictionaries.ipynb b/lessons/10 - Dictionaries.ipynb index 0c1eba8..7e37450 100644 --- a/lessons/10 - Dictionaries.ipynb +++ b/lessons/10 - Dictionaries.ipynb @@ -1 +1,556 @@ -{"cells":[{"cell_type":"markdown","metadata":{"id":"kjdcNtg3k8Aa"},"source":["# Dictionaries\n","\n","### CDH course \"Programming in Python\"\n","\n","[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n","\n","Previous module: [9. String manipulation](https://colab.research.google.com/drive/1Z7NNMxqHTSMoUadH3pVL6Bg6oii4qUoQ)\n","\n","### This module\n","\n","- Learn about _dictionaries_, a useful way of storing and looking up data"]},{"cell_type":"markdown","metadata":{"id":"CtgPyDQ6lkT4"},"source":["## What are dictionaries?\n","\n","We have already seen one type of data structure, the _list_. Lists store values in a specific _order_, and we can retrieve values by their position in the list."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"OKs8GP5zk9bM"},"outputs":[],"source":["fruits = ['apple', 'banana', 'orange']\n","\n","assert fruits is not ['banana', 'apple', 'orange'] # order matters!\n","\n","print(fruits[1])"]},{"cell_type":"markdown","metadata":{"id":"OOyTxS6_mv2o"},"source":["What if we want to store some extra information about each type of fruit? That is where dictionaries come in handy. Here I save the colour of each fruit."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"d0SFY-fil7-l"},"outputs":[],"source":["fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'}"]},{"cell_type":"markdown","metadata":{"id":"RiNPElB7nj2F"},"source":["Some vocabulary: `'apple'`, `'banana'` and `'orange'` are _keys_ of the dictionary, while `'red'`, `'yellow'` and `'orange'` are _values_. Keys and values come in pairs: `'red'` is the value for the key `'apple'`. We'll see what we can do with this in a bit.\n","\n","Dictionaries need not contain strings. The values of a dictionary can be anything you want. The keys are a bit more restricted: strings, numbers, and tuples are fine, but complex data structures like lists and other dictionaries are not."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"ao56gCSxnhkS"},"outputs":[],"source":["students_by_year = {2021: ['Julian', 'Jelte'], 2022: ['Julian', 'Jelte', 'Berit']}\n","\n","years_per_student = {'Jelte': [2021, 2022], 'Julian': [2021, 2022], 'Berit': 2022}\n","\n","# long dictionaries can be written on multiple lines, to keep it readable\n","dictionaries_by_topic = {\n"," 'fruit': fruit_colors,\n"," 'python course': [students_by_year, years_per_student]\n","}"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"JgfCNNoHn3uR"},"outputs":[],"source":["# this will be rejected\n","shared_interests = {['Julian', 'Jelte']: ['cats', 'programming'], \n"," ['Jelte', 'Berit']: 'music'}"]},{"cell_type":"markdown","metadata":{"id":"1tYs_3xBqx2e"},"source":["The keys of a dictionary should be **unique**. If you try to write something with a key that already exists, it will overwrite the old value."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"TykfEB8iq4MQ"},"outputs":[],"source":["unambiguous_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'}\n","ambiguous_colors = {'apple': 'green', 'apple': 'red', 'banana': 'yellow', 'orange': 'orange'}\n","assert ambiguous_colors == unambiguous_colors\n","\n","print(ambiguous_colors)"]},{"cell_type":"markdown","metadata":{"id":"_3J4oaBAsTGa"},"source":["Order does not matter for dictionaries!"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"pK0eYwsasBJ9"},"outputs":[],"source":["fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'}\n","fruit_colors_2 = {'banana': 'yellow', 'apple': 'red', 'orange': 'orange'}\n","\n","assert fruit_colors == fruit_colors_2"]},{"cell_type":"markdown","metadata":{"id":"L56hdp03r9Q6"},"source":["You can make emtpy dictionaries too."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"da-Re3sPsyYS"},"outputs":[],"source":["empty = {} # a dictionary with no keys / values\n","\n","new_dict = dict() # constructor function"]},{"cell_type":"markdown","metadata":{"id":"nksJpi6mqgXY"},"source":["### Accessing\n","\n","With lists and tuples, we access elements with the index number. In dictionaries we access them by the key."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"SzxfzTANsjGN"},"outputs":[],"source":["fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'}\n","students_by_year = {2021: ['Julian', 'Jelte'], 2022: ['Julian', 'Jelte', 'Berit']}\n","\n","print(fruit_colors['apple'])\n","print(students_by_year[2021])"]},{"cell_type":"markdown","metadata":{"id":"yL7NrEzwtm_r"},"source":["### Assiging / reassigning\n","\n","You can also assign or reassign values by the key. If the key is _not_ in the dictionary yet, it creates the key-value pair."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"HEOgD62xtPQj"},"outputs":[],"source":["fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'}\n","fruit_colors['apple'] = 'red or green'\n","print(fruit_colors)\n","\n","fruit_colors['kiwi'] = 'green'\n","print(fruit_colors)\n","\n","# be aware that misspelling a key creates a new entry\n","fruit_colors['bnaana'] = 'yellwo'\n","print(fruit_colors)"]},{"cell_type":"markdown","metadata":{"id":"zeJU8wt9uY2V"},"source":["### Updating\n","Dictionaries can be *updated* with another dictionary. This is a way to quickly add multiple key-value pairs:\n","\n","- `dictionary.update(other_dictionary)`\n","- this happens *in-place*, the function doesn't return anything"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"pyxjqy31uWEs"},"outputs":[],"source":["students_by_year = {2021: ['Julian', 'Jelte'], 2022: ['Julian', 'Jelte', 'Berit']}\n","more_students = {2023: ['Sheean', 'Mees', 'Luka'], 2024: ['Hopefully a lot']}\n","\n","print(students_by_year.update(more_students))\n","print(students_by_year)"]},{"cell_type":"markdown","metadata":{"id":"FvGfwcVjuwIY"},"source":["### Deleting\n","To delete a key-value pair: `del dictionary[key]`"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"72V_U4tUupnw"},"outputs":[],"source":["fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'}\n","del fruit_colors['orange']\n","print(fruit_colors)"]},{"cell_type":"markdown","metadata":{"id":"-kH7Vw3WvgFj"},"source":["### Iterables\n","Dictionaries have a few useful functions:\n","- `dictionary.keys()` returns a list of all keys\n","- `dictionary.values()` returns a list of all values\n","- `dictionary.items()` returns a list of tuples: `(key, value)`"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"sNwzKYOAvk-d"},"outputs":[],"source":["fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'}\n","\n","# which types of fruit do we have information about?\n","print(fruit_colors.keys())\n","\n","# which colors do we have?\n","print(fruit_colors.values())\n","\n","# what's in the dictionary?\n","print(fruit_colors.items())"]},{"cell_type":"markdown","metadata":{"id":"3oBW4bxgv5JV"},"source":["### Checking contents\n","We can use the iterables above to check the contents of a dictionary."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"ESzsmJCXv7pl"},"outputs":[],"source":["fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'}\n","\n","print('banana' in fruit_colors.keys())\n","print('yellow' in fruit_colors.values())\n","\n","# shortcut for checking key:\n","print('banana' in fruit_colors)\n","\n","# check an entire key-value pair:\n","print(('banana', 'yellow') in fruit_colors.items())"]},{"cell_type":"markdown","metadata":{"id":"x2nJsta6wRGQ"},"source":["### Alternative ways to access elements\n","- `dictionary.get(key)` returns value at key\n"," - returns `None` if they key does not exist\n"," - optional parameter: default return value\n","- `dictionary.pop(key)` returns value of key, and deletes key.\n"," - raises an error if the key does not exist\n"," - optional parameter: default return value"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"11lfiYMiwTct"},"outputs":[],"source":["fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'}\n","\n","print(fruit_colors.get('banana'))\n","print(fruit_colors.get('bnaana'))\n","print(fruit_colors.get('bnaana', 'yellow?'))\n","\n","print(fruit_colors.pop('banana'))\n","print(fruit_colors)\n","print(fruit_colors.pop('banana', 'wait, what was banana again?'))"]},{"cell_type":"markdown","metadata":{"id":"1ezL-XhlvHBq"},"source":["## Exercise 10.1: Dictionaries"]},{"cell_type":"markdown","metadata":{"id":"rxcz60KQximW"},"source":["1 . Below are two dictionaries containing information about different types of fruit. Print a nice message about each fruit stating its colour and price. For example, _An apple is red and costs € 2.50_, etc."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"yn89oAAZu33C"},"outputs":[],"source":["fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'}\n","fruit_prices = {'apple': 2.50, 'banana': 2.10, 'orange': 1.50}\n","\n","# your code here..."]},{"cell_type":"markdown","metadata":{"id":"Gtp5V9dE0LxK"},"source":["2 . Here is a longer lists of fruit colours. Write a function `count_fruits` which gets gets a colour as input and returns the number of fruits that have that colour (according to `lots_of_fruit`)."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"S7gCNyLCxdrO"},"outputs":[],"source":["lots_of_fruit = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange',\n"," 'cucumber': 'green', 'kiwi': 'green', 'strawberry': 'red',\n"," 'pineapple': 'yellow','blackberry': 'black', 'cherry': 'red',\n"," 'gooseberry': 'green', 'raspberry': 'red', 'mandarin': 'orange',\n"," 'lemon': 'yellow', 'lime': 'green'}"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"nScDQipK35qN"},"outputs":[],"source":["\n","# your code here...\n","\n","# let's see if it works!\n","assert count_fruits('red') == 4\n","assert count_fruits('lavender') == 0"]},{"cell_type":"markdown","metadata":{"id":"-Qp6R3Kp3GId"},"source":["3 . The list `fruit_basket` contains a bunch of fruits. Can you make a dictionary `fruit_counts` which gives the amount for each fruit in `fruit_basket`? (Do not count the fruits by hand!)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"awf-lLQO3N1U"},"outputs":[],"source":["fruit_basket = ['apple', 'banana', 'banana', 'banana', 'apple', 'orange',\n"," 'orange', 'grape', 'grape', 'grape', 'grape', 'grape', 'grape',\n"," 'grape', 'grape', 'grape', 'pear', 'apple', 'strawberry',\n"," 'strawberry', 'strawberry', 'orange']"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"MDpIZpbm3-BG"},"outputs":[],"source":["# your code here..\n","\n","\n","# let's see if it works!\n","assert fruit_counts['apple'] == 3\n"]},{"cell_type":"markdown","metadata":{"id":"h-kJhTO951pc"},"source":["4 . Here is a different list, which contains the words in a sentence. Can you use your code above to make a dictionary `word_counts` telling us how often each word occurs? (Tip: if you need to do very similar tasks, make a function!)\n","\n","Write a function that takes a dictionary like `word_counts` tells us the most commonly occuring item and the count. Note that there can be multiple items that occurred the most."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"mdNug4ct5645"},"outputs":[],"source":["# the variable sent0 contains the first sentence of The Catcher in the Rye\n","# split into single words\n","sent0 = ['If', 'you', 'really', 'want', 'to', 'hear', 'about', 'it,', 'the', \n"," 'first', 'thing', 'you’ll', 'probably', 'want', 'to', 'know', 'is', \n"," 'where', 'I', 'was', 'born,', 'and', 'what', 'my', 'lousy', 'childhood', \n"," 'was', 'like,', 'and', 'how', 'my', 'parents', 'were', 'occupied', \n"," 'and', 'all', 'before', 'they', 'had', 'me,', 'and', 'all', 'that', \n"," 'David', 'Copperfield', 'kind', 'of', 'crap,', 'but', 'I', 'don’t', \n"," 'feel', 'like', 'going', 'into', 'it,', 'if', 'you', 'want', \n"," 'to', 'know', 'the', 'truth.']"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"XGY3qSEk6B9j"},"outputs":[],"source":["# your code here..."]}],"metadata":{"colab":{"provenance":[]},"kernelspec":{"display_name":"Python 3","name":"python3"},"language_info":{"name":"python"}},"nbformat":4,"nbformat_minor":0} +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "kjdcNtg3k8Aa" + }, + "source": [ + "# Dictionaries\n", + "\n", + "### CDH course \"Programming in Python\"\n", + "\n", + "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "\n", + "Previous module: [9. String manipulation](https://colab.research.google.com/drive/1Z7NNMxqHTSMoUadH3pVL6Bg6oii4qUoQ)\n", + "\n", + "### This module\n", + "\n", + "- Learn about _dictionaries_, a useful way of storing and looking up data" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "CtgPyDQ6lkT4" + }, + "source": [ + "## What are dictionaries?\n", + "\n", + "We have already seen one type of data structure, the _list_. Lists store values in a specific _order_, and we can retrieve values by their position in the list." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "OKs8GP5zk9bM" + }, + "outputs": [], + "source": [ + "fruits = ['apple', 'banana', 'orange']\n", + "\n", + "assert fruits is not ['banana', 'apple', 'orange'] # order matters!\n", + "\n", + "print(fruits[1])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "OOyTxS6_mv2o" + }, + "source": [ + "What if we want to store some extra information about each type of fruit? That is where dictionaries come in handy. Here I save the colour of each fruit." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "d0SFY-fil7-l" + }, + "outputs": [], + "source": [ + "fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "RiNPElB7nj2F" + }, + "source": [ + "Some vocabulary: `'apple'`, `'banana'` and `'orange'` are _keys_ of the dictionary, while `'red'`, `'yellow'` and `'orange'` are _values_. Keys and values come in pairs: `'red'` is the value for the key `'apple'`. We'll see what we can do with this in a bit.\n", + "\n", + "Dictionaries need not contain strings. The values of a dictionary can be anything you want. The keys are a bit more restricted: strings, numbers, and tuples are fine, but complex data structures like lists and other dictionaries are not." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ao56gCSxnhkS" + }, + "outputs": [], + "source": [ + "students_by_year = {2021: ['Julian', 'Jelte'], 2022: ['Julian', 'Jelte', 'Berit']}\n", + "\n", + "years_per_student = {'Jelte': [2021, 2022], 'Julian': [2021, 2022], 'Berit': 2022}\n", + "\n", + "# long dictionaries can be written on multiple lines, to keep it readable\n", + "dictionaries_by_topic = {\n", + " 'fruit': fruit_colors,\n", + " 'python course': [students_by_year, years_per_student]\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "JgfCNNoHn3uR" + }, + "outputs": [], + "source": [ + "# this will be rejected\n", + "shared_interests = {['Julian', 'Jelte']: ['cats', 'programming'], \n", + " ['Jelte', 'Berit']: 'music'}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1tYs_3xBqx2e" + }, + "source": [ + "The keys of a dictionary should be **unique**. If you try to write something with a key that already exists, it will overwrite the old value." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "TykfEB8iq4MQ" + }, + "outputs": [], + "source": [ + "unambiguous_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'}\n", + "ambiguous_colors = {'apple': 'green', 'apple': 'red', 'banana': 'yellow', 'orange': 'orange'}\n", + "assert ambiguous_colors == unambiguous_colors\n", + "\n", + "print(ambiguous_colors)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "_3J4oaBAsTGa" + }, + "source": [ + "Order does not matter for dictionaries!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "pK0eYwsasBJ9" + }, + "outputs": [], + "source": [ + "fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'}\n", + "fruit_colors_2 = {'banana': 'yellow', 'apple': 'red', 'orange': 'orange'}\n", + "\n", + "assert fruit_colors == fruit_colors_2" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "L56hdp03r9Q6" + }, + "source": [ + "You can make emtpy dictionaries too." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "da-Re3sPsyYS" + }, + "outputs": [], + "source": [ + "empty = {} # a dictionary with no keys / values\n", + "\n", + "new_dict = dict() # constructor function" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "nksJpi6mqgXY" + }, + "source": [ + "### Accessing\n", + "\n", + "With lists and tuples, we access elements with the index number. In dictionaries we access them by the key." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "SzxfzTANsjGN" + }, + "outputs": [], + "source": [ + "fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'}\n", + "students_by_year = {2021: ['Julian', 'Jelte'], 2022: ['Julian', 'Jelte', 'Berit']}\n", + "\n", + "print(fruit_colors['apple'])\n", + "print(students_by_year[2021])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "yL7NrEzwtm_r" + }, + "source": [ + "### Assiging / reassigning\n", + "\n", + "You can also assign or reassign values by the key. If the key is _not_ in the dictionary yet, it creates the key-value pair." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "HEOgD62xtPQj" + }, + "outputs": [], + "source": [ + "fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'}\n", + "fruit_colors['apple'] = 'red or green'\n", + "print(fruit_colors)\n", + "\n", + "fruit_colors['kiwi'] = 'green'\n", + "print(fruit_colors)\n", + "\n", + "# be aware that misspelling a key creates a new entry\n", + "fruit_colors['bnaana'] = 'yellwo'\n", + "print(fruit_colors)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zeJU8wt9uY2V" + }, + "source": [ + "### Updating\n", + "Dictionaries can be *updated* with another dictionary. This is a way to quickly add multiple key-value pairs:\n", + "\n", + "- `dictionary.update(other_dictionary)`\n", + "- this happens *in-place*, the function doesn't return anything" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "pyxjqy31uWEs" + }, + "outputs": [], + "source": [ + "students_by_year = {2021: ['Julian', 'Jelte'], 2022: ['Julian', 'Jelte', 'Berit']}\n", + "more_students = {2023: ['Sheean', 'Mees', 'Luka'], 2024: ['Hopefully a lot']}\n", + "\n", + "print(students_by_year.update(more_students))\n", + "print(students_by_year)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "FvGfwcVjuwIY" + }, + "source": [ + "### Deleting\n", + "To delete a key-value pair: `del dictionary[key]`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "72V_U4tUupnw" + }, + "outputs": [], + "source": [ + "fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'}\n", + "del fruit_colors['orange']\n", + "print(fruit_colors)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-kH7Vw3WvgFj" + }, + "source": [ + "### Iterables\n", + "Dictionaries have a few useful functions:\n", + "- `dictionary.keys()` returns a list of all keys\n", + "- `dictionary.values()` returns a list of all values\n", + "- `dictionary.items()` returns a list of tuples: `(key, value)`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "sNwzKYOAvk-d" + }, + "outputs": [], + "source": [ + "fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'}\n", + "\n", + "# which types of fruit do we have information about?\n", + "print(fruit_colors.keys())\n", + "\n", + "# which colors do we have?\n", + "print(fruit_colors.values())\n", + "\n", + "# what's in the dictionary?\n", + "print(fruit_colors.items())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3oBW4bxgv5JV" + }, + "source": [ + "### Checking contents\n", + "We can use the iterables above to check the contents of a dictionary." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ESzsmJCXv7pl" + }, + "outputs": [], + "source": [ + "fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'}\n", + "\n", + "print('banana' in fruit_colors.keys())\n", + "print('yellow' in fruit_colors.values())\n", + "\n", + "# shortcut for checking key:\n", + "print('banana' in fruit_colors)\n", + "\n", + "# check an entire key-value pair:\n", + "print(('banana', 'yellow') in fruit_colors.items())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "x2nJsta6wRGQ" + }, + "source": [ + "### Alternative ways to access elements\n", + "- `dictionary.get(key)` returns value at key\n", + " - returns `None` if they key does not exist\n", + " - optional parameter: default return value\n", + "- `dictionary.pop(key)` returns value of key, and deletes key.\n", + " - raises an error if the key does not exist\n", + " - optional parameter: default return value" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "11lfiYMiwTct" + }, + "outputs": [], + "source": [ + "fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'}\n", + "\n", + "print(fruit_colors.get('banana'))\n", + "print(fruit_colors.get('bnaana'))\n", + "print(fruit_colors.get('bnaana', 'yellow?'))\n", + "\n", + "print(fruit_colors.pop('banana'))\n", + "print(fruit_colors)\n", + "print(fruit_colors.pop('banana', 'wait, what was banana again?'))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1ezL-XhlvHBq" + }, + "source": [ + "## Exercise 10.1: Dictionaries" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rxcz60KQximW" + }, + "source": [ + "1 . Below are two dictionaries containing information about different types of fruit. Print a nice message about each fruit stating its colour and price. For example, _An apple is red and costs € 2.50_, etc." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "yn89oAAZu33C" + }, + "outputs": [], + "source": [ + "fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'}\n", + "fruit_prices = {'apple': 2.50, 'banana': 2.10, 'orange': 1.50}\n", + "\n", + "# your code here..." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Gtp5V9dE0LxK" + }, + "source": [ + "2 . Here is a longer lists of fruit colours. Write a function `count_fruits` which gets gets a colour as input and returns the number of fruits that have that colour (according to `lots_of_fruit`)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "S7gCNyLCxdrO" + }, + "outputs": [], + "source": [ + "lots_of_fruit = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange',\n", + " 'cucumber': 'green', 'kiwi': 'green', 'strawberry': 'red',\n", + " 'pineapple': 'yellow','blackberry': 'black', 'cherry': 'red',\n", + " 'gooseberry': 'green', 'raspberry': 'red', 'mandarin': 'orange',\n", + " 'lemon': 'yellow', 'lime': 'green'}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "nScDQipK35qN" + }, + "outputs": [], + "source": [ + "\n", + "# your code here...\n", + "\n", + "# let's see if it works!\n", + "assert count_fruits('red') == 4\n", + "assert count_fruits('lavender') == 0" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-Qp6R3Kp3GId" + }, + "source": [ + "3 . The list `fruit_basket` contains a bunch of fruits. Can you make a dictionary `fruit_counts` which gives the amount for each fruit in `fruit_basket`? (Do not count the fruits by hand!)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "awf-lLQO3N1U" + }, + "outputs": [], + "source": [ + "fruit_basket = ['apple', 'banana', 'banana', 'banana', 'apple', 'orange',\n", + " 'orange', 'grape', 'grape', 'grape', 'grape', 'grape', 'grape',\n", + " 'grape', 'grape', 'grape', 'pear', 'apple', 'strawberry',\n", + " 'strawberry', 'strawberry', 'orange']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "MDpIZpbm3-BG", + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "# your code here..\n", + "\n", + "\n", + "# let's see if it works!\n", + "assert fruit_counts['apple'] == 3" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "h-kJhTO951pc" + }, + "source": [ + "4 . Here is a different list, which contains the words in a sentence. Can you use your code above to make a dictionary `word_counts` telling us how often each word occurs? (Tip: if you need to do very similar tasks, make a function!)\n", + "\n", + "Write a function that takes a dictionary like `word_counts` tells us the most commonly occuring item and the count. Note that there can be multiple items that occurred the most." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "mdNug4ct5645" + }, + "outputs": [], + "source": [ + "# the variable sent0 contains the first sentence of The Catcher in the Rye\n", + "# split into single words\n", + "sent0 = ['If', 'you', 'really', 'want', 'to', 'hear', 'about', 'it,', 'the', \n", + " 'first', 'thing', 'you’ll', 'probably', 'want', 'to', 'know', 'is', \n", + " 'where', 'I', 'was', 'born,', 'and', 'what', 'my', 'lousy', 'childhood', \n", + " 'was', 'like,', 'and', 'how', 'my', 'parents', 'were', 'occupied', \n", + " 'and', 'all', 'before', 'they', 'had', 'me,', 'and', 'all', 'that', \n", + " 'David', 'Copperfield', 'kind', 'of', 'crap,', 'but', 'I', 'don’t', \n", + " 'feel', 'like', 'going', 'into', 'it,', 'if', 'you', 'want', \n", + " 'to', 'know', 'the', 'truth.']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "XGY3qSEk6B9j" + }, + "outputs": [], + "source": [ + "# your code here..." + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "jupytext": { + "main_language": "python" + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/lessons/10 - Dictionaries.py b/lessons/10 - Dictionaries.py new file mode 100644 index 0000000..3ff4590 --- /dev/null +++ b/lessons/10 - Dictionaries.py @@ -0,0 +1,265 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# name: python3 +# --- + +# %% [markdown] id="kjdcNtg3k8Aa" +# # Dictionaries +# +# ### CDH course "Programming in Python" +# +# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# +# Previous module: [9. String manipulation](https://colab.research.google.com/drive/1Z7NNMxqHTSMoUadH3pVL6Bg6oii4qUoQ) +# +# ### This module +# +# - Learn about _dictionaries_, a useful way of storing and looking up data + +# %% [markdown] id="CtgPyDQ6lkT4" +# ## What are dictionaries? +# +# We have already seen one type of data structure, the _list_. Lists store values in a specific _order_, and we can retrieve values by their position in the list. + +# %% id="OKs8GP5zk9bM" +fruits = ['apple', 'banana', 'orange'] + +assert fruits is not ['banana', 'apple', 'orange'] # order matters! + +print(fruits[1]) + +# %% [markdown] id="OOyTxS6_mv2o" +# What if we want to store some extra information about each type of fruit? That is where dictionaries come in handy. Here I save the colour of each fruit. + +# %% id="d0SFY-fil7-l" +fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'} + +# %% [markdown] id="RiNPElB7nj2F" +# Some vocabulary: `'apple'`, `'banana'` and `'orange'` are _keys_ of the dictionary, while `'red'`, `'yellow'` and `'orange'` are _values_. Keys and values come in pairs: `'red'` is the value for the key `'apple'`. We'll see what we can do with this in a bit. +# +# Dictionaries need not contain strings. The values of a dictionary can be anything you want. The keys are a bit more restricted: strings, numbers, and tuples are fine, but complex data structures like lists and other dictionaries are not. + +# %% id="ao56gCSxnhkS" +students_by_year = {2021: ['Julian', 'Jelte'], 2022: ['Julian', 'Jelte', 'Berit']} + +years_per_student = {'Jelte': [2021, 2022], 'Julian': [2021, 2022], 'Berit': 2022} + +# long dictionaries can be written on multiple lines, to keep it readable +dictionaries_by_topic = { + 'fruit': fruit_colors, + 'python course': [students_by_year, years_per_student] +} + +# %% id="JgfCNNoHn3uR" +# this will be rejected +shared_interests = {['Julian', 'Jelte']: ['cats', 'programming'], + ['Jelte', 'Berit']: 'music'} + +# %% [markdown] id="1tYs_3xBqx2e" +# The keys of a dictionary should be **unique**. If you try to write something with a key that already exists, it will overwrite the old value. + +# %% id="TykfEB8iq4MQ" +unambiguous_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'} +ambiguous_colors = {'apple': 'green', 'apple': 'red', 'banana': 'yellow', 'orange': 'orange'} +assert ambiguous_colors == unambiguous_colors + +print(ambiguous_colors) + +# %% [markdown] id="_3J4oaBAsTGa" +# Order does not matter for dictionaries! + +# %% id="pK0eYwsasBJ9" +fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'} +fruit_colors_2 = {'banana': 'yellow', 'apple': 'red', 'orange': 'orange'} + +assert fruit_colors == fruit_colors_2 + +# %% [markdown] id="L56hdp03r9Q6" +# You can make emtpy dictionaries too. + +# %% id="da-Re3sPsyYS" +empty = {} # a dictionary with no keys / values + +new_dict = dict() # constructor function + +# %% [markdown] id="nksJpi6mqgXY" +# ### Accessing +# +# With lists and tuples, we access elements with the index number. In dictionaries we access them by the key. + +# %% id="SzxfzTANsjGN" +fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'} +students_by_year = {2021: ['Julian', 'Jelte'], 2022: ['Julian', 'Jelte', 'Berit']} + +print(fruit_colors['apple']) +print(students_by_year[2021]) + +# %% [markdown] id="yL7NrEzwtm_r" +# ### Assiging / reassigning +# +# You can also assign or reassign values by the key. If the key is _not_ in the dictionary yet, it creates the key-value pair. + +# %% id="HEOgD62xtPQj" +fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'} +fruit_colors['apple'] = 'red or green' +print(fruit_colors) + +fruit_colors['kiwi'] = 'green' +print(fruit_colors) + +# be aware that misspelling a key creates a new entry +fruit_colors['bnaana'] = 'yellwo' +print(fruit_colors) + +# %% [markdown] id="zeJU8wt9uY2V" +# ### Updating +# Dictionaries can be *updated* with another dictionary. This is a way to quickly add multiple key-value pairs: +# +# - `dictionary.update(other_dictionary)` +# - this happens *in-place*, the function doesn't return anything + +# %% id="pyxjqy31uWEs" +students_by_year = {2021: ['Julian', 'Jelte'], 2022: ['Julian', 'Jelte', 'Berit']} +more_students = {2023: ['Sheean', 'Mees', 'Luka'], 2024: ['Hopefully a lot']} + +print(students_by_year.update(more_students)) +print(students_by_year) + +# %% [markdown] id="FvGfwcVjuwIY" +# ### Deleting +# To delete a key-value pair: `del dictionary[key]` + +# %% id="72V_U4tUupnw" +fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'} +del fruit_colors['orange'] +print(fruit_colors) + +# %% [markdown] id="-kH7Vw3WvgFj" +# ### Iterables +# Dictionaries have a few useful functions: +# - `dictionary.keys()` returns a list of all keys +# - `dictionary.values()` returns a list of all values +# - `dictionary.items()` returns a list of tuples: `(key, value)` + +# %% id="sNwzKYOAvk-d" +fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'} + +# which types of fruit do we have information about? +print(fruit_colors.keys()) + +# which colors do we have? +print(fruit_colors.values()) + +# what's in the dictionary? +print(fruit_colors.items()) + +# %% [markdown] id="3oBW4bxgv5JV" +# ### Checking contents +# We can use the iterables above to check the contents of a dictionary. + +# %% id="ESzsmJCXv7pl" +fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'} + +print('banana' in fruit_colors.keys()) +print('yellow' in fruit_colors.values()) + +# shortcut for checking key: +print('banana' in fruit_colors) + +# check an entire key-value pair: +print(('banana', 'yellow') in fruit_colors.items()) + +# %% [markdown] id="x2nJsta6wRGQ" +# ### Alternative ways to access elements +# - `dictionary.get(key)` returns value at key +# - returns `None` if they key does not exist +# - optional parameter: default return value +# - `dictionary.pop(key)` returns value of key, and deletes key. +# - raises an error if the key does not exist +# - optional parameter: default return value + +# %% id="11lfiYMiwTct" +fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'} + +print(fruit_colors.get('banana')) +print(fruit_colors.get('bnaana')) +print(fruit_colors.get('bnaana', 'yellow?')) + +print(fruit_colors.pop('banana')) +print(fruit_colors) +print(fruit_colors.pop('banana', 'wait, what was banana again?')) + +# %% [markdown] id="1ezL-XhlvHBq" +# ## Exercise 10.1: Dictionaries + +# %% [markdown] id="rxcz60KQximW" +# 1 . Below are two dictionaries containing information about different types of fruit. Print a nice message about each fruit stating its colour and price. For example, _An apple is red and costs € 2.50_, etc. + +# %% id="yn89oAAZu33C" +fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'} +fruit_prices = {'apple': 2.50, 'banana': 2.10, 'orange': 1.50} + +# your code here... + +# %% [markdown] id="Gtp5V9dE0LxK" +# 2 . Here is a longer lists of fruit colours. Write a function `count_fruits` which gets gets a colour as input and returns the number of fruits that have that colour (according to `lots_of_fruit`). + +# %% id="S7gCNyLCxdrO" +lots_of_fruit = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange', + 'cucumber': 'green', 'kiwi': 'green', 'strawberry': 'red', + 'pineapple': 'yellow','blackberry': 'black', 'cherry': 'red', + 'gooseberry': 'green', 'raspberry': 'red', 'mandarin': 'orange', + 'lemon': 'yellow', 'lime': 'green'} + +# %% id="nScDQipK35qN" + +# your code here... + +# let's see if it works! +assert count_fruits('red') == 4 +assert count_fruits('lavender') == 0 + +# %% [markdown] id="-Qp6R3Kp3GId" +# 3 . The list `fruit_basket` contains a bunch of fruits. Can you make a dictionary `fruit_counts` which gives the amount for each fruit in `fruit_basket`? (Do not count the fruits by hand!) + +# %% id="awf-lLQO3N1U" +fruit_basket = ['apple', 'banana', 'banana', 'banana', 'apple', 'orange', + 'orange', 'grape', 'grape', 'grape', 'grape', 'grape', 'grape', + 'grape', 'grape', 'grape', 'pear', 'apple', 'strawberry', + 'strawberry', 'strawberry', 'orange'] + +# %% id="MDpIZpbm3-BG" +# your code here.. + + +# let's see if it works! +assert fruit_counts['apple'] == 3 + + +# %% [markdown] id="h-kJhTO951pc" +# 4 . Here is a different list, which contains the words in a sentence. Can you use your code above to make a dictionary `word_counts` telling us how often each word occurs? (Tip: if you need to do very similar tasks, make a function!) +# +# Write a function that takes a dictionary like `word_counts` tells us the most commonly occuring item and the count. Note that there can be multiple items that occurred the most. + +# %% id="mdNug4ct5645" +# the variable sent0 contains the first sentence of The Catcher in the Rye +# split into single words +sent0 = ['If', 'you', 'really', 'want', 'to', 'hear', 'about', 'it,', 'the', + 'first', 'thing', 'you’ll', 'probably', 'want', 'to', 'know', 'is', + 'where', 'I', 'was', 'born,', 'and', 'what', 'my', 'lousy', 'childhood', + 'was', 'like,', 'and', 'how', 'my', 'parents', 'were', 'occupied', + 'and', 'all', 'before', 'they', 'had', 'me,', 'and', 'all', 'that', + 'David', 'Copperfield', 'kind', 'of', 'crap,', 'but', 'I', 'don’t', + 'feel', 'like', 'going', 'into', 'it,', 'if', 'you', 'want', + 'to', 'know', 'the', 'truth.'] + +# %% id="XGY3qSEk6B9j" +# your code here... diff --git a/lessons/11 working with files.ipynb b/lessons/11 working with files.ipynb index b6b8560..b9e630e 100644 --- a/lessons/11 working with files.ipynb +++ b/lessons/11 working with files.ipynb @@ -1 +1,400 @@ -{"cells":[{"cell_type":"markdown","metadata":{"id":"uHYIr4R9dWyQ"},"source":["# Module 11: Working with files\n","\n","### CDH course \"Programming in Python\"\n","\n","[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n","\n","Previous module: [10. dictionaries](https://colab.research.google.com/drive/16mecEPkVGUBIFoHIALO7dE8qv7PynHL9)\n","\n","### This module\n","\n","- Reading files\n","- Writing files\n","- Use existing code"]},{"cell_type":"markdown","metadata":{"id":"MgaAMPDbdiTr"},"source":["## Reading files\n","It is not very desirable to keep data in your code. It clutters up, making the code harder to read. Also, we want to work with some interchangeable data, that exists outside of our code.\n","\n","Python provides an easy way to work with files."]},{"cell_type":"markdown","metadata":{"id":"p9157kmNeAfI"},"source":["### Read a file\n","- Provide a path to the file\n","- Open the file `open()`, this provides a *File Object*\n","- Work with the contents"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"kup_j23UeW71"},"outputs":[],"source":["# Make sure the path is available to Python\n","# In notebooks, this means we need to upload the file\n","PATH = 'countries.csv'\n","\n","file = open(PATH)\n","type(file)\n","\n","contents = file.read()\n","print(type(contents))\n","print(contents)\n","\n","lines = file.readlines()\n","print(type(lines))\n","print(lines)\n"]},{"cell_type":"markdown","metadata":{"id":"96NQljy2ez-E"},"source":["### Sidenote: constants\n","Isn't the convention that variables are lowercased? \n","Not if they are *constants*:\n","- Variables that do not change during the progam\n","- Often represent some 'setting' or 'constant value'\n"," - `PATH = 'countries.csv'`\n"," - `PI = 3.14159265358979323846264338327950288419716939937510582097` \n"]},{"cell_type":"markdown","metadata":{"id":"ZlbuEqiKftk-"},"source":["## Closing files\n","- If a file is opened, it should also be closed (like a freezer)\n","- If not, nasty things can happen\n","- Call `.close()` on the File Object"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"SzytvDuCgBiE"},"outputs":[],"source":["# Manually close\n","file = open(PATH)\n","data = file.read()\n","file.close()"]},{"cell_type":"markdown","metadata":{"id":"9V_7uhwdgDXA"},"source":["- Even better: use a *context manager*\n"," - opens the file\n"," - assigns it to a variable\n"," - and closes once you are done with it\n","- Use the `with as ` syntax"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"a-Q7R8x4gby6"},"outputs":[],"source":["with open(PATH) as file:\n"," data = file.read()\n","\n","print(data)"]},{"cell_type":"markdown","metadata":{"id":"bbtncTLHhUaq"},"source":["## Writing files\n","- Provide `mode` parameter with value `'w'` (write) to open a file in write mode\n"," - note that `mode` has a default value: `'r'` (read)\n","- On the file object, use `file.write()`\n","- If you want to write on a new line, make sure to append `'\\n'` (newline character)\n","- Alternatively, use `print(, file=)`"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"lSmlpO2-hn6j"},"outputs":[],"source":["WRITE_PATH = 'myfile.txt'\n","\n","with open(WRITE_PATH, mode='w') as file:\n"," file.write('Hello, world!\\n')\n"," file.write('This is a new file created by write.')\n","\n","with open(WRITE_PATH, mode='w') as file:\n"," print('Hello world!', file=file)\n"," print('This is a new file created by print.', file=file)"]},{"cell_type":"markdown","metadata":{"id":"QVQvj4ztkhmj"},"source":["Append to an existing file by using `mode='a'` (append)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"NhD03dHgklBT"},"outputs":[],"source":["with open(WRITE_PATH, mode='a') as file:\n"," print('Another bit of text', file=file)"]},{"cell_type":"markdown","metadata":{"id":"YC0q3Z4HiM5Z"},"source":["## Exercise 11.1 - Files\n","In the code block below, try to predict what will be printed, then run the code."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"vZtR4360i7kh"},"outputs":[],"source":["PATH = 'sample_data/california_housing_test.csv'\n","file = open(PATH)\n","print(file)\n","print(file.read())\n","print(file.readlines())\n","file.close()\n","print(file)"]},{"cell_type":"markdown","metadata":{"id":"BXOkW2J7ldYK"},"source":["Read the file `'sampledata/california_housing_test.csv'`, and print the first row containing data as a list.\n","\n","Are the data in the correct format? Try to fix it if not."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"eP_TfHcmlsce"},"outputs":[],"source":[]},{"cell_type":"markdown","metadata":{"id":"Phv4ave0mh5e"},"source":["## Using existing code\n","- People before you have worked with CSV files\n","- Someone probably wrote and published their code\n","- Available in *packages* and *libraries*\n","- `csv` *module* is available in the [standard library](https://docs.python.org/3.7/library/index.html). This library is *always* available in Python.\n","- Python is known for its strong ecosystem of packages\n","- Many libraries from outside the standard library are preinstalled in these Notebooks"]},{"cell_type":"markdown","metadata":{"id":"TBgRxECDnTKW"},"source":["### Importing existing code\n","Use the `import ` syntax to use an entire Python file in your own code"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"fXIBTOFDnVFU"},"outputs":[],"source":["import csv\n","print(csv.reader)"]},{"cell_type":"markdown","metadata":{"id":"NxBU-xkZneT6"},"source":["Alternatively, only import the parts your are interested in"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"Ykrw__BXni5z"},"outputs":[],"source":["from csv import reader\n","print(reader)"]},{"cell_type":"markdown","metadata":{"id":"dMIZr2o_nlch"},"source":["## Working with the `csv` module\n","The `reader` function has some nice utilities that make it easy to read csv files"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"05Smz2DbntnT"},"outputs":[],"source":["import csv\n","\n","def read_data():\n"," with open(PATH) as csv_file:\n"," reader = csv.reader(csv_file, delimiter=',')\n"," return list(reader)\n","\n","data = read_data()\n","print(data[0])\n","print(data[1])\n","print(data[10][3])"]},{"cell_type":"markdown","metadata":{"id":"TRHR32Bfn9tO"},"source":["Alternatively, use the `csv.DictReader` to read the file as a list of dictionaries."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"T67PXssOoICa"},"outputs":[],"source":["import csv\n","\n","def read_data_dict():\n"," with open(PATH) as csv_file:\n"," reader = csv.DictReader(csv_file, delimiter=',')\n"," return list(reader)\n","\n","data = read_data_dict()\n","print(data[0])\n","print(data[1])\n","print(data[10]['name'])"]},{"cell_type":"markdown","metadata":{"id":"h-mOS3pwoc1O"},"source":["## Exercise 11.2: Bonus - working with csv datasets\n","> 💡 If you wish to perform an analysis on your own dataset in the upcoming sessions, make sure you complete this exercise. The code you write here can be reused in your own analysis.\n","\n","1. Upload your own dataset, or use `sample_data/california_housing_test.csv`\n"," 1. Read the data from the CSV file\n"," 2. Write functions that take the data as a parameter, and return the following items:\n"," - The last row\n"," - The last column\n"," - The first row. If this is a header row, try if you can come up with a way to separate it from the data rows.\n"," - Calculate the *harmonic mean* of a column containing numbers. Search the [standard library](https://docs.python.org/3.7/library/index.html) for a a relevant module containing the code to do so.\n"," 3. Add an extra column to the data (think of something that makes sense). Write the whole dataset to a new csv file."]}],"metadata":{"colab":{"authorship_tag":"ABX9TyMB+UscaVxkKRr/LCtKDZnL","provenance":[],"toc_visible":true},"kernelspec":{"display_name":"Python 3","name":"python3"},"language_info":{"name":"python"}},"nbformat":4,"nbformat_minor":0} +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "uHYIr4R9dWyQ" + }, + "source": [ + "# Module 11: Working with files\n", + "\n", + "### CDH course \"Programming in Python\"\n", + "\n", + "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "\n", + "Previous module: [10. dictionaries](https://colab.research.google.com/drive/16mecEPkVGUBIFoHIALO7dE8qv7PynHL9)\n", + "\n", + "### This module\n", + "\n", + "- Reading files\n", + "- Writing files\n", + "- Use existing code" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "MgaAMPDbdiTr" + }, + "source": [ + "## Reading files\n", + "It is not very desirable to keep data in your code. It clutters up, making the code harder to read. Also, we want to work with some interchangeable data, that exists outside of our code.\n", + "\n", + "Python provides an easy way to work with files." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "p9157kmNeAfI" + }, + "source": [ + "### Read a file\n", + "- Provide a path to the file\n", + "- Open the file `open()`, this provides a *File Object*\n", + "- Work with the contents" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "kup_j23UeW71", + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "# Make sure the path is available to Python\n", + "# In notebooks, this means we need to upload the file\n", + "PATH = 'countries.csv'\n", + "\n", + "file = open(PATH)\n", + "type(file)\n", + "\n", + "contents = file.read()\n", + "print(type(contents))\n", + "print(contents)\n", + "\n", + "lines = file.readlines()\n", + "print(type(lines))\n", + "print(lines)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "96NQljy2ez-E" + }, + "source": [ + "### Sidenote: constants\n", + "Isn't the convention that variables are lowercased? \n", + "Not if they are *constants*:\n", + "- Variables that do not change during the progam\n", + "- Often represent some 'setting' or 'constant value'\n", + " - `PATH = 'countries.csv'`\n", + " - `PI = 3.14159265358979323846264338327950288419716939937510582097` \n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ZlbuEqiKftk-" + }, + "source": [ + "## Closing files\n", + "- If a file is opened, it should also be closed (like a freezer)\n", + "- If not, nasty things can happen\n", + "- Call `.close()` on the File Object" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "SzytvDuCgBiE" + }, + "outputs": [], + "source": [ + "# Manually close\n", + "file = open(PATH)\n", + "data = file.read()\n", + "file.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9V_7uhwdgDXA" + }, + "source": [ + "- Even better: use a *context manager*\n", + " - opens the file\n", + " - assigns it to a variable\n", + " - and closes once you are done with it\n", + "- Use the `with as ` syntax" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "a-Q7R8x4gby6" + }, + "outputs": [], + "source": [ + "with open(PATH) as file:\n", + " data = file.read()\n", + "\n", + "print(data)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "bbtncTLHhUaq" + }, + "source": [ + "## Writing files\n", + "- Provide `mode` parameter with value `'w'` (write) to open a file in write mode\n", + " - note that `mode` has a default value: `'r'` (read)\n", + "- On the file object, use `file.write()`\n", + "- If you want to write on a new line, make sure to append `'\\n'` (newline character)\n", + "- Alternatively, use `print(, file=)`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "lSmlpO2-hn6j" + }, + "outputs": [], + "source": [ + "WRITE_PATH = 'myfile.txt'\n", + "\n", + "with open(WRITE_PATH, mode='w') as file:\n", + " file.write('Hello, world!\\n')\n", + " file.write('This is a new file created by write.')\n", + "\n", + "with open(WRITE_PATH, mode='w') as file:\n", + " print('Hello world!', file=file)\n", + " print('This is a new file created by print.', file=file)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "QVQvj4ztkhmj" + }, + "source": [ + "Append to an existing file by using `mode='a'` (append)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "NhD03dHgklBT" + }, + "outputs": [], + "source": [ + "with open(WRITE_PATH, mode='a') as file:\n", + " print('Another bit of text', file=file)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "YC0q3Z4HiM5Z" + }, + "source": [ + "## Exercise 11.1 - Files\n", + "In the code block below, try to predict what will be printed, then run the code." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "vZtR4360i7kh" + }, + "outputs": [], + "source": [ + "PATH = 'sample_data/california_housing_test.csv'\n", + "file = open(PATH)\n", + "print(file)\n", + "print(file.read())\n", + "print(file.readlines())\n", + "file.close()\n", + "print(file)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BXOkW2J7ldYK" + }, + "source": [ + "Read the file `'sampledata/california_housing_test.csv'`, and print the first row containing data as a list.\n", + "\n", + "Are the data in the correct format? Try to fix it if not." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "eP_TfHcmlsce" + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Phv4ave0mh5e" + }, + "source": [ + "## Using existing code\n", + "- People before you have worked with CSV files\n", + "- Someone probably wrote and published their code\n", + "- Available in *packages* and *libraries*\n", + "- `csv` *module* is available in the [standard library](https://docs.python.org/3.7/library/index.html). This library is *always* available in Python.\n", + "- Python is known for its strong ecosystem of packages\n", + "- Many libraries from outside the standard library are preinstalled in these Notebooks" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "TBgRxECDnTKW" + }, + "source": [ + "### Importing existing code\n", + "Use the `import ` syntax to use an entire Python file in your own code" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "fXIBTOFDnVFU" + }, + "outputs": [], + "source": [ + "import csv\n", + "print(csv.reader)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "NxBU-xkZneT6" + }, + "source": [ + "Alternatively, only import the parts your are interested in" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Ykrw__BXni5z" + }, + "outputs": [], + "source": [ + "from csv import reader\n", + "print(reader)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "dMIZr2o_nlch" + }, + "source": [ + "## Working with the `csv` module\n", + "The `reader` function has some nice utilities that make it easy to read csv files" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "05Smz2DbntnT" + }, + "outputs": [], + "source": [ + "import csv\n", + "\n", + "def read_data():\n", + " with open(PATH) as csv_file:\n", + " reader = csv.reader(csv_file, delimiter=',')\n", + " return list(reader)\n", + "\n", + "data = read_data()\n", + "print(data[0])\n", + "print(data[1])\n", + "print(data[10][3])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "TRHR32Bfn9tO" + }, + "source": [ + "Alternatively, use the `csv.DictReader` to read the file as a list of dictionaries." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "T67PXssOoICa" + }, + "outputs": [], + "source": [ + "import csv\n", + "\n", + "def read_data_dict():\n", + " with open(PATH) as csv_file:\n", + " reader = csv.DictReader(csv_file, delimiter=',')\n", + " return list(reader)\n", + "\n", + "data = read_data_dict()\n", + "print(data[0])\n", + "print(data[1])\n", + "print(data[10]['name'])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "h-mOS3pwoc1O" + }, + "source": [ + "## Exercise 11.2: Bonus - working with csv datasets\n", + "> 💡 If you wish to perform an analysis on your own dataset in the upcoming sessions, make sure you complete this exercise. The code you write here can be reused in your own analysis.\n", + "\n", + "1. Upload your own dataset, or use `sample_data/california_housing_test.csv`\n", + " 1. Read the data from the CSV file\n", + " 2. Write functions that take the data as a parameter, and return the following items:\n", + " - The last row\n", + " - The last column\n", + " - The first row. If this is a header row, try if you can come up with a way to separate it from the data rows.\n", + " - Calculate the *harmonic mean* of a column containing numbers. Search the [standard library](https://docs.python.org/3.7/library/index.html) for a a relevant module containing the code to do so.\n", + " 3. Add an extra column to the data (think of something that makes sense). Write the whole dataset to a new csv file." + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyMB+UscaVxkKRr/LCtKDZnL", + "provenance": [], + "toc_visible": true + }, + "jupytext": { + "main_language": "python" + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/lessons/11 working with files.py b/lessons/11 working with files.py new file mode 100644 index 0000000..c4a70ad --- /dev/null +++ b/lessons/11 working with files.py @@ -0,0 +1,207 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# name: python3 +# --- + +# %% [markdown] id="uHYIr4R9dWyQ" +# # Module 11: Working with files +# +# ### CDH course "Programming in Python" +# +# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# +# Previous module: [10. dictionaries](https://colab.research.google.com/drive/16mecEPkVGUBIFoHIALO7dE8qv7PynHL9) +# +# ### This module +# +# - Reading files +# - Writing files +# - Use existing code + +# %% [markdown] id="MgaAMPDbdiTr" +# ## Reading files +# It is not very desirable to keep data in your code. It clutters up, making the code harder to read. Also, we want to work with some interchangeable data, that exists outside of our code. +# +# Python provides an easy way to work with files. + +# %% [markdown] id="p9157kmNeAfI" +# ### Read a file +# - Provide a path to the file +# - Open the file `open()`, this provides a *File Object* +# - Work with the contents + +# %% id="kup_j23UeW71" +# Make sure the path is available to Python +# In notebooks, this means we need to upload the file +PATH = 'countries.csv' + +file = open(PATH) +type(file) + +contents = file.read() +print(type(contents)) +print(contents) + +lines = file.readlines() +print(type(lines)) +print(lines) + + +# %% [markdown] id="96NQljy2ez-E" +# ### Sidenote: constants +# Isn't the convention that variables are lowercased? +# Not if they are *constants*: +# - Variables that do not change during the progam +# - Often represent some 'setting' or 'constant value' +# - `PATH = 'countries.csv'` +# - `PI = 3.14159265358979323846264338327950288419716939937510582097` +# + +# %% [markdown] id="ZlbuEqiKftk-" +# ## Closing files +# - If a file is opened, it should also be closed (like a freezer) +# - If not, nasty things can happen +# - Call `.close()` on the File Object + +# %% id="SzytvDuCgBiE" +# Manually close +file = open(PATH) +data = file.read() +file.close() + +# %% [markdown] id="9V_7uhwdgDXA" +# - Even better: use a *context manager* +# - opens the file +# - assigns it to a variable +# - and closes once you are done with it +# - Use the `with as ` syntax + +# %% id="a-Q7R8x4gby6" +with open(PATH) as file: + data = file.read() + +print(data) + +# %% [markdown] id="bbtncTLHhUaq" +# ## Writing files +# - Provide `mode` parameter with value `'w'` (write) to open a file in write mode +# - note that `mode` has a default value: `'r'` (read) +# - On the file object, use `file.write()` +# - If you want to write on a new line, make sure to append `'\n'` (newline character) +# - Alternatively, use `print(, file=)` + +# %% id="lSmlpO2-hn6j" +WRITE_PATH = 'myfile.txt' + +with open(WRITE_PATH, mode='w') as file: + file.write('Hello, world!\n') + file.write('This is a new file created by write.') + +with open(WRITE_PATH, mode='w') as file: + print('Hello world!', file=file) + print('This is a new file created by print.', file=file) + +# %% [markdown] id="QVQvj4ztkhmj" +# Append to an existing file by using `mode='a'` (append) + +# %% id="NhD03dHgklBT" +with open(WRITE_PATH, mode='a') as file: + print('Another bit of text', file=file) + +# %% [markdown] id="YC0q3Z4HiM5Z" +# ## Exercise 11.1 - Files +# In the code block below, try to predict what will be printed, then run the code. + +# %% id="vZtR4360i7kh" +PATH = 'sample_data/california_housing_test.csv' +file = open(PATH) +print(file) +print(file.read()) +print(file.readlines()) +file.close() +print(file) + +# %% [markdown] id="BXOkW2J7ldYK" +# Read the file `'sampledata/california_housing_test.csv'`, and print the first row containing data as a list. +# +# Are the data in the correct format? Try to fix it if not. + +# %% id="eP_TfHcmlsce" + +# %% [markdown] id="Phv4ave0mh5e" +# ## Using existing code +# - People before you have worked with CSV files +# - Someone probably wrote and published their code +# - Available in *packages* and *libraries* +# - `csv` *module* is available in the [standard library](https://docs.python.org/3.7/library/index.html). This library is *always* available in Python. +# - Python is known for its strong ecosystem of packages +# - Many libraries from outside the standard library are preinstalled in these Notebooks + +# %% [markdown] id="TBgRxECDnTKW" +# ### Importing existing code +# Use the `import ` syntax to use an entire Python file in your own code + +# %% id="fXIBTOFDnVFU" +import csv +print(csv.reader) + +# %% [markdown] id="NxBU-xkZneT6" +# Alternatively, only import the parts your are interested in + +# %% id="Ykrw__BXni5z" +from csv import reader +print(reader) + +# %% [markdown] id="dMIZr2o_nlch" +# ## Working with the `csv` module +# The `reader` function has some nice utilities that make it easy to read csv files + +# %% id="05Smz2DbntnT" +import csv + +def read_data(): + with open(PATH) as csv_file: + reader = csv.reader(csv_file, delimiter=',') + return list(reader) + +data = read_data() +print(data[0]) +print(data[1]) +print(data[10][3]) + +# %% [markdown] id="TRHR32Bfn9tO" +# Alternatively, use the `csv.DictReader` to read the file as a list of dictionaries. + +# %% id="T67PXssOoICa" +import csv + +def read_data_dict(): + with open(PATH) as csv_file: + reader = csv.DictReader(csv_file, delimiter=',') + return list(reader) + +data = read_data_dict() +print(data[0]) +print(data[1]) +print(data[10]['name']) + +# %% [markdown] id="h-mOS3pwoc1O" +# ## Exercise 11.2: Bonus - working with csv datasets +# > 💡 If you wish to perform an analysis on your own dataset in the upcoming sessions, make sure you complete this exercise. The code you write here can be reused in your own analysis. +# +# 1. Upload your own dataset, or use `sample_data/california_housing_test.csv` +# 1. Read the data from the CSV file +# 2. Write functions that take the data as a parameter, and return the following items: +# - The last row +# - The last column +# - The first row. If this is a header row, try if you can come up with a way to separate it from the data rows. +# - Calculate the *harmonic mean* of a column containing numbers. Search the [standard library](https://docs.python.org/3.7/library/index.html) for a a relevant module containing the code to do so. +# 3. Add an extra column to the data (think of something that makes sense). Write the whole dataset to a new csv file. diff --git a/lessons/Bonus Exercise data.ipynb b/lessons/Bonus Exercise data.ipynb index c2633a8..66dbf00 100644 --- a/lessons/Bonus Exercise data.ipynb +++ b/lessons/Bonus Exercise data.ipynb @@ -1 +1,551 @@ -{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"provenance":[],"authorship_tag":"ABX9TyPlTZNDb2yTw7vB3bqNYG48"},"kernelspec":{"name":"python3","display_name":"Python 3"},"language_info":{"name":"python"}},"cells":[{"cell_type":"code","source":["catcher_chapter1 = '''\n","If you really want to hear about it, the first thing you’ll probably want to know is where I was born, and what my lousy childhood was like, and how my parents were occupied and all before they had me, and all that David Copperfield kind of crap, but I don’t feel like going into it, if you want to know the truth. In the first place, that stuff bores me, and in the second place, my parents would have two hemorrhages apiece if I told anything pretty personal about them. They’re quitee touchy about anything like that, especially my father. They’re nice and all - I’m not saying that - but they’re also touchy as hell. Besides, I’m not going to tell you my whole goodam autobiography or anything. I’ll just tell you about this madman stuff that happened to me last Christmas just before I got pretty run-down and had to come out and take it easy. I mean that’s all I told D.B. about, and he’s my brother and all. He’s in Hollywood. That isn’t too far from this crumby place, and he comes over and visits me practically every week end. He’s going to drive me home when I go home next month maybe. He just got a Jaguar. One of those little English jobs that can do around two hundred miles an hour. It cost him damn near four thousand bucks. He’s got a lot of dough, now. He didn’t use to. He used to be just a regular writer, when he was home. He wrote thizs terrific book of short stories, The Secret Goldfish, in case you never heard of him. The best one in it was «‘The Secret Goldfish.’ It was about this little kid that wouldn’t let anybody look at his goldfish because he’d bought it with his own money. It killed me. Now he’s out in Hollywood, D.B., being a prostitute. If there’s one thing I hate, it’s the movies. Don’t even mention them to me.\n","Where I want to start is the day I left Pencey Prep. Pencey Prep is the school that’s in Agertown, Pennsylvania. You probably heard of it. You’ve probably seen the ads, anyway. They advertise in about a thousand magazines, always showing some hot-shot guy on a horse jumping over a fence. Like as if all you ever did at Pencey was play polo all the time. I never even once saw a horse anywhere near the place. And underneath the guy on the horse’s picture, it always says: ‘Since 1888 we have been molding boys into splendid, clear-thinking young men’. Strictly for the birds. They don’t do any damn more molding at Pencey than they do at any other school. And I didn’t know anybody there that was splendid and clear-thinking and all. Maybe two guys. If that many. And they probably came to Pencey that way.\n","Anyway, it was the Saturday of the football game with Saxon Hall. The game with Saxon Hall was supposed to be a very big deal around Pencey. It was the last game of the year, and you were supposed to commit suicide ou something if old Pencey didn’t win. I remember around three o’clock that afternoon I was standing way the hell on top of Thomsen Hill, right next to this crazy cannon that was in the Revolutionary War and all. You could see the whole field from there, and you could see the two teams bashing each other all over the place. You couldn’t see the granstand too hot, but you could hear them all yelling, deep and terrific on the Pencey side, because practically the whole school except me was there, and scrawny and faggy on the Saxon Hall side, because the visiting team hardly ever brought many people with them.\n","There were never many girls at all at the football games. Only seniors were allowed to bring girls with them. It was a terrible school, no matter how you looked at it. I like to be somewhere at least where you can see a few girls around once in a while, even if they’re only scratching their arms or blowing their noses or even just giggling or something. Old Selma Thurmer - she was the headmaster’s master - showed up at the games quite often, but she wasn’t exactly the type that drove you mad with desire. She was a pretty nice girl, though, I sat next to her once in the bus from Agerstown and we sort of struck up a conversation. I liked her. She had a big nose and her nails were all all bitten down and bleedy-looking and she had on those damn falsies that point all over the place, but you felt sort of sorry for her. Wha I liked about her, she didn’t give you a lot of horse manure about what a great guy her father was. She probably knew what a phony slob h e was.\n","The reason I was standing way up on Thomsen Hill, instead of down at the game, was because I’d just got back from New York with the fencing team. I was the goddam manager of the fencing team. Very big deal. We’d gone in to Newyork that morning for this fencing meet with McBurney School. Only, we didn’t have the meet. I left all the foils and equipment and stuff on the goddam subway. It wasn’t all my fault. I had to keep getting up to look at this map, so we’d know where to get off. So we got back to Pencey around two-thirty instead of around dinnertime. The whole team ostracized me the whole way back on the train. It was pretty funny, in a way.\n","The other reason I wasn’t down at the game was because I was on my way to say good-by to old Spencer, my history teacher. He had the grippe, and I figured I probably wouldn’t see him again till Christmas vacation started. He wrote me this note saying he wanted to see me before I went home. He knew I wasn’t coming back to Pencey.\n","I forgot to tell you about that. They kicked me out. I wasn’t supposed to come back after Christmas vacation, on account of I was flunking four subjects and not applying myself and all. They gave me frequent warning to start applying myself - especially around mid-terms, when my parents came up for a conference with old Thurmer - but I didn’t do it. So I got the ax. They give guys the ax quite frequently at Pencey. It has a very good academic rating, Pencey. It really does.\n","Anyway, it was December and all, and it was cold as a witch’s teat, especially on top of that stupid hill. I only had on my reversible and no gloves or anything. sThe week before that, somebody’d stolen my camel’s-hair coat right out of my room, with my fur-lined gloves right in the pocket and all. Pencey was full of crooks. Quite a few guys came from these very wealthy families, but it was full of crooks anyway. The more expensive a school is, the more crooks it has - I’m not kidding. Anyway, I kept standing next to that crazy cannon, looking down at the game and freezing my ass off. Only, I wasn’t watching the game too much. What I was really hanging around for, I was trying to feel some kind of a good-by. I mean I’ve left schools and places I didn’t even know I was leaving them. I don’t care if it’s a sad good-by or a bad good-by, but when I leave a place I live I like to know I’m leaving it. If you don’t, you feel even worse.\n","I was lucky. All of a sudden I thought of something that helped make me know I was getting the hell out. I suddenly remembered this time, in around October, that I and Robert Tichener and Paul Campbell were chucking a football around, in front of the academic building. there were nice guys, especially Tichener. It was just before dinner and it was getting pretty dark out, but we kept chucking the ball around anyway. It kept getting darker and darker, and we could hardly see the ball anyway. It kept getting darker and darker, and we could hardly see the ball any more, but we didn’t want to stop doing what we were doing. Finally we had to. This teacher that taught biology, Mr Zambesi, stuck his head out of this window in the academic building and told us to go back to the dorm and get ready for dinner. If I get a chance to remember that kind of stuff, I can get a good-by when I need one - at least, most of the time I can. As soon as I got it, I turned around and started running down the other side of the hill, toward old Spencer’s house. He didn’t live on the campus. He lived on Anthony Wayne Avenue.\n","I ran all the way to the main gate, and then I waited a second till I got my breath. I have no wind, if you want to know the truth. I’m quite a heavy smoker, for one thing - that is, I used to be. They made me cut it out. Another thing, I grew six and a half inches last year. That’s also how I practically got t.b. and came out here for all these goddam checkups and stuff. I’m pretty healthy, though.\n","Anyway, as soon as I got my breath back I ran accross Route 204. It was icy as hell and I damn near fell down. I don’t know what I was running for - I guess I just felt like it. After I got accross the road, I felt like I was sort of disappearing. It was that kind of a crazy afternoon, terricfically cold, and no sun out or anything, and you felt like you were disappearing every time you crossed a road.\n","Boy, I rang that doorbell fast when I got to old Spencer’s house. I was really frozen. My ears were hurting and I could hardly move my fingers at all. ‘C’mon, c’mon,’ I said right out loud, almost, ‘somebody open the door.’ Finally old Mrs Spencer opened it. They didn’t have a maid or anything, and they always opened the door themselves. They didn’t have too much dough.\n","‘Holden!’ Mrs Spencer said. ‘How lovely to see you! Come in, dear! Are youfrozen to death?’ I think she was glad to see me. She liked me. At least, I think she did.\n","Boy, did I get in that house fast. ‘How are you, Mrs Spencer?’ I said. ‘How’s Mr Spencer?’\n","‘Let me take your coat, dear,’ she said. She didn’t hear me ask her how Mr Spencer was. She was sort of deaf.\n","She hung up my coat in the hall closet, and I sort of brushed my hair back with my hand. I wear a crew cut quite frequently and I never have to comb it much. ‘How’ve you been, Mrs Spencer?’ I said again, only louder, so she’d hear me.\n","‘I’ve been just fine, Holden.’ She closed the closet door. ‘How have you been?’ The way she asked me, I knew right away old Spencer’d told her I’d been kicked out.\n","‘Fine,’ I said. ‘How’s Mr Spencer? He over his grippe yet?’\n","‘Over it! Holden, he’s behaving like a perfect - I don’t know what... He’s in his room, dear. Go right in.’\n","'''\n","\n","matilda_chapter1 = '''It's a funny thing about mothers and fathers. Even when their\n","own child is the most disgusting little blister you could ever\n","imagine, they still think that he or she is wonderful.\n","Some parents go further. They become so blinded by\n","adoration they manage to convince themselves their child has\n","qualities of genius.\n","Well, there is nothing very wrong with all this. It's the way\n","of the world. It is only when the parents begin telling us about\n","the brilliance of their own revolting offspring, that we start\n","shouting, \"Bring us a basin! We're going to be sick!\"\n","\n","School teachers suffer a good deal from having to listen to\n","this sort of twaddle from proud parents, but they usually get\n","their own back when the time comes to write the end-of-term\n","reports. If I were a teacher I would cook up some real\n","scorchers for the children of doting parents. \"Your son\n","Maximilian\", I would write, \"is a total wash-out. I hope you\n","have a family business you can push him into when he leaves\n","school because he sure as heck won't get a job anywhere else.\"\n","Or if I were feeling lyrical that day, I might write, \"It is a\n","curious truth that grasshoppers have their hearing-organs in \n","the sides of the abdomen. Your daughter Vanessa, judging by\n","what she's learnt this term, has no hearing-organs at all.\"\n","\n","I might even delve deeper into natural history and say,\n","\"The periodical cicada spends six years as a grub\n","underground, and no more than six days as a free creature of\n","sunlight and air. Your son Wilfred has spent six years as a\n","grub in this school and we are still waiting for him to emerge\n","from the chrysalis.\" A particularly poisonous little girl might\n","sting me into saying, \"Fiona has the same glacial beauty as an\n","iceberg, but unlike the iceberg she has absolutely nothing\n","below the surface.\" I\n","think I might enjoy writing end-of-term reports for the\n","stinkers in my class. But enough of that. We have to get on.\n","Occasionally one comes across parents who take the\n","opposite line, who show no interest at all in their children,\n","and these of course are far worse than the doting ones. Mr\n","and Mrs Wormwood were two such parents. They had a son\n","called Michael and a daughter called Matilda, and the parents\n","looked upon Matilda in particular as nothing more than a scab. A scab is something\n","you have to put up with until the time c\n","when you can pick it off and flick it away.\n","omes \n","Mr and Mrs Wormwood looked forward enormously to the\n","time when they could pick their little daughter off and flick\n","her away, preferably into the next county or even further than\n","that.\n","It is bad enough when parents treat ordinary children as\n","though they were scabs and bunions, but it becomes\n","somehow a lot worse when the child in\n","question is extraordinary, and by that I\n","mean sensitive and brilliant. Matilda was\n","both of these things, but above all she was\n","brilliant. Her mind was so nimble and she\n","was so quick to learn that her ability should\n","have been obvious even to the most half-witted of parents.\n","But Mr and Mrs Wormwood were both so gormless and so\n","wrapped up in their own silly little lives that they failed to\n","notice anything unusual about their daughter. To tell the\n","truth, I doubt they would have noticed had she crawled into\n","the house with a broken leg.\n","Matilda's brother Michael was a perfectly normal boy, but\n","the sister, as I said, was something to make your eyes pop. By\n","the age of one and a half her speech was perfect and she\n","knew as many words as most grown-ups. The parents, instead \n","of applauding her, called her a noisy chatterbox and told her\n","sharply that small girls should be seen and not heard.\n","By the time she was three, Matilda had taught herself to\n","read by studying newspapers and magazines that lay around\n","the house. At the age of four, she could read fast and well and\n","she naturally began hankering after books. The only book in\n","the whole of this enlightened household was something called\n","Easy Cooking belonging to her mother, and when she had\n","read this from cover to cover and had learnt all the recipes by\n","heart, she decided she wanted something more interesting.\n","\n","\"Daddy,\" she said, \"do you think you could buy me a\n","book?\"\n","\"A book?\" he said. \"What d'you want a flaming book for?\"\n","\"To read, Daddy.\"\n","\"What's wrong with the telly, for heaven's sake? We've got a\n","lovely telly with a twelve-inch screen and now you come\n","asking for a book! You're getting spoiled, my girl!\"\n","Nearly every weekday afternoon Matilda was left alone in\n","the house. Her brother (five years older than her) went to\n","school. Her father went to work and her mother went out\n","playing bingo in a town eight miles away. Mrs Wormwood\n","was hooked on bingo and played it five afternoons a week. On \n","the afternoon of the day when her father had refused to buy\n","her a book, Matilda set out all by herself to walk to the public\n","library in the village. When she arrived, she introduced\n","herself to the librarian, Mrs Phelps. She asked if she might sit\n","awhile and read a book. Mrs Phelps, slightly taken aback at\n","the arrival of such a tiny girl unacccompanied by a parent,\n","nevertheless told her she was very welcome.\n","\"Where are the children's books please?\" Matilda asked.\n","\"They're over there on those lower shelves,\" Mrs Phelps\n","told her. \"Would you like me to help you find a nice one with\n","lots of pictures in it?\"\n","\"No, thank you,\" Matilda said. \"I'm sure I can manage.\"\n","From then on, every afternoon, as soon as her mother had\n","left for bingo, Matilda would toddle down to the library. The\n","walk took only ten minutes and this allowed her two glorious\n","hours sitting quietly by herself in a cosy corner devouring one\n","book after another. When she had read every single children's\n","book in the place, she started wandering round in search of\n","something else.\n","Mrs Phelps, who had been watching her with fascination\n","for the past few weeks, now got up from her desk and went\n","over to her. \"Can I help you, Matilda?\" she asked. \n","\"I'm wondering what to read next,\" Matilda said. \"I've\n","finished all the children's books.\"\n","\"You mean you've looked at the pictures?\"\n","\"Yes, but I've read the books as well.\"\n","Mrs Phelps looked down at Matilda from her great height\n","and Matilda looked right back up at her.\n","\"I thought some were very poor,\" Matilda said, \"but others\n","were lovely. I liked The Secret Garden best of all. It was full of\n","mystery. The mystery of the room behind the closed door and\n","the mystery of the garden behind the big wall.\"\n","Mrs Phelps was stunned. ''Exactly how old are you,\n","Matilda?\" she asked.\n","\"Four years and three months,\" Matilda said.\n","Mrs Phelps was more stunned than ever, but she had the\n","sense not to show it. \"What sort of a book would you like to\n","read next?\" she asked.\n","Matilda said, \"I would like a really good one that grownups read. A famous one. I don't know any names.\"\n","Mrs Phelps looked along the shelves, taking her time. She\n","didn't quite know what to bring out. How, she asked herself,\n","does one choose a famous grown-up book for a four-year-old\n","girl? Her first thought was to pick a young teenager's\n","romance of the kind that is written for fifteen-year-old \n","schoolgirls, but for some reason she found herself instinctively walking past that particular shelf.\n","\"Try this,\" she said at last. \"It's very famous and very good.\n","If it's too long for you, just let me know and I'll find\n","something shorter and a bit easier.\"\n","\"Great Expectations,\" Matilda read, \"by Charles Dickens.\n","I'd love to try it.\"\n","I must be mad, Mrs Phelps told herself, but to Matilda she\n","said, \"Of course you may try it.\"\n","Over the next few afternoons Mrs Phelps could hardly take\n","her eyes from the small girl sitting for hour after hour in the\n","big armchair at the far end of the room with the book on her\n","lap. It was necessary to rest it on the lap because it was too\n","heavy for her to hold up, which meant she had to sit leaning\n","forward in order to read. And a strange sight it was, this tiny\n","dark-haired person sitting there with her feet nowhere near\n","touching the floor, totally absorbed in the wonderful\n","adventures of Pip and old Miss Havisham and her cobwebbed\n","house and by the spell of magic that Dickens the great storyteller had woven with his words. The only movement from\n","the reader was the lifting of the hand every now and then to\n","turn over a page, and Mrs Phelps always felt sad when the \n","time came for her to cross the floor and say; \"It's ten to five,\n","Matilda.\"\n","During the first week of Matilda's visits Mrs Phelps had\n","said to her, \"Does your mother walk you down here every day\n","and then take you home?\"\n","\"My mother goes to Aylesbury every afternoon to play\n","bingo,\" Matilda had said. \"She doesn't know I come here.\"\n","\"But that's surely not right,\" Mrs Phelps said. \"I think you'd\n","better ask her.\"\n","\"I'd rather not,\" Matilda said. \"She doesn't encourage\n","reading books. Nor does my father.\"\n","\"But what do they expect you to do every afternoon in an\n","empty house?\"\n","\"Just mooch around and watch the telly.\"\n","\"I see.\"\n","\"She doesn't really care what I do,\" Matilda said a little\n","sadly.\n","Mrs Phelps was concerned about the child's safety on the\n","walk through the fairly busy village High Street and the\n","crossing of the road, but she decided not to interfere.\n","Within a week, Matilda had finished Great Expectations\n","which in that edition contained four hundred and eleven \n","pages. \"I loved it,\" she said to Mrs Phelps. \"Has Mr Dickens\n","written any others?\"\n","\"A great number,\" said the astounded Mrs Phelps. \"Shall I\n","choose you another?\"\n","Over the next six months, under Mrs Phelps's\n","watchful and compassionate eye, Matilda read the following\n","books:\n","\n","Nicholas Nickleby by Charles Dickens\n","Oliver Twist by Charles Dickens\n","Jane Eyre by Charlotte Bronte\n","Pride and Prejudice by Jane Austen\n","Tess of the D'Urbervilles by Thomas Hardy\n","Gone to Earth by Mary Webb\n","Kim by Rudyard Kipling\n","The Invisible Man by H. G. Wells\n","The Old Man and the Sea by Ernest Hemingway\n","The Sound and the Fury by William Faulkner\n","The Grapes of Wrath by John Steinbeck\n","The Good Companions by J. B. Priestley\n","Brighton Rock by Graham Greene\n","Animal Farm by George Orwell\n"," \n","It was a formidable list and by now Mrs Phelps was filled\n","with wonder and excitement, but it was probably a good thing\n","that she did not allow herself to be completely carried away\n","by it all. Almost anyone else witnessing the achievements of\n","this small child would have been tempted to make a great fuss\n","and shout the news all over the village and beyond, but not so\n","Mrs Phelps. She was someone who minded her own business\n","and had long since discovered it was seldom worth while to\n","interfere with other people's children.\n","\"Mr Hemingway says a lot of things I don't understand,\"\n","Matilda said to her. \"Especially about men and women. But I\n","loved it all the same. The way he tells it I feel I am right there\n","on the spot watching it all happen.\"\n","''A fine writer will always make you feel that,\" Mrs Phelps\n","said. \"And don't worry about the bits you can't understand.\n","Sit back and allow the words to wash around you, like music.\"\n","\"I will, I will.\"\n","\"Did you know\", Mrs Phelps said, \"that public libraries like\n","this allow you to borrow books and take them home?\"\n","\"I didn't know that,\" Matilda said. \"Could I do it?\"\n","\"Of course,\" Mrs Phelps said. \"When you have chosen the\n","book you want, bring it to me so I can make a note of it and \n","it's yours for two weeks. You can take more than one if you\n","wish.\"\n","\n","From then on, Matilda would visit the library only once a\n","week in order to take out new books and return the old ones.\n","Her own small bedroom now became her reading-room and\n","there she would sit and read most afternoons, often with a\n","mug of hot chocolate beside her. She was not quite tall\n","enough to reach things around the kitchen, but she kept a\n","small box in the outhouse which she brought in and stood on\n","in order to get whatever she wanted. Mostly it was hot\n","chocolate she made, warming the milk in a saucepan on the\n","stove before mixing it. Occasionally she made Bovril or\n","Ovaltine. It was pleasant to take a hot drink up to her room\n","and have it beside her as she sat in her silent room reading in\n","the empty house in the afternoons. The books transported her\n","into new worlds and introduced her to amazing people who\n","lived exciting lives. She went on olden-day sailing ships with\n","Joseph Conrad. She went to Africa with Ernest Hemingway\n","and to India with Rudyard Kipling. She travelled all over the\n","world while sitting in her little room in an English village.\n","'''\n","\n","ij_chapter1 = '''I am seated in an office, surrounded by heads and bodies. My posture is consciously congruent to the shape of my hard chair.\n","This is a cold room in University Administration, wood-walled, Remington-hung, double-windowed against the November heat,\n","insulated from Administrative sounds by the reception area outside, at which Uncle Charles, Mr. deLint and I were lately received.\n","I am in here.\n","Three faces have resolved into place above summer-weight sportcoats and half-Windsors across a polished pine conference\n","table shiny with the spidered light of an Arizona noon. These are three Deans — of Admissions, Academic Affairs, Athletic\n","Affairs. I do not know which face belongs to whom.\n","I believe I appear neutral, maybe even pleasant, though I've been coached to err on the side of neutrality and not attempt what\n","would feel to me like a pleasant expression or smile.\n","I have committed to crossing my legs I hope carefully, ankle on knee, hands together in the lap of my slacks. My fingers are\n","mated into a mirrored series of what manifests, to me, as the letter X. The interview room's other personnel include: the\n","University's Director of Composition, its varsity tennis coach, and Academy prorector Mr. A. deLint. C.T. is beside me; the others\n","sit, stand and stand, respectively, at the periphery of my focus. The tennis coach jingles pocket-change. There is something\n","vaguely digestive about the room's odor. The high-traction sole of my complimentary Nike sneaker runs parallel to the wobbling\n","loafer of my mother's half-brother, here in his capacity as Headmaster, sitting in the chair to what I hope is my immediate right,\n","also facing Deans.\n","The Dean at left, a lean yellowish man whose fixed smile nevertheless has the impermanent quality of something stamped\n","into uncooperative material, is a personality-type I've come lately to appreciate, the type who delays need of any response from\n","me by relating my side of the story for me, to me. Passed a packet of computer-sheets by the shaggy lion of a Dean at center, he is\n","speaking more or less to these pages, smiling down.\n","'You are Harold Incandenza, eighteen, date of secondary-school graduation approximately one month from now, attending the\n","Enfield Tennis Academy, Enfield, Massachusetts, a boarding school, where you reside.' His reading glasses are rectangular, courtshaped, the sidelines at top and bottom. 'You are, according to Coach White and Dean [unintelligible], a regionally, nationally,\n","and continentally ranked junior tennis player, a potential O.N.A.N.C.A.A. athlete of substantial promise, recruited by Coach\n","White via correspondence with Dr. Tavis here commencing .. . February of this year.' The top page is removed and brought\n","around neatly to the bottom of the sheaf, at intervals. 'You have been in residence at the Enfield Tennis Academy since age\n","seven.’\n","I am debating whether to risk scratching the right side of my jaw, where there is a wen.\n","'Coach White informs our offices that he holds the Enfield Tennis Academy's program and achievements in high regard, that\n","the University of Arizona tennis squad has profited from the prior matriculation of several former E.T.A. alumni, one of whom\n","was one Mr. Aubrey F. deLint, who appears also to be with you here today. Coach White and his staff have given us —’\n","The yellow administrator's usage is on the whole undistinguished, though I have to admit he's made himself understood. The\n","Director of Composition seems to have more than the normal number of eyebrows. The Dean at right is looking at my face a bit\n","strangely.\n","Uncle Charles is saying that though he can anticipate that the Deans might be predisposed to weigh what he avers as coming\n","from his possible appearance as a kind of cheerleader for E.T.A., he can assure the assembled Deans that all this is true, and that\n","the Academy has presently in residence no fewer than a third of the continent's top thirty juniors, in age brackets all across the\n","board, and that I here, who go by 'Hal,' usually, am 'right up there among the very cream.' Right and center Deans smile\n","professionally; the heads of deLint and the coach incline as the Dean at left clears his throat:\n","'— belief that you could well make, even as a freshman, a real contribution to this University's varsity tennis program. We are\n","pleased,' he either says or reads, removing a page, 'that a competition of some major sort here has brought you down and given us\n","the chance to sit down and chat together about your application and potential recruitment and matriculation and scholarship.’\n","'I've been asked to add that Hal here is seeded third, Boys' 18-and-Under Singles, in the prestigious WhataBurger Southwest\n","Junior Invitational out at the Randolph Tennis Center —' says what I infer is Athletic Affairs, his cocked head showing a freckled\n","scalp.\n","'Out at Randolph Park, near the outstanding El Con Marriott,' C.T. inserts, 'a venue the whole contingent's been vocal about\n","finding absolutely top-hole thus far, which —’\n","'Just so, Chuck, and that according to Chuck here Hal has already justified his seed, he's reached the semifinals as of this\n","morning's apparently impressive win, and that he'll be playing out at the Center again tomorrow, against the winner of a\n","quarterfinal game tonight, and so will be playing tomorrow at I believe scheduled for 0830 —’\n","'Try to get under way before the godawful heat out there. Though of course a dry heat.’\n","'— and has apparently already qualified for this winter's Continental Indoors, up in Edmonton, Kirk tells me —' cocking\n","further to look up and left at the varsity coach, whose smile's teeth are radiant against a violent sunburn — 'Which is something\n","indeed.' He smiles, looking at me. 'Did we get all that right Hal.’\n","C.T. has crossed his arms casually; their triceps' flesh is webbed with mottle in the air-conditioned sunlight. 'You sure did.\n","Bill.' He smiles. The two halves of his mustache never quite match. 'And let me say if I may that Hal's excited, excited to be\n","invited for the third year running to the Invitational again, to be back here in a community he has real affection for, to visit with\n","your alumni and coaching staff, to have already justified his high seed in this week's not unstiff competition, to as they say still be\n","in it without the fat woman in the Viking hat having sung, so to speak, but of course most of all to have a chance to meet you\n","gentlemen and have a look at the facilities here. Everything here is absolutely top-slot, from what he's seen.’\n","There is a silence. DeLint shifts his back against the room's panelling and recenters his weight. My uncle beams and \n","straightens a straight watchband. 62.5% of the room's faces are directed my way, pleasantly expectant. My chest bumps like a\n","dryer with shoes in it. I compose what I project will be seen as a smile. I turn this way and that, slightly, sort of directing the\n","expression to everyone in the room.\n","There is a new silence. The yellow Dean's eyebrows go circumflex. The two other Deans look to the Director of Composition.\n","The tennis coach has moved to stand at the broad window, feeling at the back of his crewcut. Uncle Charles strokes the forearm\n","above his watch. Sharp curved palm-shadows move slightly over the pine table's shine, the one head's shadow a black moon.\n","'Is Hal all right, Chuck?' Athletic Affairs asks. 'Hal just seemed to ... well, grimace. Is he in pain? Are you in pain, son?’\n","'Hal's right as rain,' smiles my uncle, soothing the air with a casual hand. 'Just a bit of a let's call it maybe a facial tic, slightly,\n","at all the adrenaline of being here on your impressive campus, justifying his seed so far without dropping a set, receiving that\n","official written offer of not only waivers but a living allowance from Coach White here, on Pac 10 letterhead, being ready in all\n","probability to sign a National Letter of Intent right here and now this very day, he's indicated to me.' C.T. looks to me, his look\n","horribly mild. I do the safe thing, relaxing every muscle in my face, emptying out all expression. I stare carefully into the\n","Kekuléan knot of the middle Dean's necktie.\n","My silent response to the expectant silence begins to affect the air of the room, the bits of dust and sportcoat-lint stirred\n","around by the AC's vents dancing jaggedly in the slanted plane of windowlight, the air over the table like the sparkling space just\n","above a fresh-poured seltzer. The coach, in a slight accent neither British nor Australian, is telling C.T. that the whole applicationinterface process, while usually just a pleasant formality, is probably best accentuated by letting the applicant speak up for\n","himself. Right and center Deans have inclined together in soft conference, forming a kind of tepee of skin and hair. I presume it's\n","probably facilitate that the tennis coach mistook for accentuate, though accelerate, while clunkier than facilitate, is from a phonetic\n","perspective more sensible, as a mistake. The Dean with the flat yellow face has leaned forward, his lips drawn back from his teeth\n","in what I see as concern. His hands come together on the conference table's surface. His own fingers look like they mate as my\n","own four-X series dissolves and I hold tight to the sides of my chair.\n","We need candidly to chat re potential problems with my application, they and I, he is beginning to say. He makes a reference\n","to candor and its value.\n","'The issues my office faces with the application materials on file from you, Hal, involve some test scores.' He glances down at\n","a colorful sheet of standardized scores in the trench his arms have made. 'The Admissions staff is looking at standardized test\n","scores from you that are, as I'm sure you know and can explain, are, shall we say ... subnormal.' I'm to explain.\n","It's clear that this really pretty sincere yellow Dean at left is Admissions. And surely the little aviarian figure at right is\n","Athletics, then, because the facial creases of the shaggy middle Dean are now pursed in a kind of distanced affront, an I'm-eatingsomething-that-makes-me-really-appreciate-the-presence-of-whatever-I'm-drinking-along-with-it look that spells professionally\n","Academic reservations. An uncomplicated loyalty to standards, then, at center. My uncle looks to Athletics as if puzzled. He shifts\n","slightly in his chair.\n","The incongruity between Admissions's hand- and face-color is almost wild. '—verbal scores that are just quite a bit closer to\n","zero than we're comfortable with, as against a secondary-school transcript from the institution where both your mother and her\n","brother are administrators —' reading directly out of the sheaf inside his arms' ellipse — 'that this past year, yes, has fallen off a\n","bit, but by the word I mean \"fallen off\" to outstanding from three previous years of frankly incredible.’\n","'Off the charts.’\n","'Most institutions do not even have grades of A with multiple pluses after it,' says the Director of Composition, his expression\n","impossible to interpret.\n","'This kind of ... how shall I put it... incongruity,' Admissions says, his expression frank and concerned, 'I've got to tell you\n","sends up a red flag of potential concern during the admissions process.’\n","'We thus invite you to explain the appearance of incongruity if not outright shenanigans.' Students has a tiny piping voice\n","that's absurd coming out of a face this big.\n","'Surely by incredible you meant very very very impressive, as opposed to literally quote \"incredible,\" surely,' says C.T.,\n","seeming to watch the coach at the window massaging the back of his neck. The huge window gives out on nothing more than\n","dazzling sunlight and cracked earth with heat-shimmers over it.\n","'Then there is before us the matter of not the required two but nine separate application essays, some of which of nearly\n","monograph-length, each without exception being —' different sheet — 'the adjective various evalua-tors used was quote \"stellar\"\n","—’\n","Dir. of Comp.: 'I made in my assessment deliberate use of lapidary and effete.’\n","'— but in areas and with titles, I'm sure you recall quite well, Hal: \"Neoclassical Assumptions in Contemporary Prescriptive\n","Grammar,\" \"The Implications of Post-Fourier Transformations for a Holographically Mimetic Cinema,\" \"The Emergence of\n","Heroic Stasis in Broadcast Entertainment\" —’\n","' \"Montague Grammar and the Semantics of Physical Modality\"?’\n","' \"A Man Who Began to Suspect He Was Made of Glass\"?’\n","' \"Tertiary Symbolism in Justinian Erotica\"?’\n","Now showing broad expanses of recessed gum. 'Suffice to say that there's some frank and candid concern about the recipient\n","of these unfortunate test scores, though perhaps explainable test scores, being these essays' sole individual author.’\n","'I'm not sure Hal's sure just what's being implied here,' my uncle says. The Dean at center is fingering his lapels as he\n","interprets distasteful computed data.\n","'What the University is saying here is that from a strictly academic point of view there are admission problems that Hal needs\n","to try to help us iron out. A matriculant's first role at the University is and must be as a student. We couldn't admit a student we\n","have reason to suspect can't cut the mustard, no matter how much of an asset he might be on the field.’\n","'Dean Sawyer means the court, of course, Chuck,' Athletic Affairs says, head severely cocked so he's including the White\n","person behind him in the address somehow. 'Not to mention O.N.A.N.C.A.A. regulations and investigators always snuffling\n","around for some sort of whiff of the smell of impropriety.’ \n","The varsity tennis coach looks at his own watch.\n","'Assuming these board scores are accurate reflectors of true capacity in this case,' Academic Affairs says, his high voice\n","serious and sotto, still looking at the file before him as if it were a plate of something bad, Til tell you right now my opinion is it\n","wouldn't be fair. It wouldn't be fair to the other applicants. Wouldn't be fair to the University community.' He looks at me. 'And\n","it'd be especially unfair to Hal himself. Admitting a boy we see as simply an athletic asset would amount to just using that boy.\n","We're under myriad scrutiny to make sure we're not using anybody. Your board results, son, indicate that we could be accused of\n","using you.’\n","Uncle Charles is asking Coach White to ask the Dean of Athletic Affairs whether the weather over scores would be as heavy\n","if I were, say, a revenue-raising football prodigy. The familiar panic at feeling misperceived is rising, and my chest bumps and\n","thuds. I expend energy on remaining utterly silent in my chair, empty, my eyes two great pale zeros. People have promised to get\n","me through this.\n","Uncle C.T., though, has the pinched look of the cornered. His voice takes on an odd timbre when he's cornered, as if he were\n","shouting as he receded. 'Hal's grades at E.T.A., which is I should stress an Academy, not simply a camp or factory, accredited by\n","both the Commonwealth of Massachusetts and the North American Sports Academy Association, it's focused on the total needs of\n","the player and student, founded by a towering intellectual figure whom I hardly need name, here, and based by him on the\n","rigorous Oxbridge Quadrivium-Trivium curricular model, a school fully staffed and equipped, by a fully certified staff, should\n","show that my nephew here can cut just about any Pac 10 mustard that needs cutting, and that —’\n","DeLint is moving toward the tennis coach, who is shaking his head.\n","'— would be able to see a distinct flavor of minor-sport prejudice about this whole thing,' C.T. says, crossing and recrossing\n","his legs as I listen, composed and staring.\n","The room's carbonated silence is now hostile. T think it's time to let the actual applicant himself speak out on his own behalf,'\n","Academic Affairs says very quietly. 'This seems somehow impossible with you here, sir.’\n","Athletics smiles tiredly under a hand that massages the bridge of his nose. 'Maybe you'd excuse us for a moment and wait\n","outside, Chuck.’\n","'Coach White could accompany Mr. Tavis and his associate out to reception,' the yellow Dean says, smiling into my\n","unfocused eyes.\n","'— led to believe this had all been ironed out in advance, from the —' C.T. is saying as he and deLint are shown to the door.\n","The tennis coach extends a hypertrophied arm. Athletics says 'We're all friends and colleagues here.’\n","This is not working out. It strikes me that exit signs would look to a native speaker of Latin like red-lit signs that say HE\n","leaves. I would yield to the urge to bolt for the door ahead of them if I could know that bolting for the door is what the men in this\n","room would see. DeLint is murmuring something to the tennis coach. Sounds of keyboards, phone consoles as the door is briefly\n","opened, then firmly shut. I am alone among administrative heads.\n","'— offense intended to anyone,' Athletic Affairs is saying, his sportcoat tan and his necktie insigniated in tiny print — 'beyond\n","just physical abilities out there in play, which believe me we respect, want, believe me.’\n","'— question about it we wouldn't be so anxious to chat with you directly, see?’\n","'— that we've known in processing several prior applications through Coach White's office that the Enfield School is\n","operated, however impressively, by close relations of first your brother, who I can still remember the way White's predecessor\n","Maury Klamkin wooed that kid, so that grades' objectivity can be all too easily called into question —’\n","'By whomsoever's calling — N.A.A.U.P., ill-willed Pac 10 programs, O.N.A.N.C.A.A. —’\n","The essays are old ones, yes, but they are mine; de moi. But they are, yes, old, not quite on the application's instructed subject\n","of Most Meaningful Educational Experience Ever. If I'd done you one from the last year, it would look to you like some sort of\n","infant's random stabs on a keyboard, and to you, who use whomsoever as a subject. And in this new smaller company, the\n","Director of Composition seems abruptly to have actuated, emerged as both the Alpha of the pack here and way more effeminate\n","than he'd seemed at first, standing hip-shot with a hand on his waist, walking with a roll to his shoulders, jingling change as he\n","pulls up his pants as he slides into the chair still warm from C.T.'s bottom, crossing his legs in a way that inclines him well into\n","my personal space, so that I can see multiple eyebrow-tics and capillary webs in the oysters below his eyes and smell fabricsoftener and the remains of a breath-mint turned sour.\n","'. . . a bright, solid, but very shy boy, we know about your being very shy, Kirk White's told us what your athletically built if\n","rather stand-offish younger instructor told him,' the Director says softly, cupping what I feel to be a hand over my sportcoat's\n","biceps (surely not), 'who simply needs to swallow hard and trust and tell his side of the story to these gentlemen who bear no\n","maliciousness none at all but are doing our jobs and trying to look out for everyone's interests at the same time.’\n","I can picture deLint and White sitting with their elbows on their knees in the defecatory posture of all athletes at rest, deLint\n","staring at his huge thumbs, while C.T. in the reception area paces in a tight ellipse, speaking into his portable phone. I have been\n","coached for this like a Don before a RICO hearing. A neutral and affectless silence. The sort of all-defensive game Schtitt used to\n","have me play: the best defense: let everything bounce off you; do nothing. I'd tell you all you want and more, if the sounds I made\n","could be what you hear.\n","Athletics with his head out from under his wing: '— to avoid admission procedures that could be seen as primarily athleticsoriented. It could be a mess, son.’\n","'Bill means the appearance, not necessarily the real true facts of the matter, which you alone can fill in,' says the Director of\n","Composition.\n","'— the appearance of the high athletic ranking, the subnormal scores, the over-academic essays, the incredible grades\n","vortexing out of what could be seen as a nepotistic situation.’\n","The yellow Dean has leaned so far forward that his tie is going to have a horizontal dent from the table-edge, his face sallow\n","and kindly and no-shit-whatever:\n","'Look here, Mr. Incandenza, Hal, please just explain to me why we couldn't be accused of using you, son. Why nobody could \n","come and say to us, why, look here, University of Arizona, here you are using a boy for just his body, a boy so shy and withdrawn\n","he won't speak up for himself, a jock with doctored marks and a store-bought application.’\n","The Brewster's-Angle light of the tabletop appears as a rose flush behind my closed lids. I cannot make myself understood. 'I\n","am not just a jock,' I say slowly. Distinctly. 'My transcript for the last year might have been dickied a bit, maybe, but that was to\n","get me over a rough spot. The grades prior to that are de moi.' My eyes are closed; the room is silent. 'I cannot make myself\n","understood, now.' I am speaking slowly and distinctly. 'Call it something I ate.’\n","It's funny what you don't recall. Our first home, in the suburb of Weston, which I barely remember — my eldest brother Orin\n","says he can remember being in the home's backyard with our mother in the early spring, helping the Moms till some sort of garden\n","out of the cold yard. March or early April. The garden's area was a rough rectangle laid out with Popsicle sticks and twine. Orin\n","was removing rocks and hard clods from the Moms's path as she worked the rented Rototiller, a wheelbarrow-shaped, gas-driven\n","thing that roared and snorted and bucked and he remembers seemed to propel the Moms rather than vice versa, the Moms very tall\n","and having to stoop painfully to hold on, her feet leaving drunken prints in the tilled earth. He remembers that in the middle of the\n","tilling I came tear-assing out the door and into the backyard wearing some sort of fuzzy red Pooh-wear, crying, holding out\n","something he said was really unpleasant-looking in my upturned palm. He says I was around five and crying and was vividly red\n","in the cold spring air. I was saying something over and over; he couldn't make it out until our mother saw me and shut down the\n","tiller, ears ringing, and came over to see what I was holding out. This turned out to have been a large patch of mold — Orin posits\n","from some dark corner of the Weston home's basement, which was warm from the furnace and flooded every spring. The patch\n","itself he describes as horrific: darkly green, glossy, vaguely hirsute, speckled with parasitic fungal points of yellow, orange, red.\n","Worse, they could see that the patch looked oddly incomplete, gnawed-on; and some of the nauseous stuff was smeared around\n","my open mouth. 'I ate this,' was what I was saying. I held the patch out to the Moms, who had her contacts out for the dirty work,\n","and at first, bending way down, saw only her crying child, hand out, proffering; and in that most maternal of reflexes she, who\n","feared and loathed more than anything spoilage and filth, reached to take whatever her baby held out — as in how many used\n","heavy Kleenex, spit-back candies, wads of chewed-out gum in how many theaters, airports, backseats, tournament lounges? O.\n","stood there, he says, hefting a cold clod, playing with the Velcro on his puffy coat, watching as the Moms, bent way down to me,\n","hand reaching, her lowering face with its presbyopic squint, suddenly stopped, froze, beginning to I.D. what it was I held out,\n","countenancing evidence of oral contact with same. He remembers her face as past describing. Her outstretched hand, still\n","Rototrembling, hung in the air before mine.\n","'I ate this,' I said.\n","'Pardon me?’\n","O. says he can only remember (sic) saying something caustic as he lim-boed a crick out of his back. He says he must have felt\n","a terrible impending anxiety. The Moms refused ever even to go into the damp basement. I had stopped crying, he remembers, and\n","simply stood there, the size and shape of a hydrant, in red PJ's with attached feet, holding out the mold, seriously, like the report of\n","some kind of audit.\n","O. says his memory diverges at this point, probably as a result of anxiety. In his first memory, the Moms's path around the\n","yard is a broad circle of hysteria:\n","'God!' she calls out.\n","'Help! My son ate this!' she yells in Orin's second and more fleshed-out recollection, yelling it over and over, holding the\n","speckled patch aloft in a pincer of fingers, running around and around the garden's rectangle while O. gaped at his first real sight\n","of adult hysteria. Suburban neighbors' heads appeared in windows and over the fences, looking. O. remembers me tripping over\n","the garden's laid-out twine, getting up dirty, crying, trying to follow.\n","'God! Help! My son ate this! Help!' she kept yelling, running a tight pattern just inside the square of string; and my brother\n","Orin remembers noting how even in hysterical trauma her flight-lines were plumb, her footprints Native-American-straight, her\n","turns, inside the ideogram of string, crisp and martial, crying 'My son ate this! Help!' and lapping me twice before the memory\n","recedes.\n","'My application's not bought,' I am telling them, calling into the darkness of the red cave that opens out before closed eyes. 'I\n","am not just a boy who plays tennis. I have an intricate history. Experiences and feelings. I'm complex.\n","'I read,' I say. 'I study and read. I bet I've read everything you've read. Don't think I haven't. I consume libraries. I wear out\n","spines and ROM-drives. I do things like get in a taxi and say, \"The library, and step on it.\" My instincts concerning syntax and\n","mechanics are better than your own, I can tell, with due respect.\n","'But it transcends the mechanics. I'm not a machine. I feel and believe. I have opinions. Some of them are interesting. I could,\n","if you'd let me, talk and talk. Let's talk about anything. I believe the influence of Kierkegaard on Camus is underestimated. I\n","believe Dennis Gabor may very well have been the Antichrist. I believe Hobbes is just Rousseau in a dark mirror. I believe, with\n","Hegel, that transcendence is absorption. I could interface you guys right under the table,' I say. 'I'm not just a creãtus,\n","manufactured, conditioned, bred for a function.’\n","I open my eyes. 'Please don't think I don't care.’\n","I look out. Directed my way is horror. I rise from the chair. I see jowls sagging, eyebrows high on trembling foreheads,\n","cheeks bright-white. The chair recedes below me.\n","'Sweet mother of Christ,' the Director says.\n","T'm fine,' I tell them, standing. From the yellow Dean's expression, there's a brutal wind blowing from my direction.\n","Academics' face has gone instantly old. Eight eyes have become blank discs that stare at whatever they see.\n","'Good God,' whispers Athletics.\n","'Please don't worry,' I say. 'I can explain.' I soothe the air with a casual hand.\n","Both my arms are pinioned from behind by the Director of Comp., who wrestles me roughly down, on me with all his weight.\n","I taste floor.\n","'What's wrong?’\n","I say 'Nothing is wrong.’ \n","'''"],"metadata":{"id":"7gksh6WLMy2q"},"execution_count":null,"outputs":[]}]} \ No newline at end of file +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "7gksh6WLMy2q" + }, + "outputs": [], + "source": [ + "catcher_chapter1 = '''\n", + "If you really want to hear about it, the first thing you’ll probably want to know is where I was born, and what my lousy childhood was like, and how my parents were occupied and all before they had me, and all that David Copperfield kind of crap, but I don’t feel like going into it, if you want to know the truth. In the first place, that stuff bores me, and in the second place, my parents would have two hemorrhages apiece if I told anything pretty personal about them. They’re quitee touchy about anything like that, especially my father. They’re nice and all - I’m not saying that - but they’re also touchy as hell. Besides, I’m not going to tell you my whole goodam autobiography or anything. I’ll just tell you about this madman stuff that happened to me last Christmas just before I got pretty run-down and had to come out and take it easy. I mean that’s all I told D.B. about, and he’s my brother and all. He’s in Hollywood. That isn’t too far from this crumby place, and he comes over and visits me practically every week end. He’s going to drive me home when I go home next month maybe. He just got a Jaguar. One of those little English jobs that can do around two hundred miles an hour. It cost him damn near four thousand bucks. He’s got a lot of dough, now. He didn’t use to. He used to be just a regular writer, when he was home. He wrote thizs terrific book of short stories, The Secret Goldfish, in case you never heard of him. The best one in it was «‘The Secret Goldfish.’ It was about this little kid that wouldn’t let anybody look at his goldfish because he’d bought it with his own money. It killed me. Now he’s out in Hollywood, D.B., being a prostitute. If there’s one thing I hate, it’s the movies. Don’t even mention them to me.\n", + "Where I want to start is the day I left Pencey Prep. Pencey Prep is the school that’s in Agertown, Pennsylvania. You probably heard of it. You’ve probably seen the ads, anyway. They advertise in about a thousand magazines, always showing some hot-shot guy on a horse jumping over a fence. Like as if all you ever did at Pencey was play polo all the time. I never even once saw a horse anywhere near the place. And underneath the guy on the horse’s picture, it always says: ‘Since 1888 we have been molding boys into splendid, clear-thinking young men’. Strictly for the birds. They don’t do any damn more molding at Pencey than they do at any other school. And I didn’t know anybody there that was splendid and clear-thinking and all. Maybe two guys. If that many. And they probably came to Pencey that way.\n", + "Anyway, it was the Saturday of the football game with Saxon Hall. The game with Saxon Hall was supposed to be a very big deal around Pencey. It was the last game of the year, and you were supposed to commit suicide ou something if old Pencey didn’t win. I remember around three o’clock that afternoon I was standing way the hell on top of Thomsen Hill, right next to this crazy cannon that was in the Revolutionary War and all. You could see the whole field from there, and you could see the two teams bashing each other all over the place. You couldn’t see the granstand too hot, but you could hear them all yelling, deep and terrific on the Pencey side, because practically the whole school except me was there, and scrawny and faggy on the Saxon Hall side, because the visiting team hardly ever brought many people with them.\n", + "There were never many girls at all at the football games. Only seniors were allowed to bring girls with them. It was a terrible school, no matter how you looked at it. I like to be somewhere at least where you can see a few girls around once in a while, even if they’re only scratching their arms or blowing their noses or even just giggling or something. Old Selma Thurmer - she was the headmaster’s master - showed up at the games quite often, but she wasn’t exactly the type that drove you mad with desire. She was a pretty nice girl, though, I sat next to her once in the bus from Agerstown and we sort of struck up a conversation. I liked her. She had a big nose and her nails were all all bitten down and bleedy-looking and she had on those damn falsies that point all over the place, but you felt sort of sorry for her. Wha I liked about her, she didn’t give you a lot of horse manure about what a great guy her father was. She probably knew what a phony slob h e was.\n", + "The reason I was standing way up on Thomsen Hill, instead of down at the game, was because I’d just got back from New York with the fencing team. I was the goddam manager of the fencing team. Very big deal. We’d gone in to Newyork that morning for this fencing meet with McBurney School. Only, we didn’t have the meet. I left all the foils and equipment and stuff on the goddam subway. It wasn’t all my fault. I had to keep getting up to look at this map, so we’d know where to get off. So we got back to Pencey around two-thirty instead of around dinnertime. The whole team ostracized me the whole way back on the train. It was pretty funny, in a way.\n", + "The other reason I wasn’t down at the game was because I was on my way to say good-by to old Spencer, my history teacher. He had the grippe, and I figured I probably wouldn’t see him again till Christmas vacation started. He wrote me this note saying he wanted to see me before I went home. He knew I wasn’t coming back to Pencey.\n", + "I forgot to tell you about that. They kicked me out. I wasn’t supposed to come back after Christmas vacation, on account of I was flunking four subjects and not applying myself and all. They gave me frequent warning to start applying myself - especially around mid-terms, when my parents came up for a conference with old Thurmer - but I didn’t do it. So I got the ax. They give guys the ax quite frequently at Pencey. It has a very good academic rating, Pencey. It really does.\n", + "Anyway, it was December and all, and it was cold as a witch’s teat, especially on top of that stupid hill. I only had on my reversible and no gloves or anything. sThe week before that, somebody’d stolen my camel’s-hair coat right out of my room, with my fur-lined gloves right in the pocket and all. Pencey was full of crooks. Quite a few guys came from these very wealthy families, but it was full of crooks anyway. The more expensive a school is, the more crooks it has - I’m not kidding. Anyway, I kept standing next to that crazy cannon, looking down at the game and freezing my ass off. Only, I wasn’t watching the game too much. What I was really hanging around for, I was trying to feel some kind of a good-by. I mean I’ve left schools and places I didn’t even know I was leaving them. I don’t care if it’s a sad good-by or a bad good-by, but when I leave a place I live I like to know I’m leaving it. If you don’t, you feel even worse.\n", + "I was lucky. All of a sudden I thought of something that helped make me know I was getting the hell out. I suddenly remembered this time, in around October, that I and Robert Tichener and Paul Campbell were chucking a football around, in front of the academic building. there were nice guys, especially Tichener. It was just before dinner and it was getting pretty dark out, but we kept chucking the ball around anyway. It kept getting darker and darker, and we could hardly see the ball anyway. It kept getting darker and darker, and we could hardly see the ball any more, but we didn’t want to stop doing what we were doing. Finally we had to. This teacher that taught biology, Mr Zambesi, stuck his head out of this window in the academic building and told us to go back to the dorm and get ready for dinner. If I get a chance to remember that kind of stuff, I can get a good-by when I need one - at least, most of the time I can. As soon as I got it, I turned around and started running down the other side of the hill, toward old Spencer’s house. He didn’t live on the campus. He lived on Anthony Wayne Avenue.\n", + "I ran all the way to the main gate, and then I waited a second till I got my breath. I have no wind, if you want to know the truth. I’m quite a heavy smoker, for one thing - that is, I used to be. They made me cut it out. Another thing, I grew six and a half inches last year. That’s also how I practically got t.b. and came out here for all these goddam checkups and stuff. I’m pretty healthy, though.\n", + "Anyway, as soon as I got my breath back I ran accross Route 204. It was icy as hell and I damn near fell down. I don’t know what I was running for - I guess I just felt like it. After I got accross the road, I felt like I was sort of disappearing. It was that kind of a crazy afternoon, terricfically cold, and no sun out or anything, and you felt like you were disappearing every time you crossed a road.\n", + "Boy, I rang that doorbell fast when I got to old Spencer’s house. I was really frozen. My ears were hurting and I could hardly move my fingers at all. ‘C’mon, c’mon,’ I said right out loud, almost, ‘somebody open the door.’ Finally old Mrs Spencer opened it. They didn’t have a maid or anything, and they always opened the door themselves. They didn’t have too much dough.\n", + "‘Holden!’ Mrs Spencer said. ‘How lovely to see you! Come in, dear! Are youfrozen to death?’ I think she was glad to see me. She liked me. At least, I think she did.\n", + "Boy, did I get in that house fast. ‘How are you, Mrs Spencer?’ I said. ‘How’s Mr Spencer?’\n", + "‘Let me take your coat, dear,’ she said. She didn’t hear me ask her how Mr Spencer was. She was sort of deaf.\n", + "She hung up my coat in the hall closet, and I sort of brushed my hair back with my hand. I wear a crew cut quite frequently and I never have to comb it much. ‘How’ve you been, Mrs Spencer?’ I said again, only louder, so she’d hear me.\n", + "‘I’ve been just fine, Holden.’ She closed the closet door. ‘How have you been?’ The way she asked me, I knew right away old Spencer’d told her I’d been kicked out.\n", + "‘Fine,’ I said. ‘How’s Mr Spencer? He over his grippe yet?’\n", + "‘Over it! Holden, he’s behaving like a perfect - I don’t know what... He’s in his room, dear. Go right in.’\n", + "'''\n", + "\n", + "matilda_chapter1 = '''It's a funny thing about mothers and fathers. Even when their\n", + "own child is the most disgusting little blister you could ever\n", + "imagine, they still think that he or she is wonderful.\n", + "Some parents go further. They become so blinded by\n", + "adoration they manage to convince themselves their child has\n", + "qualities of genius.\n", + "Well, there is nothing very wrong with all this. It's the way\n", + "of the world. It is only when the parents begin telling us about\n", + "the brilliance of their own revolting offspring, that we start\n", + "shouting, \"Bring us a basin! We're going to be sick!\"\n", + "\n", + "School teachers suffer a good deal from having to listen to\n", + "this sort of twaddle from proud parents, but they usually get\n", + "their own back when the time comes to write the end-of-term\n", + "reports. If I were a teacher I would cook up some real\n", + "scorchers for the children of doting parents. \"Your son\n", + "Maximilian\", I would write, \"is a total wash-out. I hope you\n", + "have a family business you can push him into when he leaves\n", + "school because he sure as heck won't get a job anywhere else.\"\n", + "Or if I were feeling lyrical that day, I might write, \"It is a\n", + "curious truth that grasshoppers have their hearing-organs in \n", + "the sides of the abdomen. Your daughter Vanessa, judging by\n", + "what she's learnt this term, has no hearing-organs at all.\"\n", + "\n", + "I might even delve deeper into natural history and say,\n", + "\"The periodical cicada spends six years as a grub\n", + "underground, and no more than six days as a free creature of\n", + "sunlight and air. Your son Wilfred has spent six years as a\n", + "grub in this school and we are still waiting for him to emerge\n", + "from the chrysalis.\" A particularly poisonous little girl might\n", + "sting me into saying, \"Fiona has the same glacial beauty as an\n", + "iceberg, but unlike the iceberg she has absolutely nothing\n", + "below the surface.\" I\n", + "think I might enjoy writing end-of-term reports for the\n", + "stinkers in my class. But enough of that. We have to get on.\n", + "Occasionally one comes across parents who take the\n", + "opposite line, who show no interest at all in their children,\n", + "and these of course are far worse than the doting ones. Mr\n", + "and Mrs Wormwood were two such parents. They had a son\n", + "called Michael and a daughter called Matilda, and the parents\n", + "looked upon Matilda in particular as nothing more than a scab. A scab is something\n", + "you have to put up with until the time c\n", + "when you can pick it off and flick it away.\n", + "omes \n", + "Mr and Mrs Wormwood looked forward enormously to the\n", + "time when they could pick their little daughter off and flick\n", + "her away, preferably into the next county or even further than\n", + "that.\n", + "It is bad enough when parents treat ordinary children as\n", + "though they were scabs and bunions, but it becomes\n", + "somehow a lot worse when the child in\n", + "question is extraordinary, and by that I\n", + "mean sensitive and brilliant. Matilda was\n", + "both of these things, but above all she was\n", + "brilliant. Her mind was so nimble and she\n", + "was so quick to learn that her ability should\n", + "have been obvious even to the most half-witted of parents.\n", + "But Mr and Mrs Wormwood were both so gormless and so\n", + "wrapped up in their own silly little lives that they failed to\n", + "notice anything unusual about their daughter. To tell the\n", + "truth, I doubt they would have noticed had she crawled into\n", + "the house with a broken leg.\n", + "Matilda's brother Michael was a perfectly normal boy, but\n", + "the sister, as I said, was something to make your eyes pop. By\n", + "the age of one and a half her speech was perfect and she\n", + "knew as many words as most grown-ups. The parents, instead \n", + "of applauding her, called her a noisy chatterbox and told her\n", + "sharply that small girls should be seen and not heard.\n", + "By the time she was three, Matilda had taught herself to\n", + "read by studying newspapers and magazines that lay around\n", + "the house. At the age of four, she could read fast and well and\n", + "she naturally began hankering after books. The only book in\n", + "the whole of this enlightened household was something called\n", + "Easy Cooking belonging to her mother, and when she had\n", + "read this from cover to cover and had learnt all the recipes by\n", + "heart, she decided she wanted something more interesting.\n", + "\n", + "\"Daddy,\" she said, \"do you think you could buy me a\n", + "book?\"\n", + "\"A book?\" he said. \"What d'you want a flaming book for?\"\n", + "\"To read, Daddy.\"\n", + "\"What's wrong with the telly, for heaven's sake? We've got a\n", + "lovely telly with a twelve-inch screen and now you come\n", + "asking for a book! You're getting spoiled, my girl!\"\n", + "Nearly every weekday afternoon Matilda was left alone in\n", + "the house. Her brother (five years older than her) went to\n", + "school. Her father went to work and her mother went out\n", + "playing bingo in a town eight miles away. Mrs Wormwood\n", + "was hooked on bingo and played it five afternoons a week. On \n", + "the afternoon of the day when her father had refused to buy\n", + "her a book, Matilda set out all by herself to walk to the public\n", + "library in the village. When she arrived, she introduced\n", + "herself to the librarian, Mrs Phelps. She asked if she might sit\n", + "awhile and read a book. Mrs Phelps, slightly taken aback at\n", + "the arrival of such a tiny girl unacccompanied by a parent,\n", + "nevertheless told her she was very welcome.\n", + "\"Where are the children's books please?\" Matilda asked.\n", + "\"They're over there on those lower shelves,\" Mrs Phelps\n", + "told her. \"Would you like me to help you find a nice one with\n", + "lots of pictures in it?\"\n", + "\"No, thank you,\" Matilda said. \"I'm sure I can manage.\"\n", + "From then on, every afternoon, as soon as her mother had\n", + "left for bingo, Matilda would toddle down to the library. The\n", + "walk took only ten minutes and this allowed her two glorious\n", + "hours sitting quietly by herself in a cosy corner devouring one\n", + "book after another. When she had read every single children's\n", + "book in the place, she started wandering round in search of\n", + "something else.\n", + "Mrs Phelps, who had been watching her with fascination\n", + "for the past few weeks, now got up from her desk and went\n", + "over to her. \"Can I help you, Matilda?\" she asked. \n", + "\"I'm wondering what to read next,\" Matilda said. \"I've\n", + "finished all the children's books.\"\n", + "\"You mean you've looked at the pictures?\"\n", + "\"Yes, but I've read the books as well.\"\n", + "Mrs Phelps looked down at Matilda from her great height\n", + "and Matilda looked right back up at her.\n", + "\"I thought some were very poor,\" Matilda said, \"but others\n", + "were lovely. I liked The Secret Garden best of all. It was full of\n", + "mystery. The mystery of the room behind the closed door and\n", + "the mystery of the garden behind the big wall.\"\n", + "Mrs Phelps was stunned. ''Exactly how old are you,\n", + "Matilda?\" she asked.\n", + "\"Four years and three months,\" Matilda said.\n", + "Mrs Phelps was more stunned than ever, but she had the\n", + "sense not to show it. \"What sort of a book would you like to\n", + "read next?\" she asked.\n", + "Matilda said, \"I would like a really good one that grownups read. A famous one. I don't know any names.\"\n", + "Mrs Phelps looked along the shelves, taking her time. She\n", + "didn't quite know what to bring out. How, she asked herself,\n", + "does one choose a famous grown-up book for a four-year-old\n", + "girl? Her first thought was to pick a young teenager's\n", + "romance of the kind that is written for fifteen-year-old \n", + "schoolgirls, but for some reason she found herself instinctively walking past that particular shelf.\n", + "\"Try this,\" she said at last. \"It's very famous and very good.\n", + "If it's too long for you, just let me know and I'll find\n", + "something shorter and a bit easier.\"\n", + "\"Great Expectations,\" Matilda read, \"by Charles Dickens.\n", + "I'd love to try it.\"\n", + "I must be mad, Mrs Phelps told herself, but to Matilda she\n", + "said, \"Of course you may try it.\"\n", + "Over the next few afternoons Mrs Phelps could hardly take\n", + "her eyes from the small girl sitting for hour after hour in the\n", + "big armchair at the far end of the room with the book on her\n", + "lap. It was necessary to rest it on the lap because it was too\n", + "heavy for her to hold up, which meant she had to sit leaning\n", + "forward in order to read. And a strange sight it was, this tiny\n", + "dark-haired person sitting there with her feet nowhere near\n", + "touching the floor, totally absorbed in the wonderful\n", + "adventures of Pip and old Miss Havisham and her cobwebbed\n", + "house and by the spell of magic that Dickens the great storyteller had woven with his words. The only movement from\n", + "the reader was the lifting of the hand every now and then to\n", + "turn over a page, and Mrs Phelps always felt sad when the \n", + "time came for her to cross the floor and say; \"It's ten to five,\n", + "Matilda.\"\n", + "During the first week of Matilda's visits Mrs Phelps had\n", + "said to her, \"Does your mother walk you down here every day\n", + "and then take you home?\"\n", + "\"My mother goes to Aylesbury every afternoon to play\n", + "bingo,\" Matilda had said. \"She doesn't know I come here.\"\n", + "\"But that's surely not right,\" Mrs Phelps said. \"I think you'd\n", + "better ask her.\"\n", + "\"I'd rather not,\" Matilda said. \"She doesn't encourage\n", + "reading books. Nor does my father.\"\n", + "\"But what do they expect you to do every afternoon in an\n", + "empty house?\"\n", + "\"Just mooch around and watch the telly.\"\n", + "\"I see.\"\n", + "\"She doesn't really care what I do,\" Matilda said a little\n", + "sadly.\n", + "Mrs Phelps was concerned about the child's safety on the\n", + "walk through the fairly busy village High Street and the\n", + "crossing of the road, but she decided not to interfere.\n", + "Within a week, Matilda had finished Great Expectations\n", + "which in that edition contained four hundred and eleven \n", + "pages. \"I loved it,\" she said to Mrs Phelps. \"Has Mr Dickens\n", + "written any others?\"\n", + "\"A great number,\" said the astounded Mrs Phelps. \"Shall I\n", + "choose you another?\"\n", + "Over the next six months, under Mrs Phelps's\n", + "watchful and compassionate eye, Matilda read the following\n", + "books:\n", + "\n", + "Nicholas Nickleby by Charles Dickens\n", + "Oliver Twist by Charles Dickens\n", + "Jane Eyre by Charlotte Bronte\n", + "Pride and Prejudice by Jane Austen\n", + "Tess of the D'Urbervilles by Thomas Hardy\n", + "Gone to Earth by Mary Webb\n", + "Kim by Rudyard Kipling\n", + "The Invisible Man by H. G. Wells\n", + "The Old Man and the Sea by Ernest Hemingway\n", + "The Sound and the Fury by William Faulkner\n", + "The Grapes of Wrath by John Steinbeck\n", + "The Good Companions by J. B. Priestley\n", + "Brighton Rock by Graham Greene\n", + "Animal Farm by George Orwell\n", + " \n", + "It was a formidable list and by now Mrs Phelps was filled\n", + "with wonder and excitement, but it was probably a good thing\n", + "that she did not allow herself to be completely carried away\n", + "by it all. Almost anyone else witnessing the achievements of\n", + "this small child would have been tempted to make a great fuss\n", + "and shout the news all over the village and beyond, but not so\n", + "Mrs Phelps. She was someone who minded her own business\n", + "and had long since discovered it was seldom worth while to\n", + "interfere with other people's children.\n", + "\"Mr Hemingway says a lot of things I don't understand,\"\n", + "Matilda said to her. \"Especially about men and women. But I\n", + "loved it all the same. The way he tells it I feel I am right there\n", + "on the spot watching it all happen.\"\n", + "''A fine writer will always make you feel that,\" Mrs Phelps\n", + "said. \"And don't worry about the bits you can't understand.\n", + "Sit back and allow the words to wash around you, like music.\"\n", + "\"I will, I will.\"\n", + "\"Did you know\", Mrs Phelps said, \"that public libraries like\n", + "this allow you to borrow books and take them home?\"\n", + "\"I didn't know that,\" Matilda said. \"Could I do it?\"\n", + "\"Of course,\" Mrs Phelps said. \"When you have chosen the\n", + "book you want, bring it to me so I can make a note of it and \n", + "it's yours for two weeks. You can take more than one if you\n", + "wish.\"\n", + "\n", + "From then on, Matilda would visit the library only once a\n", + "week in order to take out new books and return the old ones.\n", + "Her own small bedroom now became her reading-room and\n", + "there she would sit and read most afternoons, often with a\n", + "mug of hot chocolate beside her. She was not quite tall\n", + "enough to reach things around the kitchen, but she kept a\n", + "small box in the outhouse which she brought in and stood on\n", + "in order to get whatever she wanted. Mostly it was hot\n", + "chocolate she made, warming the milk in a saucepan on the\n", + "stove before mixing it. Occasionally she made Bovril or\n", + "Ovaltine. It was pleasant to take a hot drink up to her room\n", + "and have it beside her as she sat in her silent room reading in\n", + "the empty house in the afternoons. The books transported her\n", + "into new worlds and introduced her to amazing people who\n", + "lived exciting lives. She went on olden-day sailing ships with\n", + "Joseph Conrad. She went to Africa with Ernest Hemingway\n", + "and to India with Rudyard Kipling. She travelled all over the\n", + "world while sitting in her little room in an English village.\n", + "'''\n", + "\n", + "ij_chapter1 = '''I am seated in an office, surrounded by heads and bodies. My posture is consciously congruent to the shape of my hard chair.\n", + "This is a cold room in University Administration, wood-walled, Remington-hung, double-windowed against the November heat,\n", + "insulated from Administrative sounds by the reception area outside, at which Uncle Charles, Mr. deLint and I were lately received.\n", + "I am in here.\n", + "Three faces have resolved into place above summer-weight sportcoats and half-Windsors across a polished pine conference\n", + "table shiny with the spidered light of an Arizona noon. These are three Deans — of Admissions, Academic Affairs, Athletic\n", + "Affairs. I do not know which face belongs to whom.\n", + "I believe I appear neutral, maybe even pleasant, though I've been coached to err on the side of neutrality and not attempt what\n", + "would feel to me like a pleasant expression or smile.\n", + "I have committed to crossing my legs I hope carefully, ankle on knee, hands together in the lap of my slacks. My fingers are\n", + "mated into a mirrored series of what manifests, to me, as the letter X. The interview room's other personnel include: the\n", + "University's Director of Composition, its varsity tennis coach, and Academy prorector Mr. A. deLint. C.T. is beside me; the others\n", + "sit, stand and stand, respectively, at the periphery of my focus. The tennis coach jingles pocket-change. There is something\n", + "vaguely digestive about the room's odor. The high-traction sole of my complimentary Nike sneaker runs parallel to the wobbling\n", + "loafer of my mother's half-brother, here in his capacity as Headmaster, sitting in the chair to what I hope is my immediate right,\n", + "also facing Deans.\n", + "The Dean at left, a lean yellowish man whose fixed smile nevertheless has the impermanent quality of something stamped\n", + "into uncooperative material, is a personality-type I've come lately to appreciate, the type who delays need of any response from\n", + "me by relating my side of the story for me, to me. Passed a packet of computer-sheets by the shaggy lion of a Dean at center, he is\n", + "speaking more or less to these pages, smiling down.\n", + "'You are Harold Incandenza, eighteen, date of secondary-school graduation approximately one month from now, attending the\n", + "Enfield Tennis Academy, Enfield, Massachusetts, a boarding school, where you reside.' His reading glasses are rectangular, courtshaped, the sidelines at top and bottom. 'You are, according to Coach White and Dean [unintelligible], a regionally, nationally,\n", + "and continentally ranked junior tennis player, a potential O.N.A.N.C.A.A. athlete of substantial promise, recruited by Coach\n", + "White via correspondence with Dr. Tavis here commencing .. . February of this year.' The top page is removed and brought\n", + "around neatly to the bottom of the sheaf, at intervals. 'You have been in residence at the Enfield Tennis Academy since age\n", + "seven.’\n", + "I am debating whether to risk scratching the right side of my jaw, where there is a wen.\n", + "'Coach White informs our offices that he holds the Enfield Tennis Academy's program and achievements in high regard, that\n", + "the University of Arizona tennis squad has profited from the prior matriculation of several former E.T.A. alumni, one of whom\n", + "was one Mr. Aubrey F. deLint, who appears also to be with you here today. Coach White and his staff have given us —’\n", + "The yellow administrator's usage is on the whole undistinguished, though I have to admit he's made himself understood. The\n", + "Director of Composition seems to have more than the normal number of eyebrows. The Dean at right is looking at my face a bit\n", + "strangely.\n", + "Uncle Charles is saying that though he can anticipate that the Deans might be predisposed to weigh what he avers as coming\n", + "from his possible appearance as a kind of cheerleader for E.T.A., he can assure the assembled Deans that all this is true, and that\n", + "the Academy has presently in residence no fewer than a third of the continent's top thirty juniors, in age brackets all across the\n", + "board, and that I here, who go by 'Hal,' usually, am 'right up there among the very cream.' Right and center Deans smile\n", + "professionally; the heads of deLint and the coach incline as the Dean at left clears his throat:\n", + "'— belief that you could well make, even as a freshman, a real contribution to this University's varsity tennis program. We are\n", + "pleased,' he either says or reads, removing a page, 'that a competition of some major sort here has brought you down and given us\n", + "the chance to sit down and chat together about your application and potential recruitment and matriculation and scholarship.’\n", + "'I've been asked to add that Hal here is seeded third, Boys' 18-and-Under Singles, in the prestigious WhataBurger Southwest\n", + "Junior Invitational out at the Randolph Tennis Center —' says what I infer is Athletic Affairs, his cocked head showing a freckled\n", + "scalp.\n", + "'Out at Randolph Park, near the outstanding El Con Marriott,' C.T. inserts, 'a venue the whole contingent's been vocal about\n", + "finding absolutely top-hole thus far, which —’\n", + "'Just so, Chuck, and that according to Chuck here Hal has already justified his seed, he's reached the semifinals as of this\n", + "morning's apparently impressive win, and that he'll be playing out at the Center again tomorrow, against the winner of a\n", + "quarterfinal game tonight, and so will be playing tomorrow at I believe scheduled for 0830 —’\n", + "'Try to get under way before the godawful heat out there. Though of course a dry heat.’\n", + "'— and has apparently already qualified for this winter's Continental Indoors, up in Edmonton, Kirk tells me —' cocking\n", + "further to look up and left at the varsity coach, whose smile's teeth are radiant against a violent sunburn — 'Which is something\n", + "indeed.' He smiles, looking at me. 'Did we get all that right Hal.’\n", + "C.T. has crossed his arms casually; their triceps' flesh is webbed with mottle in the air-conditioned sunlight. 'You sure did.\n", + "Bill.' He smiles. The two halves of his mustache never quite match. 'And let me say if I may that Hal's excited, excited to be\n", + "invited for the third year running to the Invitational again, to be back here in a community he has real affection for, to visit with\n", + "your alumni and coaching staff, to have already justified his high seed in this week's not unstiff competition, to as they say still be\n", + "in it without the fat woman in the Viking hat having sung, so to speak, but of course most of all to have a chance to meet you\n", + "gentlemen and have a look at the facilities here. Everything here is absolutely top-slot, from what he's seen.’\n", + "There is a silence. DeLint shifts his back against the room's panelling and recenters his weight. My uncle beams and \n", + "straightens a straight watchband. 62.5% of the room's faces are directed my way, pleasantly expectant. My chest bumps like a\n", + "dryer with shoes in it. I compose what I project will be seen as a smile. I turn this way and that, slightly, sort of directing the\n", + "expression to everyone in the room.\n", + "There is a new silence. The yellow Dean's eyebrows go circumflex. The two other Deans look to the Director of Composition.\n", + "The tennis coach has moved to stand at the broad window, feeling at the back of his crewcut. Uncle Charles strokes the forearm\n", + "above his watch. Sharp curved palm-shadows move slightly over the pine table's shine, the one head's shadow a black moon.\n", + "'Is Hal all right, Chuck?' Athletic Affairs asks. 'Hal just seemed to ... well, grimace. Is he in pain? Are you in pain, son?’\n", + "'Hal's right as rain,' smiles my uncle, soothing the air with a casual hand. 'Just a bit of a let's call it maybe a facial tic, slightly,\n", + "at all the adrenaline of being here on your impressive campus, justifying his seed so far without dropping a set, receiving that\n", + "official written offer of not only waivers but a living allowance from Coach White here, on Pac 10 letterhead, being ready in all\n", + "probability to sign a National Letter of Intent right here and now this very day, he's indicated to me.' C.T. looks to me, his look\n", + "horribly mild. I do the safe thing, relaxing every muscle in my face, emptying out all expression. I stare carefully into the\n", + "Kekuléan knot of the middle Dean's necktie.\n", + "My silent response to the expectant silence begins to affect the air of the room, the bits of dust and sportcoat-lint stirred\n", + "around by the AC's vents dancing jaggedly in the slanted plane of windowlight, the air over the table like the sparkling space just\n", + "above a fresh-poured seltzer. The coach, in a slight accent neither British nor Australian, is telling C.T. that the whole applicationinterface process, while usually just a pleasant formality, is probably best accentuated by letting the applicant speak up for\n", + "himself. Right and center Deans have inclined together in soft conference, forming a kind of tepee of skin and hair. I presume it's\n", + "probably facilitate that the tennis coach mistook for accentuate, though accelerate, while clunkier than facilitate, is from a phonetic\n", + "perspective more sensible, as a mistake. The Dean with the flat yellow face has leaned forward, his lips drawn back from his teeth\n", + "in what I see as concern. His hands come together on the conference table's surface. His own fingers look like they mate as my\n", + "own four-X series dissolves and I hold tight to the sides of my chair.\n", + "We need candidly to chat re potential problems with my application, they and I, he is beginning to say. He makes a reference\n", + "to candor and its value.\n", + "'The issues my office faces with the application materials on file from you, Hal, involve some test scores.' He glances down at\n", + "a colorful sheet of standardized scores in the trench his arms have made. 'The Admissions staff is looking at standardized test\n", + "scores from you that are, as I'm sure you know and can explain, are, shall we say ... subnormal.' I'm to explain.\n", + "It's clear that this really pretty sincere yellow Dean at left is Admissions. And surely the little aviarian figure at right is\n", + "Athletics, then, because the facial creases of the shaggy middle Dean are now pursed in a kind of distanced affront, an I'm-eatingsomething-that-makes-me-really-appreciate-the-presence-of-whatever-I'm-drinking-along-with-it look that spells professionally\n", + "Academic reservations. An uncomplicated loyalty to standards, then, at center. My uncle looks to Athletics as if puzzled. He shifts\n", + "slightly in his chair.\n", + "The incongruity between Admissions's hand- and face-color is almost wild. '—verbal scores that are just quite a bit closer to\n", + "zero than we're comfortable with, as against a secondary-school transcript from the institution where both your mother and her\n", + "brother are administrators —' reading directly out of the sheaf inside his arms' ellipse — 'that this past year, yes, has fallen off a\n", + "bit, but by the word I mean \"fallen off\" to outstanding from three previous years of frankly incredible.’\n", + "'Off the charts.’\n", + "'Most institutions do not even have grades of A with multiple pluses after it,' says the Director of Composition, his expression\n", + "impossible to interpret.\n", + "'This kind of ... how shall I put it... incongruity,' Admissions says, his expression frank and concerned, 'I've got to tell you\n", + "sends up a red flag of potential concern during the admissions process.’\n", + "'We thus invite you to explain the appearance of incongruity if not outright shenanigans.' Students has a tiny piping voice\n", + "that's absurd coming out of a face this big.\n", + "'Surely by incredible you meant very very very impressive, as opposed to literally quote \"incredible,\" surely,' says C.T.,\n", + "seeming to watch the coach at the window massaging the back of his neck. The huge window gives out on nothing more than\n", + "dazzling sunlight and cracked earth with heat-shimmers over it.\n", + "'Then there is before us the matter of not the required two but nine separate application essays, some of which of nearly\n", + "monograph-length, each without exception being —' different sheet — 'the adjective various evalua-tors used was quote \"stellar\"\n", + "—’\n", + "Dir. of Comp.: 'I made in my assessment deliberate use of lapidary and effete.’\n", + "'— but in areas and with titles, I'm sure you recall quite well, Hal: \"Neoclassical Assumptions in Contemporary Prescriptive\n", + "Grammar,\" \"The Implications of Post-Fourier Transformations for a Holographically Mimetic Cinema,\" \"The Emergence of\n", + "Heroic Stasis in Broadcast Entertainment\" —’\n", + "' \"Montague Grammar and the Semantics of Physical Modality\"?’\n", + "' \"A Man Who Began to Suspect He Was Made of Glass\"?’\n", + "' \"Tertiary Symbolism in Justinian Erotica\"?’\n", + "Now showing broad expanses of recessed gum. 'Suffice to say that there's some frank and candid concern about the recipient\n", + "of these unfortunate test scores, though perhaps explainable test scores, being these essays' sole individual author.’\n", + "'I'm not sure Hal's sure just what's being implied here,' my uncle says. The Dean at center is fingering his lapels as he\n", + "interprets distasteful computed data.\n", + "'What the University is saying here is that from a strictly academic point of view there are admission problems that Hal needs\n", + "to try to help us iron out. A matriculant's first role at the University is and must be as a student. We couldn't admit a student we\n", + "have reason to suspect can't cut the mustard, no matter how much of an asset he might be on the field.’\n", + "'Dean Sawyer means the court, of course, Chuck,' Athletic Affairs says, head severely cocked so he's including the White\n", + "person behind him in the address somehow. 'Not to mention O.N.A.N.C.A.A. regulations and investigators always snuffling\n", + "around for some sort of whiff of the smell of impropriety.’ \n", + "The varsity tennis coach looks at his own watch.\n", + "'Assuming these board scores are accurate reflectors of true capacity in this case,' Academic Affairs says, his high voice\n", + "serious and sotto, still looking at the file before him as if it were a plate of something bad, Til tell you right now my opinion is it\n", + "wouldn't be fair. It wouldn't be fair to the other applicants. Wouldn't be fair to the University community.' He looks at me. 'And\n", + "it'd be especially unfair to Hal himself. Admitting a boy we see as simply an athletic asset would amount to just using that boy.\n", + "We're under myriad scrutiny to make sure we're not using anybody. Your board results, son, indicate that we could be accused of\n", + "using you.’\n", + "Uncle Charles is asking Coach White to ask the Dean of Athletic Affairs whether the weather over scores would be as heavy\n", + "if I were, say, a revenue-raising football prodigy. The familiar panic at feeling misperceived is rising, and my chest bumps and\n", + "thuds. I expend energy on remaining utterly silent in my chair, empty, my eyes two great pale zeros. People have promised to get\n", + "me through this.\n", + "Uncle C.T., though, has the pinched look of the cornered. His voice takes on an odd timbre when he's cornered, as if he were\n", + "shouting as he receded. 'Hal's grades at E.T.A., which is I should stress an Academy, not simply a camp or factory, accredited by\n", + "both the Commonwealth of Massachusetts and the North American Sports Academy Association, it's focused on the total needs of\n", + "the player and student, founded by a towering intellectual figure whom I hardly need name, here, and based by him on the\n", + "rigorous Oxbridge Quadrivium-Trivium curricular model, a school fully staffed and equipped, by a fully certified staff, should\n", + "show that my nephew here can cut just about any Pac 10 mustard that needs cutting, and that —’\n", + "DeLint is moving toward the tennis coach, who is shaking his head.\n", + "'— would be able to see a distinct flavor of minor-sport prejudice about this whole thing,' C.T. says, crossing and recrossing\n", + "his legs as I listen, composed and staring.\n", + "The room's carbonated silence is now hostile. T think it's time to let the actual applicant himself speak out on his own behalf,'\n", + "Academic Affairs says very quietly. 'This seems somehow impossible with you here, sir.’\n", + "Athletics smiles tiredly under a hand that massages the bridge of his nose. 'Maybe you'd excuse us for a moment and wait\n", + "outside, Chuck.’\n", + "'Coach White could accompany Mr. Tavis and his associate out to reception,' the yellow Dean says, smiling into my\n", + "unfocused eyes.\n", + "'— led to believe this had all been ironed out in advance, from the —' C.T. is saying as he and deLint are shown to the door.\n", + "The tennis coach extends a hypertrophied arm. Athletics says 'We're all friends and colleagues here.’\n", + "This is not working out. It strikes me that exit signs would look to a native speaker of Latin like red-lit signs that say HE\n", + "leaves. I would yield to the urge to bolt for the door ahead of them if I could know that bolting for the door is what the men in this\n", + "room would see. DeLint is murmuring something to the tennis coach. Sounds of keyboards, phone consoles as the door is briefly\n", + "opened, then firmly shut. I am alone among administrative heads.\n", + "'— offense intended to anyone,' Athletic Affairs is saying, his sportcoat tan and his necktie insigniated in tiny print — 'beyond\n", + "just physical abilities out there in play, which believe me we respect, want, believe me.’\n", + "'— question about it we wouldn't be so anxious to chat with you directly, see?’\n", + "'— that we've known in processing several prior applications through Coach White's office that the Enfield School is\n", + "operated, however impressively, by close relations of first your brother, who I can still remember the way White's predecessor\n", + "Maury Klamkin wooed that kid, so that grades' objectivity can be all too easily called into question —’\n", + "'By whomsoever's calling — N.A.A.U.P., ill-willed Pac 10 programs, O.N.A.N.C.A.A. —’\n", + "The essays are old ones, yes, but they are mine; de moi. But they are, yes, old, not quite on the application's instructed subject\n", + "of Most Meaningful Educational Experience Ever. If I'd done you one from the last year, it would look to you like some sort of\n", + "infant's random stabs on a keyboard, and to you, who use whomsoever as a subject. And in this new smaller company, the\n", + "Director of Composition seems abruptly to have actuated, emerged as both the Alpha of the pack here and way more effeminate\n", + "than he'd seemed at first, standing hip-shot with a hand on his waist, walking with a roll to his shoulders, jingling change as he\n", + "pulls up his pants as he slides into the chair still warm from C.T.'s bottom, crossing his legs in a way that inclines him well into\n", + "my personal space, so that I can see multiple eyebrow-tics and capillary webs in the oysters below his eyes and smell fabricsoftener and the remains of a breath-mint turned sour.\n", + "'. . . a bright, solid, but very shy boy, we know about your being very shy, Kirk White's told us what your athletically built if\n", + "rather stand-offish younger instructor told him,' the Director says softly, cupping what I feel to be a hand over my sportcoat's\n", + "biceps (surely not), 'who simply needs to swallow hard and trust and tell his side of the story to these gentlemen who bear no\n", + "maliciousness none at all but are doing our jobs and trying to look out for everyone's interests at the same time.’\n", + "I can picture deLint and White sitting with their elbows on their knees in the defecatory posture of all athletes at rest, deLint\n", + "staring at his huge thumbs, while C.T. in the reception area paces in a tight ellipse, speaking into his portable phone. I have been\n", + "coached for this like a Don before a RICO hearing. A neutral and affectless silence. The sort of all-defensive game Schtitt used to\n", + "have me play: the best defense: let everything bounce off you; do nothing. I'd tell you all you want and more, if the sounds I made\n", + "could be what you hear.\n", + "Athletics with his head out from under his wing: '— to avoid admission procedures that could be seen as primarily athleticsoriented. It could be a mess, son.’\n", + "'Bill means the appearance, not necessarily the real true facts of the matter, which you alone can fill in,' says the Director of\n", + "Composition.\n", + "'— the appearance of the high athletic ranking, the subnormal scores, the over-academic essays, the incredible grades\n", + "vortexing out of what could be seen as a nepotistic situation.’\n", + "The yellow Dean has leaned so far forward that his tie is going to have a horizontal dent from the table-edge, his face sallow\n", + "and kindly and no-shit-whatever:\n", + "'Look here, Mr. Incandenza, Hal, please just explain to me why we couldn't be accused of using you, son. Why nobody could \n", + "come and say to us, why, look here, University of Arizona, here you are using a boy for just his body, a boy so shy and withdrawn\n", + "he won't speak up for himself, a jock with doctored marks and a store-bought application.’\n", + "The Brewster's-Angle light of the tabletop appears as a rose flush behind my closed lids. I cannot make myself understood. 'I\n", + "am not just a jock,' I say slowly. Distinctly. 'My transcript for the last year might have been dickied a bit, maybe, but that was to\n", + "get me over a rough spot. The grades prior to that are de moi.' My eyes are closed; the room is silent. 'I cannot make myself\n", + "understood, now.' I am speaking slowly and distinctly. 'Call it something I ate.’\n", + "It's funny what you don't recall. Our first home, in the suburb of Weston, which I barely remember — my eldest brother Orin\n", + "says he can remember being in the home's backyard with our mother in the early spring, helping the Moms till some sort of garden\n", + "out of the cold yard. March or early April. The garden's area was a rough rectangle laid out with Popsicle sticks and twine. Orin\n", + "was removing rocks and hard clods from the Moms's path as she worked the rented Rototiller, a wheelbarrow-shaped, gas-driven\n", + "thing that roared and snorted and bucked and he remembers seemed to propel the Moms rather than vice versa, the Moms very tall\n", + "and having to stoop painfully to hold on, her feet leaving drunken prints in the tilled earth. He remembers that in the middle of the\n", + "tilling I came tear-assing out the door and into the backyard wearing some sort of fuzzy red Pooh-wear, crying, holding out\n", + "something he said was really unpleasant-looking in my upturned palm. He says I was around five and crying and was vividly red\n", + "in the cold spring air. I was saying something over and over; he couldn't make it out until our mother saw me and shut down the\n", + "tiller, ears ringing, and came over to see what I was holding out. This turned out to have been a large patch of mold — Orin posits\n", + "from some dark corner of the Weston home's basement, which was warm from the furnace and flooded every spring. The patch\n", + "itself he describes as horrific: darkly green, glossy, vaguely hirsute, speckled with parasitic fungal points of yellow, orange, red.\n", + "Worse, they could see that the patch looked oddly incomplete, gnawed-on; and some of the nauseous stuff was smeared around\n", + "my open mouth. 'I ate this,' was what I was saying. I held the patch out to the Moms, who had her contacts out for the dirty work,\n", + "and at first, bending way down, saw only her crying child, hand out, proffering; and in that most maternal of reflexes she, who\n", + "feared and loathed more than anything spoilage and filth, reached to take whatever her baby held out — as in how many used\n", + "heavy Kleenex, spit-back candies, wads of chewed-out gum in how many theaters, airports, backseats, tournament lounges? O.\n", + "stood there, he says, hefting a cold clod, playing with the Velcro on his puffy coat, watching as the Moms, bent way down to me,\n", + "hand reaching, her lowering face with its presbyopic squint, suddenly stopped, froze, beginning to I.D. what it was I held out,\n", + "countenancing evidence of oral contact with same. He remembers her face as past describing. Her outstretched hand, still\n", + "Rototrembling, hung in the air before mine.\n", + "'I ate this,' I said.\n", + "'Pardon me?’\n", + "O. says he can only remember (sic) saying something caustic as he lim-boed a crick out of his back. He says he must have felt\n", + "a terrible impending anxiety. The Moms refused ever even to go into the damp basement. I had stopped crying, he remembers, and\n", + "simply stood there, the size and shape of a hydrant, in red PJ's with attached feet, holding out the mold, seriously, like the report of\n", + "some kind of audit.\n", + "O. says his memory diverges at this point, probably as a result of anxiety. In his first memory, the Moms's path around the\n", + "yard is a broad circle of hysteria:\n", + "'God!' she calls out.\n", + "'Help! My son ate this!' she yells in Orin's second and more fleshed-out recollection, yelling it over and over, holding the\n", + "speckled patch aloft in a pincer of fingers, running around and around the garden's rectangle while O. gaped at his first real sight\n", + "of adult hysteria. Suburban neighbors' heads appeared in windows and over the fences, looking. O. remembers me tripping over\n", + "the garden's laid-out twine, getting up dirty, crying, trying to follow.\n", + "'God! Help! My son ate this! Help!' she kept yelling, running a tight pattern just inside the square of string; and my brother\n", + "Orin remembers noting how even in hysterical trauma her flight-lines were plumb, her footprints Native-American-straight, her\n", + "turns, inside the ideogram of string, crisp and martial, crying 'My son ate this! Help!' and lapping me twice before the memory\n", + "recedes.\n", + "'My application's not bought,' I am telling them, calling into the darkness of the red cave that opens out before closed eyes. 'I\n", + "am not just a boy who plays tennis. I have an intricate history. Experiences and feelings. I'm complex.\n", + "'I read,' I say. 'I study and read. I bet I've read everything you've read. Don't think I haven't. I consume libraries. I wear out\n", + "spines and ROM-drives. I do things like get in a taxi and say, \"The library, and step on it.\" My instincts concerning syntax and\n", + "mechanics are better than your own, I can tell, with due respect.\n", + "'But it transcends the mechanics. I'm not a machine. I feel and believe. I have opinions. Some of them are interesting. I could,\n", + "if you'd let me, talk and talk. Let's talk about anything. I believe the influence of Kierkegaard on Camus is underestimated. I\n", + "believe Dennis Gabor may very well have been the Antichrist. I believe Hobbes is just Rousseau in a dark mirror. I believe, with\n", + "Hegel, that transcendence is absorption. I could interface you guys right under the table,' I say. 'I'm not just a creãtus,\n", + "manufactured, conditioned, bred for a function.’\n", + "I open my eyes. 'Please don't think I don't care.’\n", + "I look out. Directed my way is horror. I rise from the chair. I see jowls sagging, eyebrows high on trembling foreheads,\n", + "cheeks bright-white. The chair recedes below me.\n", + "'Sweet mother of Christ,' the Director says.\n", + "T'm fine,' I tell them, standing. From the yellow Dean's expression, there's a brutal wind blowing from my direction.\n", + "Academics' face has gone instantly old. Eight eyes have become blank discs that stare at whatever they see.\n", + "'Good God,' whispers Athletics.\n", + "'Please don't worry,' I say. 'I can explain.' I soothe the air with a casual hand.\n", + "Both my arms are pinioned from behind by the Director of Comp., who wrestles me roughly down, on me with all his weight.\n", + "I taste floor.\n", + "'What's wrong?’\n", + "I say 'Nothing is wrong.’ \n", + "'''" + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyPlTZNDb2yTw7vB3bqNYG48", + "provenance": [] + }, + "jupytext": { + "main_language": "python" + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/lessons/Bonus Exercise data.py b/lessons/Bonus Exercise data.py new file mode 100644 index 0000000..f41bb30 --- /dev/null +++ b/lessons/Bonus Exercise data.py @@ -0,0 +1,533 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# name: python3 +# --- + +# %% id="7gksh6WLMy2q" +catcher_chapter1 = ''' +If you really want to hear about it, the first thing you’ll probably want to know is where I was born, and what my lousy childhood was like, and how my parents were occupied and all before they had me, and all that David Copperfield kind of crap, but I don’t feel like going into it, if you want to know the truth. In the first place, that stuff bores me, and in the second place, my parents would have two hemorrhages apiece if I told anything pretty personal about them. They’re quitee touchy about anything like that, especially my father. They’re nice and all - I’m not saying that - but they’re also touchy as hell. Besides, I’m not going to tell you my whole goodam autobiography or anything. I’ll just tell you about this madman stuff that happened to me last Christmas just before I got pretty run-down and had to come out and take it easy. I mean that’s all I told D.B. about, and he’s my brother and all. He’s in Hollywood. That isn’t too far from this crumby place, and he comes over and visits me practically every week end. He’s going to drive me home when I go home next month maybe. He just got a Jaguar. One of those little English jobs that can do around two hundred miles an hour. It cost him damn near four thousand bucks. He’s got a lot of dough, now. He didn’t use to. He used to be just a regular writer, when he was home. He wrote thizs terrific book of short stories, The Secret Goldfish, in case you never heard of him. The best one in it was «‘The Secret Goldfish.’ It was about this little kid that wouldn’t let anybody look at his goldfish because he’d bought it with his own money. It killed me. Now he’s out in Hollywood, D.B., being a prostitute. If there’s one thing I hate, it’s the movies. Don’t even mention them to me. +Where I want to start is the day I left Pencey Prep. Pencey Prep is the school that’s in Agertown, Pennsylvania. You probably heard of it. You’ve probably seen the ads, anyway. They advertise in about a thousand magazines, always showing some hot-shot guy on a horse jumping over a fence. Like as if all you ever did at Pencey was play polo all the time. I never even once saw a horse anywhere near the place. And underneath the guy on the horse’s picture, it always says: ‘Since 1888 we have been molding boys into splendid, clear-thinking young men’. Strictly for the birds. They don’t do any damn more molding at Pencey than they do at any other school. And I didn’t know anybody there that was splendid and clear-thinking and all. Maybe two guys. If that many. And they probably came to Pencey that way. +Anyway, it was the Saturday of the football game with Saxon Hall. The game with Saxon Hall was supposed to be a very big deal around Pencey. It was the last game of the year, and you were supposed to commit suicide ou something if old Pencey didn’t win. I remember around three o’clock that afternoon I was standing way the hell on top of Thomsen Hill, right next to this crazy cannon that was in the Revolutionary War and all. You could see the whole field from there, and you could see the two teams bashing each other all over the place. You couldn’t see the granstand too hot, but you could hear them all yelling, deep and terrific on the Pencey side, because practically the whole school except me was there, and scrawny and faggy on the Saxon Hall side, because the visiting team hardly ever brought many people with them. +There were never many girls at all at the football games. Only seniors were allowed to bring girls with them. It was a terrible school, no matter how you looked at it. I like to be somewhere at least where you can see a few girls around once in a while, even if they’re only scratching their arms or blowing their noses or even just giggling or something. Old Selma Thurmer - she was the headmaster’s master - showed up at the games quite often, but she wasn’t exactly the type that drove you mad with desire. She was a pretty nice girl, though, I sat next to her once in the bus from Agerstown and we sort of struck up a conversation. I liked her. She had a big nose and her nails were all all bitten down and bleedy-looking and she had on those damn falsies that point all over the place, but you felt sort of sorry for her. Wha I liked about her, she didn’t give you a lot of horse manure about what a great guy her father was. She probably knew what a phony slob h e was. +The reason I was standing way up on Thomsen Hill, instead of down at the game, was because I’d just got back from New York with the fencing team. I was the goddam manager of the fencing team. Very big deal. We’d gone in to Newyork that morning for this fencing meet with McBurney School. Only, we didn’t have the meet. I left all the foils and equipment and stuff on the goddam subway. It wasn’t all my fault. I had to keep getting up to look at this map, so we’d know where to get off. So we got back to Pencey around two-thirty instead of around dinnertime. The whole team ostracized me the whole way back on the train. It was pretty funny, in a way. +The other reason I wasn’t down at the game was because I was on my way to say good-by to old Spencer, my history teacher. He had the grippe, and I figured I probably wouldn’t see him again till Christmas vacation started. He wrote me this note saying he wanted to see me before I went home. He knew I wasn’t coming back to Pencey. +I forgot to tell you about that. They kicked me out. I wasn’t supposed to come back after Christmas vacation, on account of I was flunking four subjects and not applying myself and all. They gave me frequent warning to start applying myself - especially around mid-terms, when my parents came up for a conference with old Thurmer - but I didn’t do it. So I got the ax. They give guys the ax quite frequently at Pencey. It has a very good academic rating, Pencey. It really does. +Anyway, it was December and all, and it was cold as a witch’s teat, especially on top of that stupid hill. I only had on my reversible and no gloves or anything. sThe week before that, somebody’d stolen my camel’s-hair coat right out of my room, with my fur-lined gloves right in the pocket and all. Pencey was full of crooks. Quite a few guys came from these very wealthy families, but it was full of crooks anyway. The more expensive a school is, the more crooks it has - I’m not kidding. Anyway, I kept standing next to that crazy cannon, looking down at the game and freezing my ass off. Only, I wasn’t watching the game too much. What I was really hanging around for, I was trying to feel some kind of a good-by. I mean I’ve left schools and places I didn’t even know I was leaving them. I don’t care if it’s a sad good-by or a bad good-by, but when I leave a place I live I like to know I’m leaving it. If you don’t, you feel even worse. +I was lucky. All of a sudden I thought of something that helped make me know I was getting the hell out. I suddenly remembered this time, in around October, that I and Robert Tichener and Paul Campbell were chucking a football around, in front of the academic building. there were nice guys, especially Tichener. It was just before dinner and it was getting pretty dark out, but we kept chucking the ball around anyway. It kept getting darker and darker, and we could hardly see the ball anyway. It kept getting darker and darker, and we could hardly see the ball any more, but we didn’t want to stop doing what we were doing. Finally we had to. This teacher that taught biology, Mr Zambesi, stuck his head out of this window in the academic building and told us to go back to the dorm and get ready for dinner. If I get a chance to remember that kind of stuff, I can get a good-by when I need one - at least, most of the time I can. As soon as I got it, I turned around and started running down the other side of the hill, toward old Spencer’s house. He didn’t live on the campus. He lived on Anthony Wayne Avenue. +I ran all the way to the main gate, and then I waited a second till I got my breath. I have no wind, if you want to know the truth. I’m quite a heavy smoker, for one thing - that is, I used to be. They made me cut it out. Another thing, I grew six and a half inches last year. That’s also how I practically got t.b. and came out here for all these goddam checkups and stuff. I’m pretty healthy, though. +Anyway, as soon as I got my breath back I ran accross Route 204. It was icy as hell and I damn near fell down. I don’t know what I was running for - I guess I just felt like it. After I got accross the road, I felt like I was sort of disappearing. It was that kind of a crazy afternoon, terricfically cold, and no sun out or anything, and you felt like you were disappearing every time you crossed a road. +Boy, I rang that doorbell fast when I got to old Spencer’s house. I was really frozen. My ears were hurting and I could hardly move my fingers at all. ‘C’mon, c’mon,’ I said right out loud, almost, ‘somebody open the door.’ Finally old Mrs Spencer opened it. They didn’t have a maid or anything, and they always opened the door themselves. They didn’t have too much dough. +‘Holden!’ Mrs Spencer said. ‘How lovely to see you! Come in, dear! Are youfrozen to death?’ I think she was glad to see me. She liked me. At least, I think she did. +Boy, did I get in that house fast. ‘How are you, Mrs Spencer?’ I said. ‘How’s Mr Spencer?’ +‘Let me take your coat, dear,’ she said. She didn’t hear me ask her how Mr Spencer was. She was sort of deaf. +She hung up my coat in the hall closet, and I sort of brushed my hair back with my hand. I wear a crew cut quite frequently and I never have to comb it much. ‘How’ve you been, Mrs Spencer?’ I said again, only louder, so she’d hear me. +‘I’ve been just fine, Holden.’ She closed the closet door. ‘How have you been?’ The way she asked me, I knew right away old Spencer’d told her I’d been kicked out. +‘Fine,’ I said. ‘How’s Mr Spencer? He over his grippe yet?’ +‘Over it! Holden, he’s behaving like a perfect - I don’t know what... He’s in his room, dear. Go right in.’ +''' + +matilda_chapter1 = '''It's a funny thing about mothers and fathers. Even when their +own child is the most disgusting little blister you could ever +imagine, they still think that he or she is wonderful. +Some parents go further. They become so blinded by +adoration they manage to convince themselves their child has +qualities of genius. +Well, there is nothing very wrong with all this. It's the way +of the world. It is only when the parents begin telling us about +the brilliance of their own revolting offspring, that we start +shouting, "Bring us a basin! We're going to be sick!" + +School teachers suffer a good deal from having to listen to +this sort of twaddle from proud parents, but they usually get +their own back when the time comes to write the end-of-term +reports. If I were a teacher I would cook up some real +scorchers for the children of doting parents. "Your son +Maximilian", I would write, "is a total wash-out. I hope you +have a family business you can push him into when he leaves +school because he sure as heck won't get a job anywhere else." +Or if I were feeling lyrical that day, I might write, "It is a +curious truth that grasshoppers have their hearing-organs in +the sides of the abdomen. Your daughter Vanessa, judging by +what she's learnt this term, has no hearing-organs at all." + +I might even delve deeper into natural history and say, +"The periodical cicada spends six years as a grub +underground, and no more than six days as a free creature of +sunlight and air. Your son Wilfred has spent six years as a +grub in this school and we are still waiting for him to emerge +from the chrysalis." A particularly poisonous little girl might +sting me into saying, "Fiona has the same glacial beauty as an +iceberg, but unlike the iceberg she has absolutely nothing +below the surface." I +think I might enjoy writing end-of-term reports for the +stinkers in my class. But enough of that. We have to get on. +Occasionally one comes across parents who take the +opposite line, who show no interest at all in their children, +and these of course are far worse than the doting ones. Mr +and Mrs Wormwood were two such parents. They had a son +called Michael and a daughter called Matilda, and the parents +looked upon Matilda in particular as nothing more than a scab. A scab is something +you have to put up with until the time c +when you can pick it off and flick it away. +omes +Mr and Mrs Wormwood looked forward enormously to the +time when they could pick their little daughter off and flick +her away, preferably into the next county or even further than +that. +It is bad enough when parents treat ordinary children as +though they were scabs and bunions, but it becomes +somehow a lot worse when the child in +question is extraordinary, and by that I +mean sensitive and brilliant. Matilda was +both of these things, but above all she was +brilliant. Her mind was so nimble and she +was so quick to learn that her ability should +have been obvious even to the most half-witted of parents. +But Mr and Mrs Wormwood were both so gormless and so +wrapped up in their own silly little lives that they failed to +notice anything unusual about their daughter. To tell the +truth, I doubt they would have noticed had she crawled into +the house with a broken leg. +Matilda's brother Michael was a perfectly normal boy, but +the sister, as I said, was something to make your eyes pop. By +the age of one and a half her speech was perfect and she +knew as many words as most grown-ups. The parents, instead +of applauding her, called her a noisy chatterbox and told her +sharply that small girls should be seen and not heard. +By the time she was three, Matilda had taught herself to +read by studying newspapers and magazines that lay around +the house. At the age of four, she could read fast and well and +she naturally began hankering after books. The only book in +the whole of this enlightened household was something called +Easy Cooking belonging to her mother, and when she had +read this from cover to cover and had learnt all the recipes by +heart, she decided she wanted something more interesting. + +"Daddy," she said, "do you think you could buy me a +book?" +"A book?" he said. "What d'you want a flaming book for?" +"To read, Daddy." +"What's wrong with the telly, for heaven's sake? We've got a +lovely telly with a twelve-inch screen and now you come +asking for a book! You're getting spoiled, my girl!" +Nearly every weekday afternoon Matilda was left alone in +the house. Her brother (five years older than her) went to +school. Her father went to work and her mother went out +playing bingo in a town eight miles away. Mrs Wormwood +was hooked on bingo and played it five afternoons a week. On +the afternoon of the day when her father had refused to buy +her a book, Matilda set out all by herself to walk to the public +library in the village. When she arrived, she introduced +herself to the librarian, Mrs Phelps. She asked if she might sit +awhile and read a book. Mrs Phelps, slightly taken aback at +the arrival of such a tiny girl unacccompanied by a parent, +nevertheless told her she was very welcome. +"Where are the children's books please?" Matilda asked. +"They're over there on those lower shelves," Mrs Phelps +told her. "Would you like me to help you find a nice one with +lots of pictures in it?" +"No, thank you," Matilda said. "I'm sure I can manage." +From then on, every afternoon, as soon as her mother had +left for bingo, Matilda would toddle down to the library. The +walk took only ten minutes and this allowed her two glorious +hours sitting quietly by herself in a cosy corner devouring one +book after another. When she had read every single children's +book in the place, she started wandering round in search of +something else. +Mrs Phelps, who had been watching her with fascination +for the past few weeks, now got up from her desk and went +over to her. "Can I help you, Matilda?" she asked. +"I'm wondering what to read next," Matilda said. "I've +finished all the children's books." +"You mean you've looked at the pictures?" +"Yes, but I've read the books as well." +Mrs Phelps looked down at Matilda from her great height +and Matilda looked right back up at her. +"I thought some were very poor," Matilda said, "but others +were lovely. I liked The Secret Garden best of all. It was full of +mystery. The mystery of the room behind the closed door and +the mystery of the garden behind the big wall." +Mrs Phelps was stunned. ''Exactly how old are you, +Matilda?" she asked. +"Four years and three months," Matilda said. +Mrs Phelps was more stunned than ever, but she had the +sense not to show it. "What sort of a book would you like to +read next?" she asked. +Matilda said, "I would like a really good one that grownups read. A famous one. I don't know any names." +Mrs Phelps looked along the shelves, taking her time. She +didn't quite know what to bring out. How, she asked herself, +does one choose a famous grown-up book for a four-year-old +girl? Her first thought was to pick a young teenager's +romance of the kind that is written for fifteen-year-old +schoolgirls, but for some reason she found herself instinctively walking past that particular shelf. +"Try this," she said at last. "It's very famous and very good. +If it's too long for you, just let me know and I'll find +something shorter and a bit easier." +"Great Expectations," Matilda read, "by Charles Dickens. +I'd love to try it." +I must be mad, Mrs Phelps told herself, but to Matilda she +said, "Of course you may try it." +Over the next few afternoons Mrs Phelps could hardly take +her eyes from the small girl sitting for hour after hour in the +big armchair at the far end of the room with the book on her +lap. It was necessary to rest it on the lap because it was too +heavy for her to hold up, which meant she had to sit leaning +forward in order to read. And a strange sight it was, this tiny +dark-haired person sitting there with her feet nowhere near +touching the floor, totally absorbed in the wonderful +adventures of Pip and old Miss Havisham and her cobwebbed +house and by the spell of magic that Dickens the great storyteller had woven with his words. The only movement from +the reader was the lifting of the hand every now and then to +turn over a page, and Mrs Phelps always felt sad when the +time came for her to cross the floor and say; "It's ten to five, +Matilda." +During the first week of Matilda's visits Mrs Phelps had +said to her, "Does your mother walk you down here every day +and then take you home?" +"My mother goes to Aylesbury every afternoon to play +bingo," Matilda had said. "She doesn't know I come here." +"But that's surely not right," Mrs Phelps said. "I think you'd +better ask her." +"I'd rather not," Matilda said. "She doesn't encourage +reading books. Nor does my father." +"But what do they expect you to do every afternoon in an +empty house?" +"Just mooch around and watch the telly." +"I see." +"She doesn't really care what I do," Matilda said a little +sadly. +Mrs Phelps was concerned about the child's safety on the +walk through the fairly busy village High Street and the +crossing of the road, but she decided not to interfere. +Within a week, Matilda had finished Great Expectations +which in that edition contained four hundred and eleven +pages. "I loved it," she said to Mrs Phelps. "Has Mr Dickens +written any others?" +"A great number," said the astounded Mrs Phelps. "Shall I +choose you another?" +Over the next six months, under Mrs Phelps's +watchful and compassionate eye, Matilda read the following +books: + +Nicholas Nickleby by Charles Dickens +Oliver Twist by Charles Dickens +Jane Eyre by Charlotte Bronte +Pride and Prejudice by Jane Austen +Tess of the D'Urbervilles by Thomas Hardy +Gone to Earth by Mary Webb +Kim by Rudyard Kipling +The Invisible Man by H. G. Wells +The Old Man and the Sea by Ernest Hemingway +The Sound and the Fury by William Faulkner +The Grapes of Wrath by John Steinbeck +The Good Companions by J. B. Priestley +Brighton Rock by Graham Greene +Animal Farm by George Orwell + +It was a formidable list and by now Mrs Phelps was filled +with wonder and excitement, but it was probably a good thing +that she did not allow herself to be completely carried away +by it all. Almost anyone else witnessing the achievements of +this small child would have been tempted to make a great fuss +and shout the news all over the village and beyond, but not so +Mrs Phelps. She was someone who minded her own business +and had long since discovered it was seldom worth while to +interfere with other people's children. +"Mr Hemingway says a lot of things I don't understand," +Matilda said to her. "Especially about men and women. But I +loved it all the same. The way he tells it I feel I am right there +on the spot watching it all happen." +''A fine writer will always make you feel that," Mrs Phelps +said. "And don't worry about the bits you can't understand. +Sit back and allow the words to wash around you, like music." +"I will, I will." +"Did you know", Mrs Phelps said, "that public libraries like +this allow you to borrow books and take them home?" +"I didn't know that," Matilda said. "Could I do it?" +"Of course," Mrs Phelps said. "When you have chosen the +book you want, bring it to me so I can make a note of it and +it's yours for two weeks. You can take more than one if you +wish." + +From then on, Matilda would visit the library only once a +week in order to take out new books and return the old ones. +Her own small bedroom now became her reading-room and +there she would sit and read most afternoons, often with a +mug of hot chocolate beside her. She was not quite tall +enough to reach things around the kitchen, but she kept a +small box in the outhouse which she brought in and stood on +in order to get whatever she wanted. Mostly it was hot +chocolate she made, warming the milk in a saucepan on the +stove before mixing it. Occasionally she made Bovril or +Ovaltine. It was pleasant to take a hot drink up to her room +and have it beside her as she sat in her silent room reading in +the empty house in the afternoons. The books transported her +into new worlds and introduced her to amazing people who +lived exciting lives. She went on olden-day sailing ships with +Joseph Conrad. She went to Africa with Ernest Hemingway +and to India with Rudyard Kipling. She travelled all over the +world while sitting in her little room in an English village. +''' + +ij_chapter1 = '''I am seated in an office, surrounded by heads and bodies. My posture is consciously congruent to the shape of my hard chair. +This is a cold room in University Administration, wood-walled, Remington-hung, double-windowed against the November heat, +insulated from Administrative sounds by the reception area outside, at which Uncle Charles, Mr. deLint and I were lately received. +I am in here. +Three faces have resolved into place above summer-weight sportcoats and half-Windsors across a polished pine conference +table shiny with the spidered light of an Arizona noon. These are three Deans — of Admissions, Academic Affairs, Athletic +Affairs. I do not know which face belongs to whom. +I believe I appear neutral, maybe even pleasant, though I've been coached to err on the side of neutrality and not attempt what +would feel to me like a pleasant expression or smile. +I have committed to crossing my legs I hope carefully, ankle on knee, hands together in the lap of my slacks. My fingers are +mated into a mirrored series of what manifests, to me, as the letter X. The interview room's other personnel include: the +University's Director of Composition, its varsity tennis coach, and Academy prorector Mr. A. deLint. C.T. is beside me; the others +sit, stand and stand, respectively, at the periphery of my focus. The tennis coach jingles pocket-change. There is something +vaguely digestive about the room's odor. The high-traction sole of my complimentary Nike sneaker runs parallel to the wobbling +loafer of my mother's half-brother, here in his capacity as Headmaster, sitting in the chair to what I hope is my immediate right, +also facing Deans. +The Dean at left, a lean yellowish man whose fixed smile nevertheless has the impermanent quality of something stamped +into uncooperative material, is a personality-type I've come lately to appreciate, the type who delays need of any response from +me by relating my side of the story for me, to me. Passed a packet of computer-sheets by the shaggy lion of a Dean at center, he is +speaking more or less to these pages, smiling down. +'You are Harold Incandenza, eighteen, date of secondary-school graduation approximately one month from now, attending the +Enfield Tennis Academy, Enfield, Massachusetts, a boarding school, where you reside.' His reading glasses are rectangular, courtshaped, the sidelines at top and bottom. 'You are, according to Coach White and Dean [unintelligible], a regionally, nationally, +and continentally ranked junior tennis player, a potential O.N.A.N.C.A.A. athlete of substantial promise, recruited by Coach +White via correspondence with Dr. Tavis here commencing .. . February of this year.' The top page is removed and brought +around neatly to the bottom of the sheaf, at intervals. 'You have been in residence at the Enfield Tennis Academy since age +seven.’ +I am debating whether to risk scratching the right side of my jaw, where there is a wen. +'Coach White informs our offices that he holds the Enfield Tennis Academy's program and achievements in high regard, that +the University of Arizona tennis squad has profited from the prior matriculation of several former E.T.A. alumni, one of whom +was one Mr. Aubrey F. deLint, who appears also to be with you here today. Coach White and his staff have given us —’ +The yellow administrator's usage is on the whole undistinguished, though I have to admit he's made himself understood. The +Director of Composition seems to have more than the normal number of eyebrows. The Dean at right is looking at my face a bit +strangely. +Uncle Charles is saying that though he can anticipate that the Deans might be predisposed to weigh what he avers as coming +from his possible appearance as a kind of cheerleader for E.T.A., he can assure the assembled Deans that all this is true, and that +the Academy has presently in residence no fewer than a third of the continent's top thirty juniors, in age brackets all across the +board, and that I here, who go by 'Hal,' usually, am 'right up there among the very cream.' Right and center Deans smile +professionally; the heads of deLint and the coach incline as the Dean at left clears his throat: +'— belief that you could well make, even as a freshman, a real contribution to this University's varsity tennis program. We are +pleased,' he either says or reads, removing a page, 'that a competition of some major sort here has brought you down and given us +the chance to sit down and chat together about your application and potential recruitment and matriculation and scholarship.’ +'I've been asked to add that Hal here is seeded third, Boys' 18-and-Under Singles, in the prestigious WhataBurger Southwest +Junior Invitational out at the Randolph Tennis Center —' says what I infer is Athletic Affairs, his cocked head showing a freckled +scalp. +'Out at Randolph Park, near the outstanding El Con Marriott,' C.T. inserts, 'a venue the whole contingent's been vocal about +finding absolutely top-hole thus far, which —’ +'Just so, Chuck, and that according to Chuck here Hal has already justified his seed, he's reached the semifinals as of this +morning's apparently impressive win, and that he'll be playing out at the Center again tomorrow, against the winner of a +quarterfinal game tonight, and so will be playing tomorrow at I believe scheduled for 0830 —’ +'Try to get under way before the godawful heat out there. Though of course a dry heat.’ +'— and has apparently already qualified for this winter's Continental Indoors, up in Edmonton, Kirk tells me —' cocking +further to look up and left at the varsity coach, whose smile's teeth are radiant against a violent sunburn — 'Which is something +indeed.' He smiles, looking at me. 'Did we get all that right Hal.’ +C.T. has crossed his arms casually; their triceps' flesh is webbed with mottle in the air-conditioned sunlight. 'You sure did. +Bill.' He smiles. The two halves of his mustache never quite match. 'And let me say if I may that Hal's excited, excited to be +invited for the third year running to the Invitational again, to be back here in a community he has real affection for, to visit with +your alumni and coaching staff, to have already justified his high seed in this week's not unstiff competition, to as they say still be +in it without the fat woman in the Viking hat having sung, so to speak, but of course most of all to have a chance to meet you +gentlemen and have a look at the facilities here. Everything here is absolutely top-slot, from what he's seen.’ +There is a silence. DeLint shifts his back against the room's panelling and recenters his weight. My uncle beams and +straightens a straight watchband. 62.5% of the room's faces are directed my way, pleasantly expectant. My chest bumps like a +dryer with shoes in it. I compose what I project will be seen as a smile. I turn this way and that, slightly, sort of directing the +expression to everyone in the room. +There is a new silence. The yellow Dean's eyebrows go circumflex. The two other Deans look to the Director of Composition. +The tennis coach has moved to stand at the broad window, feeling at the back of his crewcut. Uncle Charles strokes the forearm +above his watch. Sharp curved palm-shadows move slightly over the pine table's shine, the one head's shadow a black moon. +'Is Hal all right, Chuck?' Athletic Affairs asks. 'Hal just seemed to ... well, grimace. Is he in pain? Are you in pain, son?’ +'Hal's right as rain,' smiles my uncle, soothing the air with a casual hand. 'Just a bit of a let's call it maybe a facial tic, slightly, +at all the adrenaline of being here on your impressive campus, justifying his seed so far without dropping a set, receiving that +official written offer of not only waivers but a living allowance from Coach White here, on Pac 10 letterhead, being ready in all +probability to sign a National Letter of Intent right here and now this very day, he's indicated to me.' C.T. looks to me, his look +horribly mild. I do the safe thing, relaxing every muscle in my face, emptying out all expression. I stare carefully into the +Kekuléan knot of the middle Dean's necktie. +My silent response to the expectant silence begins to affect the air of the room, the bits of dust and sportcoat-lint stirred +around by the AC's vents dancing jaggedly in the slanted plane of windowlight, the air over the table like the sparkling space just +above a fresh-poured seltzer. The coach, in a slight accent neither British nor Australian, is telling C.T. that the whole applicationinterface process, while usually just a pleasant formality, is probably best accentuated by letting the applicant speak up for +himself. Right and center Deans have inclined together in soft conference, forming a kind of tepee of skin and hair. I presume it's +probably facilitate that the tennis coach mistook for accentuate, though accelerate, while clunkier than facilitate, is from a phonetic +perspective more sensible, as a mistake. The Dean with the flat yellow face has leaned forward, his lips drawn back from his teeth +in what I see as concern. His hands come together on the conference table's surface. His own fingers look like they mate as my +own four-X series dissolves and I hold tight to the sides of my chair. +We need candidly to chat re potential problems with my application, they and I, he is beginning to say. He makes a reference +to candor and its value. +'The issues my office faces with the application materials on file from you, Hal, involve some test scores.' He glances down at +a colorful sheet of standardized scores in the trench his arms have made. 'The Admissions staff is looking at standardized test +scores from you that are, as I'm sure you know and can explain, are, shall we say ... subnormal.' I'm to explain. +It's clear that this really pretty sincere yellow Dean at left is Admissions. And surely the little aviarian figure at right is +Athletics, then, because the facial creases of the shaggy middle Dean are now pursed in a kind of distanced affront, an I'm-eatingsomething-that-makes-me-really-appreciate-the-presence-of-whatever-I'm-drinking-along-with-it look that spells professionally +Academic reservations. An uncomplicated loyalty to standards, then, at center. My uncle looks to Athletics as if puzzled. He shifts +slightly in his chair. +The incongruity between Admissions's hand- and face-color is almost wild. '—verbal scores that are just quite a bit closer to +zero than we're comfortable with, as against a secondary-school transcript from the institution where both your mother and her +brother are administrators —' reading directly out of the sheaf inside his arms' ellipse — 'that this past year, yes, has fallen off a +bit, but by the word I mean "fallen off" to outstanding from three previous years of frankly incredible.’ +'Off the charts.’ +'Most institutions do not even have grades of A with multiple pluses after it,' says the Director of Composition, his expression +impossible to interpret. +'This kind of ... how shall I put it... incongruity,' Admissions says, his expression frank and concerned, 'I've got to tell you +sends up a red flag of potential concern during the admissions process.’ +'We thus invite you to explain the appearance of incongruity if not outright shenanigans.' Students has a tiny piping voice +that's absurd coming out of a face this big. +'Surely by incredible you meant very very very impressive, as opposed to literally quote "incredible," surely,' says C.T., +seeming to watch the coach at the window massaging the back of his neck. The huge window gives out on nothing more than +dazzling sunlight and cracked earth with heat-shimmers over it. +'Then there is before us the matter of not the required two but nine separate application essays, some of which of nearly +monograph-length, each without exception being —' different sheet — 'the adjective various evalua-tors used was quote "stellar" +—’ +Dir. of Comp.: 'I made in my assessment deliberate use of lapidary and effete.’ +'— but in areas and with titles, I'm sure you recall quite well, Hal: "Neoclassical Assumptions in Contemporary Prescriptive +Grammar," "The Implications of Post-Fourier Transformations for a Holographically Mimetic Cinema," "The Emergence of +Heroic Stasis in Broadcast Entertainment" —’ +' "Montague Grammar and the Semantics of Physical Modality"?’ +' "A Man Who Began to Suspect He Was Made of Glass"?’ +' "Tertiary Symbolism in Justinian Erotica"?’ +Now showing broad expanses of recessed gum. 'Suffice to say that there's some frank and candid concern about the recipient +of these unfortunate test scores, though perhaps explainable test scores, being these essays' sole individual author.’ +'I'm not sure Hal's sure just what's being implied here,' my uncle says. The Dean at center is fingering his lapels as he +interprets distasteful computed data. +'What the University is saying here is that from a strictly academic point of view there are admission problems that Hal needs +to try to help us iron out. A matriculant's first role at the University is and must be as a student. We couldn't admit a student we +have reason to suspect can't cut the mustard, no matter how much of an asset he might be on the field.’ +'Dean Sawyer means the court, of course, Chuck,' Athletic Affairs says, head severely cocked so he's including the White +person behind him in the address somehow. 'Not to mention O.N.A.N.C.A.A. regulations and investigators always snuffling +around for some sort of whiff of the smell of impropriety.’ +The varsity tennis coach looks at his own watch. +'Assuming these board scores are accurate reflectors of true capacity in this case,' Academic Affairs says, his high voice +serious and sotto, still looking at the file before him as if it were a plate of something bad, Til tell you right now my opinion is it +wouldn't be fair. It wouldn't be fair to the other applicants. Wouldn't be fair to the University community.' He looks at me. 'And +it'd be especially unfair to Hal himself. Admitting a boy we see as simply an athletic asset would amount to just using that boy. +We're under myriad scrutiny to make sure we're not using anybody. Your board results, son, indicate that we could be accused of +using you.’ +Uncle Charles is asking Coach White to ask the Dean of Athletic Affairs whether the weather over scores would be as heavy +if I were, say, a revenue-raising football prodigy. The familiar panic at feeling misperceived is rising, and my chest bumps and +thuds. I expend energy on remaining utterly silent in my chair, empty, my eyes two great pale zeros. People have promised to get +me through this. +Uncle C.T., though, has the pinched look of the cornered. His voice takes on an odd timbre when he's cornered, as if he were +shouting as he receded. 'Hal's grades at E.T.A., which is I should stress an Academy, not simply a camp or factory, accredited by +both the Commonwealth of Massachusetts and the North American Sports Academy Association, it's focused on the total needs of +the player and student, founded by a towering intellectual figure whom I hardly need name, here, and based by him on the +rigorous Oxbridge Quadrivium-Trivium curricular model, a school fully staffed and equipped, by a fully certified staff, should +show that my nephew here can cut just about any Pac 10 mustard that needs cutting, and that —’ +DeLint is moving toward the tennis coach, who is shaking his head. +'— would be able to see a distinct flavor of minor-sport prejudice about this whole thing,' C.T. says, crossing and recrossing +his legs as I listen, composed and staring. +The room's carbonated silence is now hostile. T think it's time to let the actual applicant himself speak out on his own behalf,' +Academic Affairs says very quietly. 'This seems somehow impossible with you here, sir.’ +Athletics smiles tiredly under a hand that massages the bridge of his nose. 'Maybe you'd excuse us for a moment and wait +outside, Chuck.’ +'Coach White could accompany Mr. Tavis and his associate out to reception,' the yellow Dean says, smiling into my +unfocused eyes. +'— led to believe this had all been ironed out in advance, from the —' C.T. is saying as he and deLint are shown to the door. +The tennis coach extends a hypertrophied arm. Athletics says 'We're all friends and colleagues here.’ +This is not working out. It strikes me that exit signs would look to a native speaker of Latin like red-lit signs that say HE +leaves. I would yield to the urge to bolt for the door ahead of them if I could know that bolting for the door is what the men in this +room would see. DeLint is murmuring something to the tennis coach. Sounds of keyboards, phone consoles as the door is briefly +opened, then firmly shut. I am alone among administrative heads. +'— offense intended to anyone,' Athletic Affairs is saying, his sportcoat tan and his necktie insigniated in tiny print — 'beyond +just physical abilities out there in play, which believe me we respect, want, believe me.’ +'— question about it we wouldn't be so anxious to chat with you directly, see?’ +'— that we've known in processing several prior applications through Coach White's office that the Enfield School is +operated, however impressively, by close relations of first your brother, who I can still remember the way White's predecessor +Maury Klamkin wooed that kid, so that grades' objectivity can be all too easily called into question —’ +'By whomsoever's calling — N.A.A.U.P., ill-willed Pac 10 programs, O.N.A.N.C.A.A. —’ +The essays are old ones, yes, but they are mine; de moi. But they are, yes, old, not quite on the application's instructed subject +of Most Meaningful Educational Experience Ever. If I'd done you one from the last year, it would look to you like some sort of +infant's random stabs on a keyboard, and to you, who use whomsoever as a subject. And in this new smaller company, the +Director of Composition seems abruptly to have actuated, emerged as both the Alpha of the pack here and way more effeminate +than he'd seemed at first, standing hip-shot with a hand on his waist, walking with a roll to his shoulders, jingling change as he +pulls up his pants as he slides into the chair still warm from C.T.'s bottom, crossing his legs in a way that inclines him well into +my personal space, so that I can see multiple eyebrow-tics and capillary webs in the oysters below his eyes and smell fabricsoftener and the remains of a breath-mint turned sour. +'. . . a bright, solid, but very shy boy, we know about your being very shy, Kirk White's told us what your athletically built if +rather stand-offish younger instructor told him,' the Director says softly, cupping what I feel to be a hand over my sportcoat's +biceps (surely not), 'who simply needs to swallow hard and trust and tell his side of the story to these gentlemen who bear no +maliciousness none at all but are doing our jobs and trying to look out for everyone's interests at the same time.’ +I can picture deLint and White sitting with their elbows on their knees in the defecatory posture of all athletes at rest, deLint +staring at his huge thumbs, while C.T. in the reception area paces in a tight ellipse, speaking into his portable phone. I have been +coached for this like a Don before a RICO hearing. A neutral and affectless silence. The sort of all-defensive game Schtitt used to +have me play: the best defense: let everything bounce off you; do nothing. I'd tell you all you want and more, if the sounds I made +could be what you hear. +Athletics with his head out from under his wing: '— to avoid admission procedures that could be seen as primarily athleticsoriented. It could be a mess, son.’ +'Bill means the appearance, not necessarily the real true facts of the matter, which you alone can fill in,' says the Director of +Composition. +'— the appearance of the high athletic ranking, the subnormal scores, the over-academic essays, the incredible grades +vortexing out of what could be seen as a nepotistic situation.’ +The yellow Dean has leaned so far forward that his tie is going to have a horizontal dent from the table-edge, his face sallow +and kindly and no-shit-whatever: +'Look here, Mr. Incandenza, Hal, please just explain to me why we couldn't be accused of using you, son. Why nobody could +come and say to us, why, look here, University of Arizona, here you are using a boy for just his body, a boy so shy and withdrawn +he won't speak up for himself, a jock with doctored marks and a store-bought application.’ +The Brewster's-Angle light of the tabletop appears as a rose flush behind my closed lids. I cannot make myself understood. 'I +am not just a jock,' I say slowly. Distinctly. 'My transcript for the last year might have been dickied a bit, maybe, but that was to +get me over a rough spot. The grades prior to that are de moi.' My eyes are closed; the room is silent. 'I cannot make myself +understood, now.' I am speaking slowly and distinctly. 'Call it something I ate.’ +It's funny what you don't recall. Our first home, in the suburb of Weston, which I barely remember — my eldest brother Orin +says he can remember being in the home's backyard with our mother in the early spring, helping the Moms till some sort of garden +out of the cold yard. March or early April. The garden's area was a rough rectangle laid out with Popsicle sticks and twine. Orin +was removing rocks and hard clods from the Moms's path as she worked the rented Rototiller, a wheelbarrow-shaped, gas-driven +thing that roared and snorted and bucked and he remembers seemed to propel the Moms rather than vice versa, the Moms very tall +and having to stoop painfully to hold on, her feet leaving drunken prints in the tilled earth. He remembers that in the middle of the +tilling I came tear-assing out the door and into the backyard wearing some sort of fuzzy red Pooh-wear, crying, holding out +something he said was really unpleasant-looking in my upturned palm. He says I was around five and crying and was vividly red +in the cold spring air. I was saying something over and over; he couldn't make it out until our mother saw me and shut down the +tiller, ears ringing, and came over to see what I was holding out. This turned out to have been a large patch of mold — Orin posits +from some dark corner of the Weston home's basement, which was warm from the furnace and flooded every spring. The patch +itself he describes as horrific: darkly green, glossy, vaguely hirsute, speckled with parasitic fungal points of yellow, orange, red. +Worse, they could see that the patch looked oddly incomplete, gnawed-on; and some of the nauseous stuff was smeared around +my open mouth. 'I ate this,' was what I was saying. I held the patch out to the Moms, who had her contacts out for the dirty work, +and at first, bending way down, saw only her crying child, hand out, proffering; and in that most maternal of reflexes she, who +feared and loathed more than anything spoilage and filth, reached to take whatever her baby held out — as in how many used +heavy Kleenex, spit-back candies, wads of chewed-out gum in how many theaters, airports, backseats, tournament lounges? O. +stood there, he says, hefting a cold clod, playing with the Velcro on his puffy coat, watching as the Moms, bent way down to me, +hand reaching, her lowering face with its presbyopic squint, suddenly stopped, froze, beginning to I.D. what it was I held out, +countenancing evidence of oral contact with same. He remembers her face as past describing. Her outstretched hand, still +Rototrembling, hung in the air before mine. +'I ate this,' I said. +'Pardon me?’ +O. says he can only remember (sic) saying something caustic as he lim-boed a crick out of his back. He says he must have felt +a terrible impending anxiety. The Moms refused ever even to go into the damp basement. I had stopped crying, he remembers, and +simply stood there, the size and shape of a hydrant, in red PJ's with attached feet, holding out the mold, seriously, like the report of +some kind of audit. +O. says his memory diverges at this point, probably as a result of anxiety. In his first memory, the Moms's path around the +yard is a broad circle of hysteria: +'God!' she calls out. +'Help! My son ate this!' she yells in Orin's second and more fleshed-out recollection, yelling it over and over, holding the +speckled patch aloft in a pincer of fingers, running around and around the garden's rectangle while O. gaped at his first real sight +of adult hysteria. Suburban neighbors' heads appeared in windows and over the fences, looking. O. remembers me tripping over +the garden's laid-out twine, getting up dirty, crying, trying to follow. +'God! Help! My son ate this! Help!' she kept yelling, running a tight pattern just inside the square of string; and my brother +Orin remembers noting how even in hysterical trauma her flight-lines were plumb, her footprints Native-American-straight, her +turns, inside the ideogram of string, crisp and martial, crying 'My son ate this! Help!' and lapping me twice before the memory +recedes. +'My application's not bought,' I am telling them, calling into the darkness of the red cave that opens out before closed eyes. 'I +am not just a boy who plays tennis. I have an intricate history. Experiences and feelings. I'm complex. +'I read,' I say. 'I study and read. I bet I've read everything you've read. Don't think I haven't. I consume libraries. I wear out +spines and ROM-drives. I do things like get in a taxi and say, "The library, and step on it." My instincts concerning syntax and +mechanics are better than your own, I can tell, with due respect. +'But it transcends the mechanics. I'm not a machine. I feel and believe. I have opinions. Some of them are interesting. I could, +if you'd let me, talk and talk. Let's talk about anything. I believe the influence of Kierkegaard on Camus is underestimated. I +believe Dennis Gabor may very well have been the Antichrist. I believe Hobbes is just Rousseau in a dark mirror. I believe, with +Hegel, that transcendence is absorption. I could interface you guys right under the table,' I say. 'I'm not just a creãtus, +manufactured, conditioned, bred for a function.’ +I open my eyes. 'Please don't think I don't care.’ +I look out. Directed my way is horror. I rise from the chair. I see jowls sagging, eyebrows high on trembling foreheads, +cheeks bright-white. The chair recedes below me. +'Sweet mother of Christ,' the Director says. +T'm fine,' I tell them, standing. From the yellow Dean's expression, there's a brutal wind blowing from my direction. +Academics' face has gone instantly old. Eight eyes have become blank discs that stare at whatever they see. +'Good God,' whispers Athletics. +'Please don't worry,' I say. 'I can explain.' I soothe the air with a casual hand. +Both my arms are pinioned from behind by the Director of Comp., who wrestles me roughly down, on me with all his weight. +I taste floor. +'What's wrong?’ +I say 'Nothing is wrong.’ +''' diff --git a/lessons/Extra exercises day 1.ipynb b/lessons/Extra exercises day 1.ipynb index e4e73a8..c156f9c 100644 --- a/lessons/Extra exercises day 1.ipynb +++ b/lessons/Extra exercises day 1.ipynb @@ -509,7 +509,6 @@ "pygments_lexer": "ipython3", "version": "3.11.2" }, - "orig_nbformat": 4, "vscode": { "interpreter": { "hash": "2cc67e3a9982c67c9491f847535e93961a1a3bd6725cdcb43113c9880de5dac3" diff --git a/lessons/Extra exercises day 1.py b/lessons/Extra exercises day 1.py new file mode 100644 index 0000000..744b2a3 --- /dev/null +++ b/lessons/Extra exercises day 1.py @@ -0,0 +1,175 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# language: python +# name: python3 +# --- + +# %% [markdown] +# Hierbij wat extra opdrachtjes voor de modules van dag 1! + +# %% [markdown] +# # Types +# ## Exercise A +# useful for after 1.4 +# +# 1. Create one variable each for every following type (replace None with an example): +# - Integer (`int`) +# - Floating point (`float`) +# - Boolean (`bool`) +# - String (`str`) + +# %% +example_int = None +example_float = None +example_bool = None +example_string = None + +# %% [markdown] +# Did you know that you can check the type of a variable in Python? If you are ever unsure of the type of a variable, use the `type()` function to check, e.g.: + +# %% +print(type(1)) +print(type(1.0)) +print(type(False)) +print(type('one')) + +# %% [markdown] +# 2. Now try and check the types of the examples you just declared using the method above: + +# %% +## Space for exercise + +# %% [markdown] +# Python is a 'dynamically typed' language, which means that the type of a variable can vary based on its context or user coercion. For example, you can coerce an integer to a float by declaring: + +# %% +example_integer = 2 # declare example integer +print(example_integer, type(example_integer)) #print the integer and its type + +coerced_float = float(example_integer) +print(coerced_float, type(coerced_float)) + +# %% [markdown] +# You can coerce these types manually or Python sometimes does it automatically, for example when you try and execute operations on a variable that requires a certain type. For instance, if you try to add the integer and float above, the result of that operation will be a float. Python automatically coerces the integer to a float to complete the addition. +# +# 3. For this exercise, try to perform some operations on these variables below. Which types can be automatically added together? And what type is the result of their operation? What happens when you try a different operation, such as subtraction? + +# %% +ex_int = 1 +ex_float = 2.0 +ex_string = 'three' +ex_bool = True + +ex_result = ex_int + ex_float # example operation +print(ex_result, type(ex_result)) + +# %% [markdown] +# # Lists +# ## Exercise B +# Let's make some lists! +# 1. Start by declaring an empty list (`empty_list = []`). + +# %% +my_list = None + +# %% [markdown] +# You can check the length of the list with the `len()` function: + +# %% +print(len(my_list)) + +# %% [markdown] +# 2. Now, add some elements to your list. Then check the length of the list again! + +# %% +## Space for exercise + +# %% [markdown] +# # Loops +# ## Exercise C + +# %% [markdown] +# 1. Now that you have your list, let's walk through it and print every element in it. Hint: use the `for` loop. + +# %% +## Space for exercise + +# %% [markdown] +# 2. Instead of simply printing the elements, see if you can print the result of an operation with each element (for example, print the element + 1). Alternatively, see if you can print the type of each element in the list. + +# %% +## Space for exercise + +# %% [markdown] +# # Logical Operators +# ## Exercise D +# Python allows the use of several logical operators, so to get you in the mindset of a computer, see if you can determine whether an expression is going to return `True` or `False`. After you have given your answer, run the code in the cell to see if you were correct! + +# %% +# Declaration of the variables used in the exercise +n1 = 1 +n2 = 2 +n3 = 3.4 +n4 = 4.5 +s1 = 'Hello' +s2 = 'World' +s3 = 'Hallo' +s4 = 'Wereld' +l1 = [n1, n2, n3, n4] +l2 = [s1, s2, s3, s4] + +# %% +#Example 1 +n1 == n2 + +# %% [markdown] +# True or False? + +# %% +#Example 2 +n1 + n2 == 3 + +# %% +#Example 3 +n1 + n3 != 3.4 + +# %% [markdown] +# True or False? + +# %% +#Example 4 +s1 + s2 == "Hello World" + +# %% [markdown] +# True or False? + +# %% +#Example 5 +s3 == "hallo" + +# %% [markdown] +# True or False? + +# %% +#Example 6 +len(l1) == len(l2) + +# %% [markdown] +# True or False? + +# %% +#Example 7 +len(s3) == len(s1) + +# %% +##Example 8 +for item in l2: + print(len(item) == 5) diff --git a/lessons/Project - text analysis.ipynb b/lessons/Project - text analysis.ipynb index a10bb14..35fe994 100644 --- a/lessons/Project - text analysis.ipynb +++ b/lessons/Project - text analysis.ipynb @@ -1 +1,254 @@ -{"cells":[{"cell_type":"markdown","metadata":{"id":"kIc3b-0fqM6n"},"source":["# Project - Text Analysis\n","In this series of exercises you will develop a text analysis tool. The steps roughly simulate real-life Digital Humanities projects:\n","- Obtaining data\n","- Cleaning data\n","- Running analyses\n","\n"]},{"cell_type":"markdown","metadata":{"id":"OSpUjXmAqJI9"},"source":["> 💡 The exercises are very useful and fun if you would like to analyse textual data. The code you write here can be reused in your own analysis.\n","\n","> ⚠️ The exercises are difficult and may take you some time to complete. Consider them a bonus, study them at home, and don't be hestitant to contact the instructors, during or after the course. If you are not interested in text-analysis, ask the instructors for similarly challenging projects on a different topic, or pursue your own ideas!\n"]},{"cell_type":"markdown","metadata":{"id":"v1MTwBNTpTcN"},"source":["## Exercise 1: Cleaning interpunction\n","Implement the function `clean_interpunction` in a way that satisfies the docstrings and assertions"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"fOZBq4OLp2Lz"},"outputs":[],"source":["def clean_interpunction(string):\n"," \"\"\"Pads words and sentence-ending interpunction with a space.\n"," Other interpunction should be removed.\n","\n"," The following characters are considered sentence-ending interpunction:\n"," - .\n"," - ?\n"," - !\n","\n"," Other interpunction that should be removed:\n"," - ,\n"," - ;\n"," - :\n"," \"\"\"\n"," output_string = None # replace this line by your own lines of code\n"," return output_string\n","\n","simple_sent = 'A sentence.'\n","complex_sent = 'A sentence, with a dependent clause.'\n","assert clean_interpunction(simple_sent) == 'A sentence .'\n","assert clean_interpunction(complex_sent) == 'A sentence with a dependent clause .'\n","print('All cases passed! The function works.')"]},{"cell_type":"markdown","metadata":{"id":"U8QVnwgwqTR6"},"source":["## Exercise 2: only_periods\n","Add an optional parameter named `only_periods` to `clean_interpunction`. \n","When this parameter is `True`, all sentence-ending interpunction should be changed to a period. \n","The use of this will become clear in the next exercise."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"TdLKVbsvqhGb"},"outputs":[],"source":["def clean_interpunction(string, only_periods=None):\n"," \"\"\"Pads words and sentence-ending interpunction with a space.\n"," Other interpunction should be removed.\n"," If only_periods is True, replace all sentence ending interpunction by a period.\n","\n"," The following characters are considered sentence-ending interpunction:\n"," - .\n"," - ?\n"," - !\n","\n"," Other interpunction that should be removed:\n"," - ,\n"," - ;\n"," - :\n"," \"\"\"\n"," output_string = None # replace this line by your own lines of code\n"," return output_string\n","\n","simple_sent = 'A sentence?'\n","complex_sent = 'A sentence, with a dependent clause!'\n","assert clean_interpunction(simple_sent) == 'A sentence ?'\n","assert clean_interpunction(complex_sent) == 'A sentence with a dependent clause !'\n","assert clean_interpunction(simple_sent, only_periods=True) == 'A sentence .'\n","assert clean_interpunction(complex_sent, only_periods=True) == 'A sentence with a dependent clause .'\n","print('All cases passed! The function works.')"]},{"cell_type":"markdown","metadata":{"id":"f5q3jw66q3EG"},"source":["## Exercise 3: Split into sentences\n","Using the results of the previous exercises, implement a function that satisfies the docstring and assertions:"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"5lTZXUgwq6lW"},"outputs":[],"source":["def split_sentences(text):\n"," \"\"\"Takes a single string and splits it into sentences.\n"," Sentences are lists containing words (as strings).\n"," Sentences are split on periods.\n"," Other interpunction is removed.\n","\n"," Returns a nested list of sentences and words\n"," \"\"\"\n"," # your code here\n"," # use your implementation of clean_interpunction\n"," sentences = None\n","\n"," # return the nested list\n"," return sentences\n","\n","\n","\n","text = 'The novel details two days in the life of 16-year-old Holden Caulfield after he has been expelled from prep school. Confused and disillusioned, Holden searches for truth and rails against the “phoniness” of the adult world.'\n","expected = [['The', 'novel', 'details', 'two', 'days', 'in', 'the', 'life', 'of', '16-year-old', 'Holden', 'Caulfield', 'after', 'he', 'has', 'been', 'expelled', 'from', 'prep', 'school'], \n"," ['Confused', 'and', 'disillusioned', 'Holden', 'searches', 'for', 'truth', 'and', 'rails', 'against', 'the', '“phoniness”', 'of', 'the', 'adult', 'world'], \n"," ]\n","\n","# If no errors occur, you solved it correctly!\n","split_text = split_sentences(text)\n","assert len(split_text) == 2\n","assert split_text == expected"]},{"cell_type":"markdown","metadata":{"id":"Um-nFxcTq1WA"},"source":["## Exercise 4 - Putting it all together"]},{"cell_type":"markdown","metadata":{"id":"YNbDTnBrheRq"},"source":["### The data\n","In the `class materials` section of the [Teams channel](https://teams.microsoft.com/l/channel/19%3aAlr7pcE2v9p1eaKT1BtNxJAB9mgCFT1olZvAMswJlEc1%40thread.tacv2/Algemeen?groupId=7fd15384-6748-4852-8646-d96963fb3524&tenantId=d72758a0-a446-4e0f-a0aa-4bf95a4a10e7) you will find three `.txt`files:\n","\n","\n","- `catcher_chapter1.txt`, containing the first chapter of The Catcher in the Rye by J.D. Salinger\n","- `matilda_chapter1.txt`, containing the first chapter of Matilda by Roald Dahl\n","- `ij_chapter1.txt`, containing a part of the first chapter (the chapters are way too long) of Inifite Jest by David Foster Wallace.\n","\n","Write some code that loads the whole content of each of these files into its corresponding variable, see code cell below.\n","\n","We will use these variables in the rest of the notebook. Make sure you've executed this cell at least once.\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"q_58MJYQadJO"},"outputs":[],"source":["catcher_chapter1 = ''\n","matilda_chapter1 = ''\n","ij_chapter1 = ''"]},{"cell_type":"markdown","metadata":{"id":"EuaRmuhwhTIZ"},"source":["### Writing functions\n","Implement a few function to use in the final analysis:\n","\n","1. Implement a function that counts the total number of words given a string containing a full text.\n","2. Implement a function that calculates the average sentence length of a text. The sentence length is defined as the number of words in a sentence. Use your implementation of `split_sentences`.1\n","3. Implement a function that calculates the average word length of a text. Again, use your implementation of `split sentences`.1\n","4. Bonus: implement a function that calculates the [automated readability index](https://en.wikipedia.org/wiki/Automated_readability_index) of a text.\n","\n","1 *You can and may combine 2. and 3. into a single function that returns two values.*\n","\n","\n","\n"]},{"cell_type":"markdown","metadata":{"id":"SSnrAH0xjUx8"},"source":["### Output\n","Implement a function that calls 1-3 (and optionally 4) for each of the novels, and outputs their results. Try to format it in a nice way, so that it is clear what the output is."]}],"metadata":{"colab":{"authorship_tag":"ABX9TyPdQ81CO+C9Rw0GWjNMy5CU","provenance":[],"toc_visible":true},"kernelspec":{"display_name":"Python 3","name":"python3"},"language_info":{"name":"python"}},"nbformat":4,"nbformat_minor":0} +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "kIc3b-0fqM6n" + }, + "source": [ + "# Project - Text Analysis\n", + "In this series of exercises you will develop a text analysis tool. The steps roughly simulate real-life Digital Humanities projects:\n", + "- Obtaining data\n", + "- Cleaning data\n", + "- Running analyses\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "OSpUjXmAqJI9" + }, + "source": [ + "> 💡 The exercises are very useful and fun if you would like to analyse textual data. The code you write here can be reused in your own analysis.\n", + "\n", + "> ⚠️ The exercises are difficult and may take you some time to complete. Consider them a bonus, study them at home, and don't be hestitant to contact the instructors, during or after the course. If you are not interested in text-analysis, ask the instructors for similarly challenging projects on a different topic, or pursue your own ideas!\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "v1MTwBNTpTcN" + }, + "source": [ + "## Exercise 1: Cleaning interpunction\n", + "Implement the function `clean_interpunction` in a way that satisfies the docstrings and assertions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "fOZBq4OLp2Lz" + }, + "outputs": [], + "source": [ + "def clean_interpunction(string):\n", + " \"\"\"Pads words and sentence-ending interpunction with a space.\n", + " Other interpunction should be removed.\n", + "\n", + " The following characters are considered sentence-ending interpunction:\n", + " - .\n", + " - ?\n", + " - !\n", + "\n", + " Other interpunction that should be removed:\n", + " - ,\n", + " - ;\n", + " - :\n", + " \"\"\"\n", + " output_string = None # replace this line by your own lines of code\n", + " return output_string\n", + "\n", + "simple_sent = 'A sentence.'\n", + "complex_sent = 'A sentence, with a dependent clause.'\n", + "assert clean_interpunction(simple_sent) == 'A sentence .'\n", + "assert clean_interpunction(complex_sent) == 'A sentence with a dependent clause .'\n", + "print('All cases passed! The function works.')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "U8QVnwgwqTR6" + }, + "source": [ + "## Exercise 2: only_periods\n", + "Add an optional parameter named `only_periods` to `clean_interpunction`. \n", + "When this parameter is `True`, all sentence-ending interpunction should be changed to a period. \n", + "The use of this will become clear in the next exercise." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "TdLKVbsvqhGb" + }, + "outputs": [], + "source": [ + "def clean_interpunction(string, only_periods=None):\n", + " \"\"\"Pads words and sentence-ending interpunction with a space.\n", + " Other interpunction should be removed.\n", + " If only_periods is True, replace all sentence ending interpunction by a period.\n", + "\n", + " The following characters are considered sentence-ending interpunction:\n", + " - .\n", + " - ?\n", + " - !\n", + "\n", + " Other interpunction that should be removed:\n", + " - ,\n", + " - ;\n", + " - :\n", + " \"\"\"\n", + " output_string = None # replace this line by your own lines of code\n", + " return output_string\n", + "\n", + "simple_sent = 'A sentence?'\n", + "complex_sent = 'A sentence, with a dependent clause!'\n", + "assert clean_interpunction(simple_sent) == 'A sentence ?'\n", + "assert clean_interpunction(complex_sent) == 'A sentence with a dependent clause !'\n", + "assert clean_interpunction(simple_sent, only_periods=True) == 'A sentence .'\n", + "assert clean_interpunction(complex_sent, only_periods=True) == 'A sentence with a dependent clause .'\n", + "print('All cases passed! The function works.')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "f5q3jw66q3EG" + }, + "source": [ + "## Exercise 3: Split into sentences\n", + "Using the results of the previous exercises, implement a function that satisfies the docstring and assertions:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "5lTZXUgwq6lW" + }, + "outputs": [], + "source": [ + "def split_sentences(text):\n", + " \"\"\"Takes a single string and splits it into sentences.\n", + " Sentences are lists containing words (as strings).\n", + " Sentences are split on periods.\n", + " Other interpunction is removed.\n", + "\n", + " Returns a nested list of sentences and words\n", + " \"\"\"\n", + " # your code here\n", + " # use your implementation of clean_interpunction\n", + " sentences = None\n", + "\n", + " # return the nested list\n", + " return sentences\n", + "\n", + "\n", + "\n", + "text = 'The novel details two days in the life of 16-year-old Holden Caulfield after he has been expelled from prep school. Confused and disillusioned, Holden searches for truth and rails against the “phoniness” of the adult world.'\n", + "expected = [['The', 'novel', 'details', 'two', 'days', 'in', 'the', 'life', 'of', '16-year-old', 'Holden', 'Caulfield', 'after', 'he', 'has', 'been', 'expelled', 'from', 'prep', 'school'], \n", + " ['Confused', 'and', 'disillusioned', 'Holden', 'searches', 'for', 'truth', 'and', 'rails', 'against', 'the', '“phoniness”', 'of', 'the', 'adult', 'world'], \n", + " ]\n", + "\n", + "# If no errors occur, you solved it correctly!\n", + "split_text = split_sentences(text)\n", + "assert len(split_text) == 2\n", + "assert split_text == expected" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Um-nFxcTq1WA" + }, + "source": [ + "## Exercise 4 - Putting it all together" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "YNbDTnBrheRq" + }, + "source": [ + "### The data\n", + "In the `class materials` section of the [Teams channel](https://teams.microsoft.com/l/channel/19%3aAlr7pcE2v9p1eaKT1BtNxJAB9mgCFT1olZvAMswJlEc1%40thread.tacv2/Algemeen?groupId=7fd15384-6748-4852-8646-d96963fb3524&tenantId=d72758a0-a446-4e0f-a0aa-4bf95a4a10e7) you will find three `.txt`files:\n", + "\n", + "\n", + "- `catcher_chapter1.txt`, containing the first chapter of The Catcher in the Rye by J.D. Salinger\n", + "- `matilda_chapter1.txt`, containing the first chapter of Matilda by Roald Dahl\n", + "- `ij_chapter1.txt`, containing a part of the first chapter (the chapters are way too long) of Inifite Jest by David Foster Wallace.\n", + "\n", + "Write some code that loads the whole content of each of these files into its corresponding variable, see code cell below.\n", + "\n", + "We will use these variables in the rest of the notebook. Make sure you've executed this cell at least once.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "q_58MJYQadJO" + }, + "outputs": [], + "source": [ + "catcher_chapter1 = ''\n", + "matilda_chapter1 = ''\n", + "ij_chapter1 = ''" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "EuaRmuhwhTIZ" + }, + "source": [ + "### Writing functions\n", + "Implement a few function to use in the final analysis:\n", + "\n", + "1. Implement a function that counts the total number of words given a string containing a full text.\n", + "2. Implement a function that calculates the average sentence length of a text. The sentence length is defined as the number of words in a sentence. Use your implementation of `split_sentences`.1\n", + "3. Implement a function that calculates the average word length of a text. Again, use your implementation of `split sentences`.1\n", + "4. Bonus: implement a function that calculates the [automated readability index](https://en.wikipedia.org/wiki/Automated_readability_index) of a text.\n", + "\n", + "1 *You can and may combine 2. and 3. into a single function that returns two values.*\n", + "\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SSnrAH0xjUx8" + }, + "source": [ + "### Output\n", + "Implement a function that calls 1-3 (and optionally 4) for each of the novels, and outputs their results. Try to format it in a nice way, so that it is clear what the output is." + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyPdQ81CO+C9Rw0GWjNMy5CU", + "provenance": [], + "toc_visible": true + }, + "jupytext": { + "main_language": "python" + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/lessons/Project - text analysis.py b/lessons/Project - text analysis.py new file mode 100644 index 0000000..0b99542 --- /dev/null +++ b/lessons/Project - text analysis.py @@ -0,0 +1,163 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# name: python3 +# --- + +# %% [markdown] id="kIc3b-0fqM6n" +# # Project - Text Analysis +# In this series of exercises you will develop a text analysis tool. The steps roughly simulate real-life Digital Humanities projects: +# - Obtaining data +# - Cleaning data +# - Running analyses +# +# + +# %% [markdown] id="OSpUjXmAqJI9" +# > 💡 The exercises are very useful and fun if you would like to analyse textual data. The code you write here can be reused in your own analysis. +# +# > ⚠️ The exercises are difficult and may take you some time to complete. Consider them a bonus, study them at home, and don't be hestitant to contact the instructors, during or after the course. If you are not interested in text-analysis, ask the instructors for similarly challenging projects on a different topic, or pursue your own ideas! +# + +# %% [markdown] id="v1MTwBNTpTcN" +# ## Exercise 1: Cleaning interpunction +# Implement the function `clean_interpunction` in a way that satisfies the docstrings and assertions + +# %% id="fOZBq4OLp2Lz" +def clean_interpunction(string): + """Pads words and sentence-ending interpunction with a space. + Other interpunction should be removed. + + The following characters are considered sentence-ending interpunction: + - . + - ? + - ! + + Other interpunction that should be removed: + - , + - ; + - : + """ + output_string = None # replace this line by your own lines of code + return output_string + +simple_sent = 'A sentence.' +complex_sent = 'A sentence, with a dependent clause.' +assert clean_interpunction(simple_sent) == 'A sentence .' +assert clean_interpunction(complex_sent) == 'A sentence with a dependent clause .' +print('All cases passed! The function works.') + + +# %% [markdown] id="U8QVnwgwqTR6" +# ## Exercise 2: only_periods +# Add an optional parameter named `only_periods` to `clean_interpunction`. +# When this parameter is `True`, all sentence-ending interpunction should be changed to a period. +# The use of this will become clear in the next exercise. + +# %% id="TdLKVbsvqhGb" +def clean_interpunction(string, only_periods=None): + """Pads words and sentence-ending interpunction with a space. + Other interpunction should be removed. + If only_periods is True, replace all sentence ending interpunction by a period. + + The following characters are considered sentence-ending interpunction: + - . + - ? + - ! + + Other interpunction that should be removed: + - , + - ; + - : + """ + output_string = None # replace this line by your own lines of code + return output_string + +simple_sent = 'A sentence?' +complex_sent = 'A sentence, with a dependent clause!' +assert clean_interpunction(simple_sent) == 'A sentence ?' +assert clean_interpunction(complex_sent) == 'A sentence with a dependent clause !' +assert clean_interpunction(simple_sent, only_periods=True) == 'A sentence .' +assert clean_interpunction(complex_sent, only_periods=True) == 'A sentence with a dependent clause .' +print('All cases passed! The function works.') + + +# %% [markdown] id="f5q3jw66q3EG" +# ## Exercise 3: Split into sentences +# Using the results of the previous exercises, implement a function that satisfies the docstring and assertions: + +# %% id="5lTZXUgwq6lW" +def split_sentences(text): + """Takes a single string and splits it into sentences. + Sentences are lists containing words (as strings). + Sentences are split on periods. + Other interpunction is removed. + + Returns a nested list of sentences and words + """ + # your code here + # use your implementation of clean_interpunction + sentences = None + + # return the nested list + return sentences + + + +text = 'The novel details two days in the life of 16-year-old Holden Caulfield after he has been expelled from prep school. Confused and disillusioned, Holden searches for truth and rails against the “phoniness” of the adult world.' +expected = [['The', 'novel', 'details', 'two', 'days', 'in', 'the', 'life', 'of', '16-year-old', 'Holden', 'Caulfield', 'after', 'he', 'has', 'been', 'expelled', 'from', 'prep', 'school'], + ['Confused', 'and', 'disillusioned', 'Holden', 'searches', 'for', 'truth', 'and', 'rails', 'against', 'the', '“phoniness”', 'of', 'the', 'adult', 'world'], + ] + +# If no errors occur, you solved it correctly! +split_text = split_sentences(text) +assert len(split_text) == 2 +assert split_text == expected + +# %% [markdown] id="Um-nFxcTq1WA" +# ## Exercise 4 - Putting it all together + +# %% [markdown] id="YNbDTnBrheRq" +# ### The data +# In the `class materials` section of the [Teams channel](https://teams.microsoft.com/l/channel/19%3aAlr7pcE2v9p1eaKT1BtNxJAB9mgCFT1olZvAMswJlEc1%40thread.tacv2/Algemeen?groupId=7fd15384-6748-4852-8646-d96963fb3524&tenantId=d72758a0-a446-4e0f-a0aa-4bf95a4a10e7) you will find three `.txt`files: +# +# +# - `catcher_chapter1.txt`, containing the first chapter of The Catcher in the Rye by J.D. Salinger +# - `matilda_chapter1.txt`, containing the first chapter of Matilda by Roald Dahl +# - `ij_chapter1.txt`, containing a part of the first chapter (the chapters are way too long) of Inifite Jest by David Foster Wallace. +# +# Write some code that loads the whole content of each of these files into its corresponding variable, see code cell below. +# +# We will use these variables in the rest of the notebook. Make sure you've executed this cell at least once. +# + +# %% id="q_58MJYQadJO" +catcher_chapter1 = '' +matilda_chapter1 = '' +ij_chapter1 = '' + +# %% [markdown] id="EuaRmuhwhTIZ" +# ### Writing functions +# Implement a few function to use in the final analysis: +# +# 1. Implement a function that counts the total number of words given a string containing a full text. +# 2. Implement a function that calculates the average sentence length of a text. The sentence length is defined as the number of words in a sentence. Use your implementation of `split_sentences`.1 +# 3. Implement a function that calculates the average word length of a text. Again, use your implementation of `split sentences`.1 +# 4. Bonus: implement a function that calculates the [automated readability index](https://en.wikipedia.org/wiki/Automated_readability_index) of a text. +# +# 1 *You can and may combine 2. and 3. into a single function that returns two values.* +# +# +# +# + +# %% [markdown] id="SSnrAH0xjUx8" +# ### Output +# Implement a function that calls 1-3 (and optionally 4) for each of the novels, and outputs their results. Try to format it in a nice way, so that it is clear what the output is. diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..2691030 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,2 @@ +[tool.jupytext] +formats = "ipynb,py:percent" diff --git a/solutions/01 introduction solutions.ipynb b/solutions/01 introduction solutions.ipynb index e81b287..08bee73 100644 --- a/solutions/01 introduction solutions.ipynb +++ b/solutions/01 introduction solutions.ipynb @@ -1 +1,636 @@ -{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"provenance":[],"toc_visible":true,"authorship_tag":"ABX9TyOdt4EUgMll2VGl6yXOOeib"},"kernelspec":{"name":"python3","display_name":"Python 3"},"language_info":{"name":"python"}},"cells":[{"cell_type":"markdown","source":["# Module 1: Introduction\n","\n","## Exercise solutions\n","\n","[Module 1](https://colab.research.google.com/drive/1i4wJpUIr77Fh1renWNjt00Bd51NbC1nB)\n","\n","## CDH course \"Programming in Python\"\n","\n","[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)"],"metadata":{"id":"Buvh9v-iYeOO"}},{"cell_type":"markdown","source":["## Exercise 1.1: Try it out"],"metadata":{"id":"pKIEfocbMaIR"}},{"cell_type":"code","source":["print(1)\n","1"],"metadata":{"id":"6hlNxRNNM1fV","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680706108778,"user_tz":-120,"elapsed":213,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"1e442ffe-2c24-468e-8f23-4dde9dae23ad"},"execution_count":4,"outputs":[{"output_type":"stream","name":"stdout","text":["1\n"]},{"output_type":"execute_result","data":{"text/plain":["1"]},"metadata":{},"execution_count":4}]},{"cell_type":"code","source":["print('oops')\n","'oops'"],"metadata":{"id":"VIm6FdRIM6OE","colab":{"base_uri":"https://localhost:8080/","height":52},"executionInfo":{"status":"ok","timestamp":1680705583794,"user_tz":-120,"elapsed":7,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"14982304-6483-4d06-f313-134443868ad9"},"execution_count":1,"outputs":[{"output_type":"stream","name":"stdout","text":["oops\n"]},{"output_type":"execute_result","data":{"text/plain":["'oops'"],"application/vnd.google.colaboratory.intrinsic+json":{"type":"string"}},"metadata":{},"execution_count":1}]},{"cell_type":"markdown","source":["The slight surprise in the above output is that `'oops'` is first printed without quotes, and then with quotes. `print` is meant to present data in a human-friendly way, so it strips quotes from text values (which we call strings). Bare values, on the other hand, are shown in the way in which you would type them into Python."],"metadata":{"id":"Yz1P17hS3Dnj"}},{"cell_type":"code","source":["print()"],"metadata":{"id":"_MN48xz5NAya","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680705926915,"user_tz":-120,"elapsed":226,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"58a646e7-26cf-4eed-97fd-37bd06d300f1"},"execution_count":2,"outputs":[{"output_type":"stream","name":"stdout","text":["\n"]}]},{"cell_type":"markdown","source":["You are allowed to call `print` without any value. This prints a newline."],"metadata":{"id":"yP3jC8AZ4Kyk"}},{"cell_type":"code","source":["print('apricot')\n","print('banana')\n","print('cherry')"],"metadata":{"id":"g9WKeIA2NVIA","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680706112032,"user_tz":-120,"elapsed":222,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"e9ca8138-4998-47b1-e2c9-3332e4e968e5"},"execution_count":5,"outputs":[{"output_type":"stream","name":"stdout","text":["apricot\n","banana\n","cherry\n"]}]},{"cell_type":"code","source":["'apricot'\n","'banana'\n","'cherry'"],"metadata":{"id":"NCbCfbHaNafJ","colab":{"base_uri":"https://localhost:8080/","height":38},"executionInfo":{"status":"ok","timestamp":1680597375941,"user_tz":-120,"elapsed":237,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"db68067b-3cba-4534-ea4d-f22b424fd253"},"execution_count":null,"outputs":[{"output_type":"execute_result","data":{"text/plain":["'cherry'"],"application/vnd.google.colaboratory.intrinsic+json":{"type":"string"}},"metadata":{},"execution_count":7}]},{"cell_type":"markdown","source":["Normally, a bare value by itself is never shown. It is a special \"service\" of notebooks that the last value in a code block is shown. This is why we see only `'cherry'` and not `'apricot'` or `'banana'`."],"metadata":{"id":"snD4bhFA4VTx"}},{"cell_type":"code","source":["# apricot\n","# banana\n","# cherry"],"metadata":{"id":"-ZuiJ92yNqpi","executionInfo":{"status":"ok","timestamp":1680706039219,"user_tz":-120,"elapsed":223,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}}},"execution_count":3,"outputs":[]},{"cell_type":"markdown","source":["Python ignores comments. From the perspective of Python, this code block is empty, so there is no output."],"metadata":{"id":"qYUevKdW4uEc"}},{"cell_type":"code","source":["print('apricot')\n","'banana'\n","# cherry"],"metadata":{"id":"b9bTbrNSNwwn","colab":{"base_uri":"https://localhost:8080/","height":52},"executionInfo":{"status":"ok","timestamp":1680706115343,"user_tz":-120,"elapsed":185,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"7229da45-10dc-4df7-9f14-d1c91b2f639f"},"execution_count":6,"outputs":[{"output_type":"stream","name":"stdout","text":["apricot\n"]},{"output_type":"execute_result","data":{"text/plain":["'banana'"],"application/vnd.google.colaboratory.intrinsic+json":{"type":"string"}},"metadata":{},"execution_count":6}]},{"cell_type":"code","source":["# apricot\n","'banana'\n","print('cherry')"],"metadata":{"id":"GYmcSm6iOAiA","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680597832375,"user_tz":-120,"elapsed":15,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"13ce5d1a-2a9f-44e2-cb38-4736ed7db735"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["cherry\n"]}]},{"cell_type":"markdown","source":["`'banana'` is not the last value in the code block, so it is not shown. `print('cherry')` has no value of itself (although it causes output); this is why we see only `cherry` and not this:\n","\n","```\n","cherry\n","'cherry'\n","```"],"metadata":{"id":"Nk_csoK1456t"}},{"cell_type":"code","source":["print('apricot')\n","'banana'\n","pass"],"metadata":{"id":"rbjHe9KbOzFb","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680598076019,"user_tz":-120,"elapsed":292,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"9491cf85-fc36-4d10-dba3-b32ea38d8cdf"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["apricot\n"]}]},{"cell_type":"markdown","source":["`'banana'` is not shown here, because it is not the last value in the code block. While `pass` does not do anything, Python still considers it code. It is not ignored like a comment."],"metadata":{"id":"S9Jrnq4E5qPX"}},{"cell_type":"code","source":["print(pass)"],"metadata":{"id":"V1GiIP_ZNK8H","colab":{"base_uri":"https://localhost:8080/","height":135},"executionInfo":{"status":"error","timestamp":1680598126605,"user_tz":-120,"elapsed":370,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"c5c40560-3aa0-4fe1-d4fe-7a18d48ee139"},"execution_count":null,"outputs":[{"output_type":"error","ename":"SyntaxError","evalue":"ignored","traceback":["\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m print(pass)\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n"]}]},{"cell_type":"markdown","source":["Our very first error! The error tells us that you cannot put a `pass` inside a `print`. The reason for that is that `pass` is not a value (but a statement)."],"metadata":{"id":"8Op9FLDB6HEZ"}},{"cell_type":"code","source":["print(#oops)"],"metadata":{"id":"sNNoSfndOSiw","colab":{"base_uri":"https://localhost:8080/","height":135},"executionInfo":{"status":"error","timestamp":1680598147401,"user_tz":-120,"elapsed":286,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"11ca5bd8-3235-4980-9b67-0a191d6515b3"},"execution_count":null,"outputs":[{"output_type":"error","ename":"SyntaxError","evalue":"ignored","traceback":["\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m print(#oops)\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m unexpected EOF while parsing\n"]}]},{"cell_type":"markdown","source":["EOF stands for \"end of file\", with \"file\" here referring to the code block. Python reads `print(` and then ignores the rest of the line because of the comment. Every `(` should have a matching `)`, so Python is surprised that the code ends without that matching `)` (which it ignored because it is inside the comment)."],"metadata":{"id":"4WV_vzIu6ZXo"}},{"cell_type":"code","source":["print('apricot', 'banana')"],"metadata":{"id":"6IZS1NUuTdOX","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680598226349,"user_tz":-120,"elapsed":312,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"04fd0ec0-61eb-4604-f9cd-0b66fb0d82bd"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["apricot banana\n"]}]},{"cell_type":"markdown","source":["You are allowed to pass more than one value to `print`. By default, it puts spaces between them. You can change the space into something else as follows:\n","\n","```py\n","# Use a comma instead of a space\n","print('apricot', 'banana', sep=',')\n","\n","# Glue values together directly (no separator)\n","print('apricot', 'banana', sep='')\n","```\n","\n","Use can use any separator you like, even very long strings."],"metadata":{"id":"G_aPWTsL64wW"}},{"cell_type":"markdown","source":["## Exercise 1.2: Hello, world!"],"metadata":{"id":"Tr3wPlHMeBCI"}},{"cell_type":"code","source":["# The following would work!\n","# (but only in a notebook)\n","'Hello, world!'"],"metadata":{"id":"jN7WTVOOSq2C"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["# This is slightly better.\n","print('Hello, world!')"],"metadata":{"id":"qNjQXYFc7wfF"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["## Next module\n","\n","[2. Values and expressions](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp) - [solutions](https://colab.research.google.com/drive/1JKeNehBZ9hhHXPdVunQ_r9cv-lLR9Byy)"],"metadata":{"id":"zI0ohEpPUwpC"}}]} \ No newline at end of file +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "Buvh9v-iYeOO" + }, + "source": [ + "# Module 1: Introduction\n", + "\n", + "## Exercise solutions\n", + "\n", + "[Module 1](https://colab.research.google.com/drive/1i4wJpUIr77Fh1renWNjt00Bd51NbC1nB)\n", + "\n", + "## CDH course \"Programming in Python\"\n", + "\n", + "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pKIEfocbMaIR" + }, + "source": [ + "## Exercise 1.1: Try it out" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 213, + "status": "ok", + "timestamp": 1680706108778, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "6hlNxRNNM1fV", + "outputId": "1e442ffe-2c24-468e-8f23-4dde9dae23ad" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n" + ] + }, + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print(1)\n", + "1" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 52 + }, + "executionInfo": { + "elapsed": 7, + "status": "ok", + "timestamp": 1680705583794, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "VIm6FdRIM6OE", + "outputId": "14982304-6483-4d06-f313-134443868ad9" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "oops\n" + ] + }, + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'oops'" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print('oops')\n", + "'oops'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Yz1P17hS3Dnj" + }, + "source": [ + "The slight surprise in the above output is that `'oops'` is first printed without quotes, and then with quotes. `print` is meant to present data in a human-friendly way, so it strips quotes from text values (which we call strings). Bare values, on the other hand, are shown in the way in which you would type them into Python." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 226, + "status": "ok", + "timestamp": 1680705926915, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "_MN48xz5NAya", + "outputId": "58a646e7-26cf-4eed-97fd-37bd06d300f1" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "print()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "yP3jC8AZ4Kyk" + }, + "source": [ + "You are allowed to call `print` without any value. This prints a newline." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 222, + "status": "ok", + "timestamp": 1680706112032, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "g9WKeIA2NVIA", + "outputId": "e9ca8138-4998-47b1-e2c9-3332e4e968e5" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "apricot\n", + "banana\n", + "cherry\n" + ] + } + ], + "source": [ + "print('apricot')\n", + "print('banana')\n", + "print('cherry')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 38 + }, + "executionInfo": { + "elapsed": 237, + "status": "ok", + "timestamp": 1680597375941, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "NCbCfbHaNafJ", + "outputId": "db68067b-3cba-4534-ea4d-f22b424fd253" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'cherry'" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "'apricot'\n", + "'banana'\n", + "'cherry'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "snD4bhFA4VTx" + }, + "source": [ + "Normally, a bare value by itself is never shown. It is a special \"service\" of notebooks that the last value in a code block is shown. This is why we see only `'cherry'` and not `'apricot'` or `'banana'`." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "executionInfo": { + "elapsed": 223, + "status": "ok", + "timestamp": 1680706039219, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "-ZuiJ92yNqpi" + }, + "outputs": [], + "source": [ + "# apricot\n", + "# banana\n", + "# cherry" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qYUevKdW4uEc" + }, + "source": [ + "Python ignores comments. From the perspective of Python, this code block is empty, so there is no output." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 52 + }, + "executionInfo": { + "elapsed": 185, + "status": "ok", + "timestamp": 1680706115343, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "b9bTbrNSNwwn", + "outputId": "7229da45-10dc-4df7-9f14-d1c91b2f639f" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "apricot\n" + ] + }, + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'banana'" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print('apricot')\n", + "'banana'\n", + "# cherry" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 15, + "status": "ok", + "timestamp": 1680597832375, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "GYmcSm6iOAiA", + "outputId": "13ce5d1a-2a9f-44e2-cb38-4736ed7db735" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "cherry\n" + ] + } + ], + "source": [ + "# apricot\n", + "'banana'\n", + "print('cherry')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Nk_csoK1456t" + }, + "source": [ + "`'banana'` is not the last value in the code block, so it is not shown. `print('cherry')` has no value of itself (although it causes output); this is why we see only `cherry` and not this:\n", + "\n", + "```\n", + "cherry\n", + "'cherry'\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 292, + "status": "ok", + "timestamp": 1680598076019, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "rbjHe9KbOzFb", + "outputId": "9491cf85-fc36-4d10-dba3-b32ea38d8cdf" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "apricot\n" + ] + } + ], + "source": [ + "print('apricot')\n", + "'banana'\n", + "pass" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "S9Jrnq4E5qPX" + }, + "source": [ + "`'banana'` is not shown here, because it is not the last value in the code block. While `pass` does not do anything, Python still considers it code. It is not ignored like a comment." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 135 + }, + "executionInfo": { + "elapsed": 370, + "status": "error", + "timestamp": 1680598126605, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "V1GiIP_ZNK8H", + "outputId": "c5c40560-3aa0-4fe1-d4fe-7a18d48ee139" + }, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "ignored", + "output_type": "error", + "traceback": [ + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m print(pass)\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + ] + } + ], + "source": [ + "print(pass)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8Op9FLDB6HEZ" + }, + "source": [ + "Our very first error! The error tells us that you cannot put a `pass` inside a `print`. The reason for that is that `pass` is not a value (but a statement)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 135 + }, + "executionInfo": { + "elapsed": 286, + "status": "error", + "timestamp": 1680598147401, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "sNNoSfndOSiw", + "outputId": "11ca5bd8-3235-4980-9b67-0a191d6515b3" + }, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "ignored", + "output_type": "error", + "traceback": [ + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m print(#oops)\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m unexpected EOF while parsing\n" + ] + } + ], + "source": [ + "print(#oops)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "4WV_vzIu6ZXo" + }, + "source": [ + "EOF stands for \"end of file\", with \"file\" here referring to the code block. Python reads `print(` and then ignores the rest of the line because of the comment. Every `(` should have a matching `)`, so Python is surprised that the code ends without that matching `)` (which it ignored because it is inside the comment)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 312, + "status": "ok", + "timestamp": 1680598226349, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "6IZS1NUuTdOX", + "outputId": "04fd0ec0-61eb-4604-f9cd-0b66fb0d82bd" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "apricot banana\n" + ] + } + ], + "source": [ + "print('apricot', 'banana')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "G_aPWTsL64wW" + }, + "source": [ + "You are allowed to pass more than one value to `print`. By default, it puts spaces between them. You can change the space into something else as follows:\n", + "\n", + "```py\n", + "# Use a comma instead of a space\n", + "print('apricot', 'banana', sep=',')\n", + "\n", + "# Glue values together directly (no separator)\n", + "print('apricot', 'banana', sep='')\n", + "```\n", + "\n", + "Use can use any separator you like, even very long strings." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Tr3wPlHMeBCI" + }, + "source": [ + "## Exercise 1.2: Hello, world!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "jN7WTVOOSq2C" + }, + "outputs": [], + "source": [ + "# The following would work!\n", + "# (but only in a notebook)\n", + "'Hello, world!'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "qNjQXYFc7wfF" + }, + "outputs": [], + "source": [ + "# This is slightly better.\n", + "print('Hello, world!')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zI0ohEpPUwpC" + }, + "source": [ + "## Next module\n", + "\n", + "[2. Values and expressions](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp) - [solutions](https://colab.research.google.com/drive/1JKeNehBZ9hhHXPdVunQ_r9cv-lLR9Byy)" + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyOdt4EUgMll2VGl6yXOOeib", + "provenance": [], + "toc_visible": true + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/solutions/01 introduction solutions.py b/solutions/01 introduction solutions.py new file mode 100644 index 0000000..d806144 --- /dev/null +++ b/solutions/01 introduction solutions.py @@ -0,0 +1,135 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# name: python3 +# --- + +# %% [markdown] id="Buvh9v-iYeOO" +# # Module 1: Introduction +# +# ## Exercise solutions +# +# [Module 1](https://colab.research.google.com/drive/1i4wJpUIr77Fh1renWNjt00Bd51NbC1nB) +# +# ## CDH course "Programming in Python" +# +# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) + +# %% [markdown] id="pKIEfocbMaIR" +# ## Exercise 1.1: Try it out + +# %% id="6hlNxRNNM1fV" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680706108778, "user_tz": -120, "elapsed": 213, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="1e442ffe-2c24-468e-8f23-4dde9dae23ad" +print(1) +1 + +# %% id="VIm6FdRIM6OE" colab={"base_uri": "https://localhost:8080/", "height": 52} executionInfo={"status": "ok", "timestamp": 1680705583794, "user_tz": -120, "elapsed": 7, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="14982304-6483-4d06-f313-134443868ad9" +print('oops') +'oops' + +# %% [markdown] id="Yz1P17hS3Dnj" +# The slight surprise in the above output is that `'oops'` is first printed without quotes, and then with quotes. `print` is meant to present data in a human-friendly way, so it strips quotes from text values (which we call strings). Bare values, on the other hand, are shown in the way in which you would type them into Python. + +# %% id="_MN48xz5NAya" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680705926915, "user_tz": -120, "elapsed": 226, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="58a646e7-26cf-4eed-97fd-37bd06d300f1" +print() + +# %% [markdown] id="yP3jC8AZ4Kyk" +# You are allowed to call `print` without any value. This prints a newline. + +# %% id="g9WKeIA2NVIA" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680706112032, "user_tz": -120, "elapsed": 222, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="e9ca8138-4998-47b1-e2c9-3332e4e968e5" +print('apricot') +print('banana') +print('cherry') + +# %% id="NCbCfbHaNafJ" colab={"base_uri": "https://localhost:8080/", "height": 38} executionInfo={"status": "ok", "timestamp": 1680597375941, "user_tz": -120, "elapsed": 237, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="db68067b-3cba-4534-ea4d-f22b424fd253" +'apricot' +'banana' +'cherry' + +# %% [markdown] id="snD4bhFA4VTx" +# Normally, a bare value by itself is never shown. It is a special "service" of notebooks that the last value in a code block is shown. This is why we see only `'cherry'` and not `'apricot'` or `'banana'`. + +# %% id="-ZuiJ92yNqpi" executionInfo={"status": "ok", "timestamp": 1680706039219, "user_tz": -120, "elapsed": 223, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} +# apricot +# banana +# cherry + +# %% [markdown] id="qYUevKdW4uEc" +# Python ignores comments. From the perspective of Python, this code block is empty, so there is no output. + +# %% id="b9bTbrNSNwwn" colab={"base_uri": "https://localhost:8080/", "height": 52} executionInfo={"status": "ok", "timestamp": 1680706115343, "user_tz": -120, "elapsed": 185, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="7229da45-10dc-4df7-9f14-d1c91b2f639f" +print('apricot') +'banana' +# cherry + +# %% id="GYmcSm6iOAiA" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680597832375, "user_tz": -120, "elapsed": 15, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="13ce5d1a-2a9f-44e2-cb38-4736ed7db735" +# apricot +'banana' +print('cherry') + +# %% [markdown] id="Nk_csoK1456t" +# `'banana'` is not the last value in the code block, so it is not shown. `print('cherry')` has no value of itself (although it causes output); this is why we see only `cherry` and not this: +# +# ``` +# cherry +# 'cherry' +# ``` + +# %% id="rbjHe9KbOzFb" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680598076019, "user_tz": -120, "elapsed": 292, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="9491cf85-fc36-4d10-dba3-b32ea38d8cdf" +print('apricot') +'banana' +pass + +# %% [markdown] id="S9Jrnq4E5qPX" +# `'banana'` is not shown here, because it is not the last value in the code block. While `pass` does not do anything, Python still considers it code. It is not ignored like a comment. + +# %% id="V1GiIP_ZNK8H" colab={"base_uri": "https://localhost:8080/", "height": 135} executionInfo={"status": "error", "timestamp": 1680598126605, "user_tz": -120, "elapsed": 370, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="c5c40560-3aa0-4fe1-d4fe-7a18d48ee139" +print(pass) + +# %% [markdown] id="8Op9FLDB6HEZ" +# Our very first error! The error tells us that you cannot put a `pass` inside a `print`. The reason for that is that `pass` is not a value (but a statement). + +# %% id="sNNoSfndOSiw" colab={"base_uri": "https://localhost:8080/", "height": 135} executionInfo={"status": "error", "timestamp": 1680598147401, "user_tz": -120, "elapsed": 286, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="11ca5bd8-3235-4980-9b67-0a191d6515b3" +print(#oops) + +# %% [markdown] id="4WV_vzIu6ZXo" +# EOF stands for "end of file", with "file" here referring to the code block. Python reads `print(` and then ignores the rest of the line because of the comment. Every `(` should have a matching `)`, so Python is surprised that the code ends without that matching `)` (which it ignored because it is inside the comment). + +# %% id="6IZS1NUuTdOX" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680598226349, "user_tz": -120, "elapsed": 312, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="04fd0ec0-61eb-4604-f9cd-0b66fb0d82bd" +print('apricot', 'banana') + +# %% [markdown] id="G_aPWTsL64wW" +# You are allowed to pass more than one value to `print`. By default, it puts spaces between them. You can change the space into something else as follows: +# +# ```py +# # Use a comma instead of a space +# print('apricot', 'banana', sep=',') +# +# # Glue values together directly (no separator) +# print('apricot', 'banana', sep='') +# ``` +# +# Use can use any separator you like, even very long strings. + +# %% [markdown] id="Tr3wPlHMeBCI" +# ## Exercise 1.2: Hello, world! + +# %% id="jN7WTVOOSq2C" +# The following would work! +# (but only in a notebook) +'Hello, world!' + +# %% id="qNjQXYFc7wfF" +# This is slightly better. +print('Hello, world!') + +# %% [markdown] id="zI0ohEpPUwpC" +# ## Next module +# +# [2. Values and expressions](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp) - [solutions](https://colab.research.google.com/drive/1JKeNehBZ9hhHXPdVunQ_r9cv-lLR9Byy) diff --git a/solutions/02 values and expressions solutions.ipynb b/solutions/02 values and expressions solutions.ipynb index 138a960..624a872 100644 --- a/solutions/02 values and expressions solutions.ipynb +++ b/solutions/02 values and expressions solutions.ipynb @@ -1 +1,1389 @@ -{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"provenance":[],"toc_visible":true,"authorship_tag":"ABX9TyPhSPWbSkxQNLvGfLg7Ov8q"},"kernelspec":{"name":"python3","display_name":"Python 3"},"language_info":{"name":"python"}},"cells":[{"cell_type":"markdown","source":["# Module 2: Values and expressions\n","\n","### Exercise solutions\n","\n","[Module 2](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp)\n","\n","### CDH course \"Programming in Python\"\n","\n","[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n","\n","Previous module: [1. Introduction](https://colab.research.google.com/drive/1i4wJpUIr77Fh1renWNjt00Bd51NbC1nB) - [solutions](https://colab.research.google.com/drive/1RIwG7hA-Ymjcm1dmSW6wA_hu1clDNrAd)"],"metadata":{"id":"fqMJHzNk5yXQ"}},{"cell_type":"markdown","source":["## Exercise 2.1: Variables and state\n","\n","In each of the code blocks below, try to predict what will be printed, then run the code. If your guess was incorrect, try to figure out why the result is different. If your guess was correct, celebrate!"],"metadata":{"id":"pDU1uK2Igmki"}},{"cell_type":"code","source":["flavor = 'vanilla'\n","print(flavor)"],"metadata":{"id":"FydkKnx_hUPq","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680606359445,"user_tz":-120,"elapsed":420,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"0cce4013-6c8a-44a9-ecc8-6720dcfcb047"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["vanilla\n"]}]},{"cell_type":"code","source":["temperature = 70\n","print(flavor)"],"metadata":{"id":"LTWods50h-Ij","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680606363219,"user_tz":-120,"elapsed":402,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"41e65fc2-80ff-45ea-dfd1-8109e8d96b95"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["vanilla\n"]}]},{"cell_type":"markdown","source":["The variable `flavor` was retained from the previous code block. The variable `temperature` is not used here."],"metadata":{"id":"pmnKAg7H95BW"}},{"cell_type":"code","source":["print(temperature)\n","temperature = 35\n","print(temperature)"],"metadata":{"id":"l2vT4L7piGRf","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680606385469,"user_tz":-120,"elapsed":450,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"472eeb4a-fe6e-4696-9817-0a481a153d5a"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["70\n","35\n"]}]},{"cell_type":"markdown","source":["`temperature` is first printed as it was retained from the previous code block. Then, it is reassigned and printed again."],"metadata":{"id":"ElhEcRIB-EsA"}},{"cell_type":"code","source":["dog_name = 'Bobby'\n","cat_name = 'Garfield'\n","dog_name = cat_name\n","cat_name = dog_name\n","print(dog_name)\n","print(cat_name)"],"metadata":{"id":"k1Z_yWLXiWy7","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680606390493,"user_tz":-120,"elapsed":230,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"41a6d3ad-c50e-4bd4-f347-07eae4f8fae9"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["Garfield\n","Garfield\n"]}]},{"cell_type":"markdown","source":["On line 3, it is important to realize that `dog_name` gets *the value of* `cat_name`. There is no permanent tie between the variables. After line 3, both variables have the same value. Hence, `cat_name` cannot change into anything else on line 4. The program has effectively forgotten the value `'Bobby'`."],"metadata":{"id":"iFkF67zp-LJk"}},{"cell_type":"markdown","source":["Before running the following code, try to explain why it does *not* output `chocolate`."],"metadata":{"id":"BZ50KykuAYPs"}},{"cell_type":"code","source":["sweet = 'chocolate'\n","savory = 'cheese'\n","dessert = 'sweet'\n","print(dessert)"],"metadata":{"id":"X3xHg6K4Aicn","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680606431330,"user_tz":-120,"elapsed":202,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"5a5e974b-cb7a-466b-f900-b8b586080212"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["sweet\n"]}]},{"cell_type":"markdown","source":["On line 3, `'sweet'` is quoted, so Python considers it a string. It does not recognize it as a variable, even though we also defined a variable with that name on line 1."],"metadata":{"id":"1qb27-Rf-wIH"}},{"cell_type":"markdown","source":["In each of the following lines of code, replace `None` by a value of your choice. Make it such that `my_int` is an integer, `my_float` is a float, `my_bool` is a boolean and `my_string` is a string."],"metadata":{"id":"0az3tNK7WD3G"}},{"cell_type":"code","source":["my_int = 42\n","my_float = .5\n","my_bool = True\n","my_string = \"I'm starving\""],"metadata":{"id":"lY-M8mfSXDfG"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["## Exercise 2.2: Bonus"],"metadata":{"id":"dvNIQh7KYuJb"}},{"cell_type":"markdown","source":["How could you verify in code whether the variables you wrote above have the correct type? Write this code below.\n","\n","Hint: scroll back to the section [Difference between types](#scrollTo=Difference_between_types)."],"metadata":{"id":"GI9fyUO8XOcp"}},{"cell_type":"code","source":["print(type(my_int))\n","print(type(my_float))\n","print(type(my_bool))\n","print(type(my_string))"],"metadata":{"id":"Om6z53RXYBoS","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680606693012,"user_tz":-120,"elapsed":223,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"3f66fabe-bd2c-4046-d165-b6dc475ffcc2"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["\n","\n","\n","\n"]}]},{"cell_type":"markdown","source":["In the code block below, replace the question mark `?` by a variable name. Make it such that the final output is `'Hooray!'`."],"metadata":{"id":"h6UKIMXCj3w9"}},{"cell_type":"code","source":["cheer = 'Hooray!'\n","alarm = 'Oh no!'\n","anthem = 'Wilhelmus van Nassauwe'\n","alarm = anthem\n","alarm = cheer # alarm instead of ?\n","cheer = anthem\n","anthem = alarm\n","print(anthem)"],"metadata":{"id":"3MI6_DY7ku30","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680606893191,"user_tz":-120,"elapsed":247,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"6a605092-cca4-41c3-dfa3-0196c98be37b"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["Hooray!\n"]}]},{"cell_type":"markdown","source":["## Exercise 2.3: Expressions\n","\n"],"metadata":{"id":"z_bXvnya5J2_"}},{"cell_type":"markdown","source":["1. Try to predict the value of each of the following code blocks. Can you explain any surprises?"],"metadata":{"id":"j9xhznQlbCBf"}},{"cell_type":"code","source":["1 + 1"],"metadata":{"id":"0QcMB4xwbSfL","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680609962892,"user_tz":-120,"elapsed":415,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"609a5c45-cd77-4aaa-c33e-928263e14c41"},"execution_count":null,"outputs":[{"output_type":"execute_result","data":{"text/plain":["2"]},"metadata":{},"execution_count":92}]},{"cell_type":"code","source":["1 * 1"],"metadata":{"id":"eHKN9kP9bWkm","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680609965892,"user_tz":-120,"elapsed":10,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"9b17e8cc-3a5a-4ac4-db2f-1454eae873af"},"execution_count":null,"outputs":[{"output_type":"execute_result","data":{"text/plain":["1"]},"metadata":{},"execution_count":93}]},{"cell_type":"code","source":["a = 1\n","b = 2\n","a + b"],"metadata":{"id":"uINoRNNbbXwJ","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680609969742,"user_tz":-120,"elapsed":323,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"1b281881-5bbc-476e-e38c-bda84855665d"},"execution_count":null,"outputs":[{"output_type":"execute_result","data":{"text/plain":["3"]},"metadata":{},"execution_count":94}]},{"cell_type":"code","source":["c = b\n","a * b * c"],"metadata":{"id":"4xyWkYlnbc_8","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680609971809,"user_tz":-120,"elapsed":15,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"9b2dae89-2331-47de-83a5-4ac63e71a06d"},"execution_count":null,"outputs":[{"output_type":"execute_result","data":{"text/plain":["4"]},"metadata":{},"execution_count":95}]},{"cell_type":"code","source":["'hakuna' + 'matata'"],"metadata":{"id":"7tW2T4mebljv","colab":{"base_uri":"https://localhost:8080/","height":38},"executionInfo":{"status":"ok","timestamp":1680609976922,"user_tz":-120,"elapsed":408,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"404d4cac-d77d-40f8-c6bc-b6f4fcf0e125"},"execution_count":null,"outputs":[{"output_type":"execute_result","data":{"text/plain":["'hakunamatata'"],"application/vnd.google.colaboratory.intrinsic+json":{"type":"string"}},"metadata":{},"execution_count":96}]},{"cell_type":"markdown","source":["If we had done `print('hakuna', 'matata')`, it would have output `hakuna matata`, with a space between the words. When we concatenate strings using `+`, Python does not take that liberty, so if we actually want to separate words with spaces, we have to add them ourselves."],"metadata":{"id":"wG4N61Jz_WJV"}},{"cell_type":"code","source":["liquid = 'water~'\n","liquid * 3"],"metadata":{"id":"uEdPHIhBb2Mw","colab":{"base_uri":"https://localhost:8080/","height":38},"executionInfo":{"status":"ok","timestamp":1680609981027,"user_tz":-120,"elapsed":332,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"fc706353-8980-4baa-fc4b-cca736f757e9"},"execution_count":null,"outputs":[{"output_type":"execute_result","data":{"text/plain":["'water~water~water~'"],"application/vnd.google.colaboratory.intrinsic+json":{"type":"string"}},"metadata":{},"execution_count":97}]},{"cell_type":"code","source":["5 - 2 - 1"],"metadata":{"id":"-D7BB50Qceo2","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680609983171,"user_tz":-120,"elapsed":236,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"1169bc24-b2d3-41d5-b190-7e1135e3d93a"},"execution_count":null,"outputs":[{"output_type":"execute_result","data":{"text/plain":["2"]},"metadata":{},"execution_count":98}]},{"cell_type":"code","source":["5 - (2 - 1)"],"metadata":{"id":"OQUGr5rGck3m","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680609985039,"user_tz":-120,"elapsed":237,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"95e07168-7633-4cc0-f961-d8beb34e5612"},"execution_count":null,"outputs":[{"output_type":"execute_result","data":{"text/plain":["4"]},"metadata":{},"execution_count":99}]},{"cell_type":"markdown","source":["The above result is different from the previous, because Python evaluates minus operators (and most other operators) left-to-right by default."],"metadata":{"id":"btuLZMUG_xfJ"}},{"cell_type":"code","source":["income = 100\n","tax = 20\n","net_income = income - tax\n","tax = 15\n","net_income"],"metadata":{"id":"Jc8fawaIdCD5","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680609988715,"user_tz":-120,"elapsed":212,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"7d524d26-14c3-439a-e78f-914552814109"},"execution_count":null,"outputs":[{"output_type":"execute_result","data":{"text/plain":["80"]},"metadata":{},"execution_count":100}]},{"cell_type":"markdown","source":["On line 3, we set `net_income` to the outcome of *the value of* `income` minus *the value of* `tax`, which are respectively `100` and `20` at that point. `net_income` then keeps its value (`80`) until we change it directly. Hence, line 4 does not change anything about `net_income`. If we wanted to update `net_income`, we would have to run line 3 again."],"metadata":{"id":"w08CSKAhADsd"}},{"cell_type":"markdown","source":["2. In the code block below, change the values of `character` and `multiplier` so that the output becomes `'Goooood!'`."],"metadata":{"id":"TlLkcRv-droI"}},{"cell_type":"code","source":["character = 'o'\n","multiplier = 5\n","\n","begin = 'G'\n","middle = character * multiplier\n","end = 'd!'\n","\n","begin + middle + end"],"metadata":{"id":"pYZUkeDJenob","colab":{"base_uri":"https://localhost:8080/","height":38},"executionInfo":{"status":"ok","timestamp":1680610076844,"user_tz":-120,"elapsed":406,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"97c1b7a5-adf1-4112-b0b4-9a7a826855de"},"execution_count":null,"outputs":[{"output_type":"execute_result","data":{"text/plain":["'Goooood!'"],"application/vnd.google.colaboratory.intrinsic+json":{"type":"string"}},"metadata":{},"execution_count":101}]},{"cell_type":"markdown","source":["3. Rewrite your Hello world-program:\n"," - Build the \"hello, world!\" string using multiple variables.\n"," - Make the program say \"hello, world!\" 3 times, each time on a new line"],"metadata":{"id":"G8X4n_a8a5tl"}},{"cell_type":"code","source":["# This works:\n","hello = 'Hello, '\n","world = 'world!\\n'\n","variable = hello + world\n","print(3 * variable)\n","\n","# This works, too:\n","slogan = '''Hello, world!\n","'''\n","result = 3 * slogan\n","print(result)\n","\n","# This would also work, but uses no variables:\n","print('Hello, world!\\n' * 3)"],"metadata":{"id":"nDUVvhDEfIVI","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680708229954,"user_tz":-120,"elapsed":8,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"8be0c0dd-dc1e-48cc-bfae-5b3c2520efed"},"execution_count":1,"outputs":[{"output_type":"stream","name":"stdout","text":["Hello, world!\n","Hello, world!\n","Hello, world!\n","\n","Hello, world!\n","Hello, world!\n","Hello, world!\n","\n","Hello, world!\n","Hello, world!\n","Hello, world!\n","\n"]}]},{"cell_type":"markdown","source":["## Exercise 2.4: Bonus"],"metadata":{"id":"3HCqdTj2fVPK"}},{"cell_type":"markdown","source":["1. Find out and describe what the following operators do:\n"," - `%` (e.g. `10 % 2`)\n"," - `//` (e.g. `10 // 2`)\n"," - `**` (e.g. `10 ** 2`)\n"," - Tip: write the expressions using variables. Change the variables and see how it affects the outcome."],"metadata":{"id":"EKFdkLkWa8EY"}},{"cell_type":"code","source":["# Regarding %, you will quickly see a pattern if you set\n","# the right operand to 3 and try a series of consecutive\n","# numbers as the left operand:\n","\n","10 % 3 == 1\n","11 % 3 == 2\n","12 % 3 == 0\n","13 % 3 == 1\n","14 % 3 == 2\n","15 % 3 == 0\n","16 % 3 == 1\n","17 % 3 == 2\n","18 % 3 == 0\n","\n","# Conclusion: % calculates the remainder of division.\n","\n","# // is like / (division), but rounded down to an integer.\n","10 / 3 == 3.33333333333333333\n","10 // 3 == 3\n","5 / 4 == 1.25\n","5 // 4 == 1\n","\n","# In general, the following equation is always true if\n","# a and b are integers:\n","(a // b) * b + a % b == a\n","\n","# a ** b raises a to the power of b.\n","3 ** 2 == 3 * 3 == 9\n","3 ** 3 == 3 * 3 * 3 == 27\n","3 ** 4 == 3 * 3 * 3 * 3 == 81"],"metadata":{"id":"V59a7vpDfzO2"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["2. Predict what will be printed on each line in the following code block, then run the code to check your answer. If you made any mistakes, try to figure out what is causing the difference."],"metadata":{"id":"o0WYIAUla-AX"}},{"cell_type":"code","source":["word = 'stylometry'\n","repeats = 3\n","extra = 2\n","\n","print((word * repeats) + str(extra))\n","# Evaluated in this order:\n","word == 'stylometry'\n","repeats == 3\n","'stylometry' * 3 == 'stylometrystylometrystylometry'\n","str(extra) == '2'\n","'stylometrystylometrystylometry' + '2' == 'stylometrystylometrystylometry2'\n","\n","print(word * (repeats + extra))\n","# Evaluated in this order:\n","word == 'stylometry'\n","repeats + extra == 5\n","'stylometry' * 5 == 'stylometrystylometrystylometrystylometrystylometry'\n","\n","print(word * repeats + str(extra))\n","# Evaluated in the same way as line 5, just without parentheses.\n","# The * operator takes precedence over the + operator by default.\n","\n","print((word + str(extra)) * repeats)\n","# Evaluated in this order:\n","word == 'stylometry'\n","str(extra) == '2'\n","word + str(extra) == 'stylometry2'\n","repeats == 3\n","'stylometry2' * 3 == 'stylometry2stylometry2stylometry2'\n","\n","print(word + str(extra * repeats))\n","# Evaluated in this order:\n","word == 'stylometry'\n","extra == 2\n","repeats == 3\n","2 * 3 == 6\n","str(6) == '6'\n","'stylometry' + '6' == 'stylometry6'\n","\n","print(word + str(extra) * repeats)\n","# Evaluated in this order:\n","word == 'stylometry'\n","extra == 2\n","str(2) == '2'\n","repeats == 3\n","# * operator takes precedence over +, so right subexpression first.\n","'2' * 3 == '222'\n","'stylometry' + '222' == 'stylometry222'"],"metadata":{"id":"Z9Vd9pdIUzT5","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680709009778,"user_tz":-120,"elapsed":202,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"1cde1422-5f7f-460f-c537-5393acf5bb2a"},"execution_count":2,"outputs":[{"output_type":"stream","name":"stdout","text":["stylometrystylometrystylometry2\n","stylometrystylometrystylometrystylometrystylometry\n","stylometrystylometrystylometry2\n","stylometry2stylometry2stylometry2\n","stylometry6\n","stylometry222\n"]}]},{"cell_type":"markdown","source":["3. Using just the variables `word`, `repeats` and `extra` from the previous code block, write a single big expression that evaluates to `'stylometry555stylometry'`."],"metadata":{"id":"DRObBQZHgsIG"}},{"cell_type":"code","source":["word + str(extra + repeats) * repeats + word\n","# (you can add parentheses, as long as you evaluate the * before the +)"],"metadata":{"id":"uLuiUk10gqwM"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["## Exercise 2.5: boolean expressions"],"metadata":{"id":"pvrcgKU18OrJ"}},{"cell_type":"markdown","source":["1. Predict for each boolean expression below: `True` or `False`? If any of your predictions is incorrect, try to find the reason."],"metadata":{"id":"sWdnjezoil9j"}},{"cell_type":"code","source":["name = 'Jelte'\n","height = 2\n","\n","name * height == 'Jelte Jelte'\n","# False because string repetition does not add spaces\n","\n","height < 2\n","# a number can never be less than itself\n","\n","2 <= height\n","# ... but a number is always less than or equal to itself\n","\n","1 < height <= 2\n","# Python allows you to compare the same expression on two sides\n","\n","2 <= height < 1\n","# Both comparison must be true for the whole expression to be True\n","\n","name <= 'Julian'\n","# Lexicographical comparison: 'Julian' is greater because\n","# 'u' comes after 'e'.\n","\n","height * 3 + 1 >= height\n","# * and + take precedence over >=, so the evaluation goes in this order:\n","height * 3 == 6\n","6 + 1 == 7\n","height == 2\n","(7 >= 2) == True"],"metadata":{"id":"RpSvrom3Z8fQ","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680614316115,"user_tz":-120,"elapsed":394,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"d3c6bb57-e8c1-4891-c01d-850fd504e1f5"},"execution_count":null,"outputs":[{"output_type":"execute_result","data":{"text/plain":["True"]},"metadata":{},"execution_count":148}]},{"cell_type":"markdown","source":["2. Run the code block below. Did you expect this result? Can you explain it?"],"metadata":{"id":"zJiaGIlZBN_P"}},{"cell_type":"code","source":["1 == True\n","\n","# 1 and True are literally the same value.\n","# More interesting equalities and inequalities:\n","\n","0 == False\n","2 != True\n","None != False"],"metadata":{"id":"_PrnFf2lioMB","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680614442403,"user_tz":-120,"elapsed":208,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"9db936a2-9182-402b-bf50-207b3366496a"},"execution_count":null,"outputs":[{"output_type":"execute_result","data":{"text/plain":["True"]},"metadata":{},"execution_count":155}]},{"cell_type":"markdown","source":["3. Replace one value in each of the following expressions so the expression becomes `True`."],"metadata":{"id":"q8QviA70kdQE"}},{"cell_type":"code","source":["3 > height\n","0 == 0\n","'Julian' < 'Julian' * 2\n","name < 2 * name"],"metadata":{"id":"qDQ9Ob5Zkrqm","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680614593732,"user_tz":-120,"elapsed":320,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"af8cc847-7dbe-4ba7-fd7b-406e47d7b69d"},"execution_count":null,"outputs":[{"output_type":"execute_result","data":{"text/plain":["True"]},"metadata":{},"execution_count":165}]},{"cell_type":"markdown","source":["4. Replace one operator in each of the following expressions so the expression becomes `True`."],"metadata":{"id":"YbdV7SQVmDVV"}},{"cell_type":"code","source":["5 > 4\n","2 - 1 == 1\n","3 + 3 == 3 * 2"],"metadata":{"id":"hzjXIwkAmChv","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680614702559,"user_tz":-120,"elapsed":254,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"a1592d3e-971f-4c75-b496-13e6875e1a55"},"execution_count":null,"outputs":[{"output_type":"execute_result","data":{"text/plain":["True"]},"metadata":{},"execution_count":171}]},{"cell_type":"markdown","source":["5. The *Groot woordenboek van de Nederlandse taal* by Van Dale publishers, colloquially known as \"De dikke Van Dale\", is a three-tome Dutch dictionary. The first tome contains words starting a–i, the second j–q and the third r–z. In the code block below, write an expression that will tell you whether `word` should be in the second tome. Try different values of `word` to verify that your solution is correct. You do not need to check whether the word is actually Dutch!"],"metadata":{"id":"SwyAMDzDn2_X"}},{"cell_type":"code","source":["word = 'archaïsch' # try also 'idee', 'j', 'quasi', 'r', 'ziezo'\n","\n","# is it in the FIRST tome?\n","word < 'j'\n","\n","# is it in the THIRD tome?\n","word >= 'r'\n","\n","# is it in the SECOND tome? (opposites of the previous expressions)\n","word >= 'j'\n","word < 'r'\n","\n","# combining both conditions in a single expression\n","'j' <= word < 'r'"],"metadata":{"id":"lmCGfx_DqMWe","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680710351970,"user_tz":-120,"elapsed":193,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"77da4795-3001-4582-ce37-58d289b7a181"},"execution_count":3,"outputs":[{"output_type":"execute_result","data":{"text/plain":["False"]},"metadata":{},"execution_count":3}]},{"cell_type":"markdown","source":["## Next module\n","\n","[3. Conditionals](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw) - [solutions](https://colab.research.google.com/drive/1Nvvjc3fGnMg2tWvfw2W1gVn2oKKbWzGI)"],"metadata":{"id":"jXSxbjf4q6q5"}}]} \ No newline at end of file +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "fqMJHzNk5yXQ" + }, + "source": [ + "# Module 2: Values and expressions\n", + "\n", + "### Exercise solutions\n", + "\n", + "[Module 2](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp)\n", + "\n", + "### CDH course \"Programming in Python\"\n", + "\n", + "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "\n", + "Previous module: [1. Introduction](https://colab.research.google.com/drive/1i4wJpUIr77Fh1renWNjt00Bd51NbC1nB) - [solutions](https://colab.research.google.com/drive/1RIwG7hA-Ymjcm1dmSW6wA_hu1clDNrAd)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pDU1uK2Igmki" + }, + "source": [ + "## Exercise 2.1: Variables and state\n", + "\n", + "In each of the code blocks below, try to predict what will be printed, then run the code. If your guess was incorrect, try to figure out why the result is different. If your guess was correct, celebrate!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 420, + "status": "ok", + "timestamp": 1680606359445, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "FydkKnx_hUPq", + "outputId": "0cce4013-6c8a-44a9-ecc8-6720dcfcb047" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "vanilla\n" + ] + } + ], + "source": [ + "flavor = 'vanilla'\n", + "print(flavor)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 402, + "status": "ok", + "timestamp": 1680606363219, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "LTWods50h-Ij", + "outputId": "41e65fc2-80ff-45ea-dfd1-8109e8d96b95" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "vanilla\n" + ] + } + ], + "source": [ + "temperature = 70\n", + "print(flavor)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pmnKAg7H95BW" + }, + "source": [ + "The variable `flavor` was retained from the previous code block. The variable `temperature` is not used here." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 450, + "status": "ok", + "timestamp": 1680606385469, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "l2vT4L7piGRf", + "outputId": "472eeb4a-fe6e-4696-9817-0a481a153d5a" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "70\n", + "35\n" + ] + } + ], + "source": [ + "print(temperature)\n", + "temperature = 35\n", + "print(temperature)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ElhEcRIB-EsA" + }, + "source": [ + "`temperature` is first printed as it was retained from the previous code block. Then, it is reassigned and printed again." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 230, + "status": "ok", + "timestamp": 1680606390493, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "k1Z_yWLXiWy7", + "outputId": "41a6d3ad-c50e-4bd4-f347-07eae4f8fae9" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Garfield\n", + "Garfield\n" + ] + } + ], + "source": [ + "dog_name = 'Bobby'\n", + "cat_name = 'Garfield'\n", + "dog_name = cat_name\n", + "cat_name = dog_name\n", + "print(dog_name)\n", + "print(cat_name)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "iFkF67zp-LJk" + }, + "source": [ + "On line 3, it is important to realize that `dog_name` gets *the value of* `cat_name`. There is no permanent tie between the variables. After line 3, both variables have the same value. Hence, `cat_name` cannot change into anything else on line 4. The program has effectively forgotten the value `'Bobby'`." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BZ50KykuAYPs" + }, + "source": [ + "Before running the following code, try to explain why it does *not* output `chocolate`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 202, + "status": "ok", + "timestamp": 1680606431330, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "X3xHg6K4Aicn", + "outputId": "5a5e974b-cb7a-466b-f900-b8b586080212" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "sweet\n" + ] + } + ], + "source": [ + "sweet = 'chocolate'\n", + "savory = 'cheese'\n", + "dessert = 'sweet'\n", + "print(dessert)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1qb27-Rf-wIH" + }, + "source": [ + "On line 3, `'sweet'` is quoted, so Python considers it a string. It does not recognize it as a variable, even though we also defined a variable with that name on line 1." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0az3tNK7WD3G" + }, + "source": [ + "In each of the following lines of code, replace `None` by a value of your choice. Make it such that `my_int` is an integer, `my_float` is a float, `my_bool` is a boolean and `my_string` is a string." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "lY-M8mfSXDfG" + }, + "outputs": [], + "source": [ + "my_int = 42\n", + "my_float = .5\n", + "my_bool = True\n", + "my_string = \"I'm starving\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "dvNIQh7KYuJb" + }, + "source": [ + "## Exercise 2.2: Bonus" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GI9fyUO8XOcp" + }, + "source": [ + "How could you verify in code whether the variables you wrote above have the correct type? Write this code below.\n", + "\n", + "Hint: scroll back to the section [Difference between types](#scrollTo=Difference_between_types)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 223, + "status": "ok", + "timestamp": 1680606693012, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "Om6z53RXYBoS", + "outputId": "3f66fabe-bd2c-4046-d165-b6dc475ffcc2" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\n", + "\n" + ] + } + ], + "source": [ + "print(type(my_int))\n", + "print(type(my_float))\n", + "print(type(my_bool))\n", + "print(type(my_string))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "h6UKIMXCj3w9" + }, + "source": [ + "In the code block below, replace the question mark `?` by a variable name. Make it such that the final output is `'Hooray!'`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 247, + "status": "ok", + "timestamp": 1680606893191, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "3MI6_DY7ku30", + "outputId": "6a605092-cca4-41c3-dfa3-0196c98be37b" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hooray!\n" + ] + } + ], + "source": [ + "cheer = 'Hooray!'\n", + "alarm = 'Oh no!'\n", + "anthem = 'Wilhelmus van Nassauwe'\n", + "alarm = anthem\n", + "alarm = cheer # alarm instead of ?\n", + "cheer = anthem\n", + "anthem = alarm\n", + "print(anthem)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "z_bXvnya5J2_" + }, + "source": [ + "## Exercise 2.3: Expressions\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "j9xhznQlbCBf" + }, + "source": [ + "1. Try to predict the value of each of the following code blocks. Can you explain any surprises?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 415, + "status": "ok", + "timestamp": 1680609962892, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "0QcMB4xwbSfL", + "outputId": "609a5c45-cd77-4aaa-c33e-928263e14c41" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 92, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 + 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 10, + "status": "ok", + "timestamp": 1680609965892, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "eHKN9kP9bWkm", + "outputId": "9b17e8cc-3a5a-4ac4-db2f-1454eae873af" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 93, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 * 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 323, + "status": "ok", + "timestamp": 1680609969742, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "uINoRNNbbXwJ", + "outputId": "1b281881-5bbc-476e-e38c-bda84855665d" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 94, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = 1\n", + "b = 2\n", + "a + b" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 15, + "status": "ok", + "timestamp": 1680609971809, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "4xyWkYlnbc_8", + "outputId": "9b2dae89-2331-47de-83a5-4ac63e71a06d" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 95, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c = b\n", + "a * b * c" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 38 + }, + "executionInfo": { + "elapsed": 408, + "status": "ok", + "timestamp": 1680609976922, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "7tW2T4mebljv", + "outputId": "404d4cac-d77d-40f8-c6bc-b6f4fcf0e125" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'hakunamatata'" + ] + }, + "execution_count": 96, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "'hakuna' + 'matata'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "wG4N61Jz_WJV" + }, + "source": [ + "If we had done `print('hakuna', 'matata')`, it would have output `hakuna matata`, with a space between the words. When we concatenate strings using `+`, Python does not take that liberty, so if we actually want to separate words with spaces, we have to add them ourselves." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 38 + }, + "executionInfo": { + "elapsed": 332, + "status": "ok", + "timestamp": 1680609981027, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "uEdPHIhBb2Mw", + "outputId": "fc706353-8980-4baa-fc4b-cca736f757e9" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'water~water~water~'" + ] + }, + "execution_count": 97, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "liquid = 'water~'\n", + "liquid * 3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 236, + "status": "ok", + "timestamp": 1680609983171, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "-D7BB50Qceo2", + "outputId": "1169bc24-b2d3-41d5-b190-7e1135e3d93a" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 98, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "5 - 2 - 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 237, + "status": "ok", + "timestamp": 1680609985039, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "OQUGr5rGck3m", + "outputId": "95e07168-7633-4cc0-f961-d8beb34e5612" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 99, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "5 - (2 - 1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "btuLZMUG_xfJ" + }, + "source": [ + "The above result is different from the previous, because Python evaluates minus operators (and most other operators) left-to-right by default." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 212, + "status": "ok", + "timestamp": 1680609988715, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "Jc8fawaIdCD5", + "outputId": "7d524d26-14c3-439a-e78f-914552814109" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "80" + ] + }, + "execution_count": 100, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "income = 100\n", + "tax = 20\n", + "net_income = income - tax\n", + "tax = 15\n", + "net_income" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "w08CSKAhADsd" + }, + "source": [ + "On line 3, we set `net_income` to the outcome of *the value of* `income` minus *the value of* `tax`, which are respectively `100` and `20` at that point. `net_income` then keeps its value (`80`) until we change it directly. Hence, line 4 does not change anything about `net_income`. If we wanted to update `net_income`, we would have to run line 3 again." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "TlLkcRv-droI" + }, + "source": [ + "2. In the code block below, change the values of `character` and `multiplier` so that the output becomes `'Goooood!'`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 38 + }, + "executionInfo": { + "elapsed": 406, + "status": "ok", + "timestamp": 1680610076844, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "pYZUkeDJenob", + "outputId": "97c1b7a5-adf1-4112-b0b4-9a7a826855de" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'Goooood!'" + ] + }, + "execution_count": 101, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "character = 'o'\n", + "multiplier = 5\n", + "\n", + "begin = 'G'\n", + "middle = character * multiplier\n", + "end = 'd!'\n", + "\n", + "begin + middle + end" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "G8X4n_a8a5tl" + }, + "source": [ + "3. Rewrite your Hello world-program:\n", + " - Build the \"hello, world!\" string using multiple variables.\n", + " - Make the program say \"hello, world!\" 3 times, each time on a new line" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 8, + "status": "ok", + "timestamp": 1680708229954, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "nDUVvhDEfIVI", + "outputId": "8be0c0dd-dc1e-48cc-bfae-5b3c2520efed" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello, world!\n", + "Hello, world!\n", + "Hello, world!\n", + "\n", + "Hello, world!\n", + "Hello, world!\n", + "Hello, world!\n", + "\n", + "Hello, world!\n", + "Hello, world!\n", + "Hello, world!\n", + "\n" + ] + } + ], + "source": [ + "# This works:\n", + "hello = 'Hello, '\n", + "world = 'world!\\n'\n", + "variable = hello + world\n", + "print(3 * variable)\n", + "\n", + "# This works, too:\n", + "slogan = '''Hello, world!\n", + "'''\n", + "result = 3 * slogan\n", + "print(result)\n", + "\n", + "# This would also work, but uses no variables:\n", + "print('Hello, world!\\n' * 3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3HCqdTj2fVPK" + }, + "source": [ + "## Exercise 2.4: Bonus" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "EKFdkLkWa8EY" + }, + "source": [ + "1. Find out and describe what the following operators do:\n", + " - `%` (e.g. `10 % 2`)\n", + " - `//` (e.g. `10 // 2`)\n", + " - `**` (e.g. `10 ** 2`)\n", + " - Tip: write the expressions using variables. Change the variables and see how it affects the outcome." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "V59a7vpDfzO2" + }, + "outputs": [], + "source": [ + "# Regarding %, you will quickly see a pattern if you set\n", + "# the right operand to 3 and try a series of consecutive\n", + "# numbers as the left operand:\n", + "\n", + "10 % 3 == 1\n", + "11 % 3 == 2\n", + "12 % 3 == 0\n", + "13 % 3 == 1\n", + "14 % 3 == 2\n", + "15 % 3 == 0\n", + "16 % 3 == 1\n", + "17 % 3 == 2\n", + "18 % 3 == 0\n", + "\n", + "# Conclusion: % calculates the remainder of division.\n", + "\n", + "# // is like / (division), but rounded down to an integer.\n", + "10 / 3 == 3.33333333333333333\n", + "10 // 3 == 3\n", + "5 / 4 == 1.25\n", + "5 // 4 == 1\n", + "\n", + "# In general, the following equation is always true if\n", + "# a and b are integers:\n", + "(a // b) * b + a % b == a\n", + "\n", + "# a ** b raises a to the power of b.\n", + "3 ** 2 == 3 * 3 == 9\n", + "3 ** 3 == 3 * 3 * 3 == 27\n", + "3 ** 4 == 3 * 3 * 3 * 3 == 81" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "o0WYIAUla-AX" + }, + "source": [ + "2. Predict what will be printed on each line in the following code block, then run the code to check your answer. If you made any mistakes, try to figure out what is causing the difference." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 202, + "status": "ok", + "timestamp": 1680709009778, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "Z9Vd9pdIUzT5", + "outputId": "1cde1422-5f7f-460f-c537-5393acf5bb2a" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "stylometrystylometrystylometry2\n", + "stylometrystylometrystylometrystylometrystylometry\n", + "stylometrystylometrystylometry2\n", + "stylometry2stylometry2stylometry2\n", + "stylometry6\n", + "stylometry222\n" + ] + } + ], + "source": [ + "word = 'stylometry'\n", + "repeats = 3\n", + "extra = 2\n", + "\n", + "print((word * repeats) + str(extra))\n", + "# Evaluated in this order:\n", + "word == 'stylometry'\n", + "repeats == 3\n", + "'stylometry' * 3 == 'stylometrystylometrystylometry'\n", + "str(extra) == '2'\n", + "'stylometrystylometrystylometry' + '2' == 'stylometrystylometrystylometry2'\n", + "\n", + "print(word * (repeats + extra))\n", + "# Evaluated in this order:\n", + "word == 'stylometry'\n", + "repeats + extra == 5\n", + "'stylometry' * 5 == 'stylometrystylometrystylometrystylometrystylometry'\n", + "\n", + "print(word * repeats + str(extra))\n", + "# Evaluated in the same way as line 5, just without parentheses.\n", + "# The * operator takes precedence over the + operator by default.\n", + "\n", + "print((word + str(extra)) * repeats)\n", + "# Evaluated in this order:\n", + "word == 'stylometry'\n", + "str(extra) == '2'\n", + "word + str(extra) == 'stylometry2'\n", + "repeats == 3\n", + "'stylometry2' * 3 == 'stylometry2stylometry2stylometry2'\n", + "\n", + "print(word + str(extra * repeats))\n", + "# Evaluated in this order:\n", + "word == 'stylometry'\n", + "extra == 2\n", + "repeats == 3\n", + "2 * 3 == 6\n", + "str(6) == '6'\n", + "'stylometry' + '6' == 'stylometry6'\n", + "\n", + "print(word + str(extra) * repeats)\n", + "# Evaluated in this order:\n", + "word == 'stylometry'\n", + "extra == 2\n", + "str(2) == '2'\n", + "repeats == 3\n", + "# * operator takes precedence over +, so right subexpression first.\n", + "'2' * 3 == '222'\n", + "'stylometry' + '222' == 'stylometry222'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "DRObBQZHgsIG" + }, + "source": [ + "3. Using just the variables `word`, `repeats` and `extra` from the previous code block, write a single big expression that evaluates to `'stylometry555stylometry'`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "uLuiUk10gqwM" + }, + "outputs": [], + "source": [ + "word + str(extra + repeats) * repeats + word\n", + "# (you can add parentheses, as long as you evaluate the * before the +)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pvrcgKU18OrJ" + }, + "source": [ + "## Exercise 2.5: boolean expressions" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "sWdnjezoil9j" + }, + "source": [ + "1. Predict for each boolean expression below: `True` or `False`? If any of your predictions is incorrect, try to find the reason." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 394, + "status": "ok", + "timestamp": 1680614316115, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "RpSvrom3Z8fQ", + "outputId": "d3c6bb57-e8c1-4891-c01d-850fd504e1f5" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 148, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "name = 'Jelte'\n", + "height = 2\n", + "\n", + "name * height == 'Jelte Jelte'\n", + "# False because string repetition does not add spaces\n", + "\n", + "height < 2\n", + "# a number can never be less than itself\n", + "\n", + "2 <= height\n", + "# ... but a number is always less than or equal to itself\n", + "\n", + "1 < height <= 2\n", + "# Python allows you to compare the same expression on two sides\n", + "\n", + "2 <= height < 1\n", + "# Both comparison must be true for the whole expression to be True\n", + "\n", + "name <= 'Julian'\n", + "# Lexicographical comparison: 'Julian' is greater because\n", + "# 'u' comes after 'e'.\n", + "\n", + "height * 3 + 1 >= height\n", + "# * and + take precedence over >=, so the evaluation goes in this order:\n", + "height * 3 == 6\n", + "6 + 1 == 7\n", + "height == 2\n", + "(7 >= 2) == True" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zJiaGIlZBN_P" + }, + "source": [ + "2. Run the code block below. Did you expect this result? Can you explain it?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 208, + "status": "ok", + "timestamp": 1680614442403, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "_PrnFf2lioMB", + "outputId": "9db936a2-9182-402b-bf50-207b3366496a" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 155, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 == True\n", + "\n", + "# 1 and True are literally the same value.\n", + "# More interesting equalities and inequalities:\n", + "\n", + "0 == False\n", + "2 != True\n", + "None != False" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "q8QviA70kdQE" + }, + "source": [ + "3. Replace one value in each of the following expressions so the expression becomes `True`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 320, + "status": "ok", + "timestamp": 1680614593732, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "qDQ9Ob5Zkrqm", + "outputId": "af8cc847-7dbe-4ba7-fd7b-406e47d7b69d" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 165, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "3 > height\n", + "0 == 0\n", + "'Julian' < 'Julian' * 2\n", + "name < 2 * name" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "YbdV7SQVmDVV" + }, + "source": [ + "4. Replace one operator in each of the following expressions so the expression becomes `True`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 254, + "status": "ok", + "timestamp": 1680614702559, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "hzjXIwkAmChv", + "outputId": "a1592d3e-971f-4c75-b496-13e6875e1a55" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 171, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "5 > 4\n", + "2 - 1 == 1\n", + "3 + 3 == 3 * 2" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SwyAMDzDn2_X" + }, + "source": [ + "5. The *Groot woordenboek van de Nederlandse taal* by Van Dale publishers, colloquially known as \"De dikke Van Dale\", is a three-tome Dutch dictionary. The first tome contains words starting a–i, the second j–q and the third r–z. In the code block below, write an expression that will tell you whether `word` should be in the second tome. Try different values of `word` to verify that your solution is correct. You do not need to check whether the word is actually Dutch!" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 193, + "status": "ok", + "timestamp": 1680710351970, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "lmCGfx_DqMWe", + "outputId": "77da4795-3001-4582-ce37-58d289b7a181" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "word = 'archaïsch' # try also 'idee', 'j', 'quasi', 'r', 'ziezo'\n", + "\n", + "# is it in the FIRST tome?\n", + "word < 'j'\n", + "\n", + "# is it in the THIRD tome?\n", + "word >= 'r'\n", + "\n", + "# is it in the SECOND tome? (opposites of the previous expressions)\n", + "word >= 'j'\n", + "word < 'r'\n", + "\n", + "# combining both conditions in a single expression\n", + "'j' <= word < 'r'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "jXSxbjf4q6q5" + }, + "source": [ + "## Next module\n", + "\n", + "[3. Conditionals](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw) - [solutions](https://colab.research.google.com/drive/1Nvvjc3fGnMg2tWvfw2W1gVn2oKKbWzGI)" + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyPhSPWbSkxQNLvGfLg7Ov8q", + "provenance": [], + "toc_visible": true + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/solutions/02 values and expressions solutions.py b/solutions/02 values and expressions solutions.py new file mode 100644 index 0000000..5874e48 --- /dev/null +++ b/solutions/02 values and expressions solutions.py @@ -0,0 +1,386 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# name: python3 +# --- + +# %% [markdown] id="fqMJHzNk5yXQ" +# # Module 2: Values and expressions +# +# ### Exercise solutions +# +# [Module 2](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp) +# +# ### CDH course "Programming in Python" +# +# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# +# Previous module: [1. Introduction](https://colab.research.google.com/drive/1i4wJpUIr77Fh1renWNjt00Bd51NbC1nB) - [solutions](https://colab.research.google.com/drive/1RIwG7hA-Ymjcm1dmSW6wA_hu1clDNrAd) + +# %% [markdown] id="pDU1uK2Igmki" +# ## Exercise 2.1: Variables and state +# +# In each of the code blocks below, try to predict what will be printed, then run the code. If your guess was incorrect, try to figure out why the result is different. If your guess was correct, celebrate! + +# %% id="FydkKnx_hUPq" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680606359445, "user_tz": -120, "elapsed": 420, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="0cce4013-6c8a-44a9-ecc8-6720dcfcb047" +flavor = 'vanilla' +print(flavor) + +# %% id="LTWods50h-Ij" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680606363219, "user_tz": -120, "elapsed": 402, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="41e65fc2-80ff-45ea-dfd1-8109e8d96b95" +temperature = 70 +print(flavor) + +# %% [markdown] id="pmnKAg7H95BW" +# The variable `flavor` was retained from the previous code block. The variable `temperature` is not used here. + +# %% id="l2vT4L7piGRf" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680606385469, "user_tz": -120, "elapsed": 450, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="472eeb4a-fe6e-4696-9817-0a481a153d5a" +print(temperature) +temperature = 35 +print(temperature) + +# %% [markdown] id="ElhEcRIB-EsA" +# `temperature` is first printed as it was retained from the previous code block. Then, it is reassigned and printed again. + +# %% id="k1Z_yWLXiWy7" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680606390493, "user_tz": -120, "elapsed": 230, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="41a6d3ad-c50e-4bd4-f347-07eae4f8fae9" +dog_name = 'Bobby' +cat_name = 'Garfield' +dog_name = cat_name +cat_name = dog_name +print(dog_name) +print(cat_name) + +# %% [markdown] id="iFkF67zp-LJk" +# On line 3, it is important to realize that `dog_name` gets *the value of* `cat_name`. There is no permanent tie between the variables. After line 3, both variables have the same value. Hence, `cat_name` cannot change into anything else on line 4. The program has effectively forgotten the value `'Bobby'`. + +# %% [markdown] id="BZ50KykuAYPs" +# Before running the following code, try to explain why it does *not* output `chocolate`. + +# %% id="X3xHg6K4Aicn" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680606431330, "user_tz": -120, "elapsed": 202, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="5a5e974b-cb7a-466b-f900-b8b586080212" +sweet = 'chocolate' +savory = 'cheese' +dessert = 'sweet' +print(dessert) + +# %% [markdown] id="1qb27-Rf-wIH" +# On line 3, `'sweet'` is quoted, so Python considers it a string. It does not recognize it as a variable, even though we also defined a variable with that name on line 1. + +# %% [markdown] id="0az3tNK7WD3G" +# In each of the following lines of code, replace `None` by a value of your choice. Make it such that `my_int` is an integer, `my_float` is a float, `my_bool` is a boolean and `my_string` is a string. + +# %% id="lY-M8mfSXDfG" +my_int = 42 +my_float = .5 +my_bool = True +my_string = "I'm starving" + +# %% [markdown] id="dvNIQh7KYuJb" +# ## Exercise 2.2: Bonus + +# %% [markdown] id="GI9fyUO8XOcp" +# How could you verify in code whether the variables you wrote above have the correct type? Write this code below. +# +# Hint: scroll back to the section [Difference between types](#scrollTo=Difference_between_types). + +# %% id="Om6z53RXYBoS" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680606693012, "user_tz": -120, "elapsed": 223, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="3f66fabe-bd2c-4046-d165-b6dc475ffcc2" +print(type(my_int)) +print(type(my_float)) +print(type(my_bool)) +print(type(my_string)) + +# %% [markdown] id="h6UKIMXCj3w9" +# In the code block below, replace the question mark `?` by a variable name. Make it such that the final output is `'Hooray!'`. + +# %% id="3MI6_DY7ku30" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680606893191, "user_tz": -120, "elapsed": 247, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="6a605092-cca4-41c3-dfa3-0196c98be37b" +cheer = 'Hooray!' +alarm = 'Oh no!' +anthem = 'Wilhelmus van Nassauwe' +alarm = anthem +alarm = cheer # alarm instead of ? +cheer = anthem +anthem = alarm +print(anthem) + +# %% [markdown] id="z_bXvnya5J2_" +# ## Exercise 2.3: Expressions +# +# + +# %% [markdown] id="j9xhznQlbCBf" +# 1. Try to predict the value of each of the following code blocks. Can you explain any surprises? + +# %% id="0QcMB4xwbSfL" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680609962892, "user_tz": -120, "elapsed": 415, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="609a5c45-cd77-4aaa-c33e-928263e14c41" +1 + 1 + +# %% id="eHKN9kP9bWkm" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680609965892, "user_tz": -120, "elapsed": 10, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="9b17e8cc-3a5a-4ac4-db2f-1454eae873af" +1 * 1 + +# %% id="uINoRNNbbXwJ" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680609969742, "user_tz": -120, "elapsed": 323, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="1b281881-5bbc-476e-e38c-bda84855665d" +a = 1 +b = 2 +a + b + +# %% id="4xyWkYlnbc_8" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680609971809, "user_tz": -120, "elapsed": 15, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="9b2dae89-2331-47de-83a5-4ac63e71a06d" +c = b +a * b * c + +# %% id="7tW2T4mebljv" colab={"base_uri": "https://localhost:8080/", "height": 38} executionInfo={"status": "ok", "timestamp": 1680609976922, "user_tz": -120, "elapsed": 408, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="404d4cac-d77d-40f8-c6bc-b6f4fcf0e125" +'hakuna' + 'matata' + +# %% [markdown] id="wG4N61Jz_WJV" +# If we had done `print('hakuna', 'matata')`, it would have output `hakuna matata`, with a space between the words. When we concatenate strings using `+`, Python does not take that liberty, so if we actually want to separate words with spaces, we have to add them ourselves. + +# %% id="uEdPHIhBb2Mw" colab={"base_uri": "https://localhost:8080/", "height": 38} executionInfo={"status": "ok", "timestamp": 1680609981027, "user_tz": -120, "elapsed": 332, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="fc706353-8980-4baa-fc4b-cca736f757e9" +liquid = 'water~' +liquid * 3 + +# %% id="-D7BB50Qceo2" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680609983171, "user_tz": -120, "elapsed": 236, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="1169bc24-b2d3-41d5-b190-7e1135e3d93a" +5 - 2 - 1 + +# %% id="OQUGr5rGck3m" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680609985039, "user_tz": -120, "elapsed": 237, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="95e07168-7633-4cc0-f961-d8beb34e5612" +5 - (2 - 1) + +# %% [markdown] id="btuLZMUG_xfJ" +# The above result is different from the previous, because Python evaluates minus operators (and most other operators) left-to-right by default. + +# %% id="Jc8fawaIdCD5" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680609988715, "user_tz": -120, "elapsed": 212, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="7d524d26-14c3-439a-e78f-914552814109" +income = 100 +tax = 20 +net_income = income - tax +tax = 15 +net_income + +# %% [markdown] id="w08CSKAhADsd" +# On line 3, we set `net_income` to the outcome of *the value of* `income` minus *the value of* `tax`, which are respectively `100` and `20` at that point. `net_income` then keeps its value (`80`) until we change it directly. Hence, line 4 does not change anything about `net_income`. If we wanted to update `net_income`, we would have to run line 3 again. + +# %% [markdown] id="TlLkcRv-droI" +# 2. In the code block below, change the values of `character` and `multiplier` so that the output becomes `'Goooood!'`. + +# %% id="pYZUkeDJenob" colab={"base_uri": "https://localhost:8080/", "height": 38} executionInfo={"status": "ok", "timestamp": 1680610076844, "user_tz": -120, "elapsed": 406, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="97c1b7a5-adf1-4112-b0b4-9a7a826855de" +character = 'o' +multiplier = 5 + +begin = 'G' +middle = character * multiplier +end = 'd!' + +begin + middle + end + +# %% [markdown] id="G8X4n_a8a5tl" +# 3. Rewrite your Hello world-program: +# - Build the "hello, world!" string using multiple variables. +# - Make the program say "hello, world!" 3 times, each time on a new line + +# %% id="nDUVvhDEfIVI" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680708229954, "user_tz": -120, "elapsed": 8, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="8be0c0dd-dc1e-48cc-bfae-5b3c2520efed" +# This works: +hello = 'Hello, ' +world = 'world!\n' +variable = hello + world +print(3 * variable) + +# This works, too: +slogan = '''Hello, world! +''' +result = 3 * slogan +print(result) + +# This would also work, but uses no variables: +print('Hello, world!\n' * 3) + +# %% [markdown] id="3HCqdTj2fVPK" +# ## Exercise 2.4: Bonus + +# %% [markdown] id="EKFdkLkWa8EY" +# 1. Find out and describe what the following operators do: +# - `%` (e.g. `10 % 2`) +# - `//` (e.g. `10 // 2`) +# - `**` (e.g. `10 ** 2`) +# - Tip: write the expressions using variables. Change the variables and see how it affects the outcome. + +# %% id="V59a7vpDfzO2" +# Regarding %, you will quickly see a pattern if you set +# the right operand to 3 and try a series of consecutive +# numbers as the left operand: + +10 % 3 == 1 +11 % 3 == 2 +12 % 3 == 0 +13 % 3 == 1 +14 % 3 == 2 +15 % 3 == 0 +16 % 3 == 1 +17 % 3 == 2 +18 % 3 == 0 + +# Conclusion: % calculates the remainder of division. + +# // is like / (division), but rounded down to an integer. +10 / 3 == 3.33333333333333333 +10 // 3 == 3 +5 / 4 == 1.25 +5 // 4 == 1 + +# In general, the following equation is always true if +# a and b are integers: +(a // b) * b + a % b == a + +# a ** b raises a to the power of b. +3 ** 2 == 3 * 3 == 9 +3 ** 3 == 3 * 3 * 3 == 27 +3 ** 4 == 3 * 3 * 3 * 3 == 81 + +# %% [markdown] id="o0WYIAUla-AX" +# 2. Predict what will be printed on each line in the following code block, then run the code to check your answer. If you made any mistakes, try to figure out what is causing the difference. + +# %% id="Z9Vd9pdIUzT5" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680709009778, "user_tz": -120, "elapsed": 202, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="1cde1422-5f7f-460f-c537-5393acf5bb2a" +word = 'stylometry' +repeats = 3 +extra = 2 + +print((word * repeats) + str(extra)) +# Evaluated in this order: +word == 'stylometry' +repeats == 3 +'stylometry' * 3 == 'stylometrystylometrystylometry' +str(extra) == '2' +'stylometrystylometrystylometry' + '2' == 'stylometrystylometrystylometry2' + +print(word * (repeats + extra)) +# Evaluated in this order: +word == 'stylometry' +repeats + extra == 5 +'stylometry' * 5 == 'stylometrystylometrystylometrystylometrystylometry' + +print(word * repeats + str(extra)) +# Evaluated in the same way as line 5, just without parentheses. +# The * operator takes precedence over the + operator by default. + +print((word + str(extra)) * repeats) +# Evaluated in this order: +word == 'stylometry' +str(extra) == '2' +word + str(extra) == 'stylometry2' +repeats == 3 +'stylometry2' * 3 == 'stylometry2stylometry2stylometry2' + +print(word + str(extra * repeats)) +# Evaluated in this order: +word == 'stylometry' +extra == 2 +repeats == 3 +2 * 3 == 6 +str(6) == '6' +'stylometry' + '6' == 'stylometry6' + +print(word + str(extra) * repeats) +# Evaluated in this order: +word == 'stylometry' +extra == 2 +str(2) == '2' +repeats == 3 +# * operator takes precedence over +, so right subexpression first. +'2' * 3 == '222' +'stylometry' + '222' == 'stylometry222' + +# %% [markdown] id="DRObBQZHgsIG" +# 3. Using just the variables `word`, `repeats` and `extra` from the previous code block, write a single big expression that evaluates to `'stylometry555stylometry'`. + +# %% id="uLuiUk10gqwM" +word + str(extra + repeats) * repeats + word +# (you can add parentheses, as long as you evaluate the * before the +) + +# %% [markdown] id="pvrcgKU18OrJ" +# ## Exercise 2.5: boolean expressions + +# %% [markdown] id="sWdnjezoil9j" +# 1. Predict for each boolean expression below: `True` or `False`? If any of your predictions is incorrect, try to find the reason. + +# %% id="RpSvrom3Z8fQ" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680614316115, "user_tz": -120, "elapsed": 394, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="d3c6bb57-e8c1-4891-c01d-850fd504e1f5" +name = 'Jelte' +height = 2 + +name * height == 'Jelte Jelte' +# False because string repetition does not add spaces + +height < 2 +# a number can never be less than itself + +2 <= height +# ... but a number is always less than or equal to itself + +1 < height <= 2 +# Python allows you to compare the same expression on two sides + +2 <= height < 1 +# Both comparison must be true for the whole expression to be True + +name <= 'Julian' +# Lexicographical comparison: 'Julian' is greater because +# 'u' comes after 'e'. + +height * 3 + 1 >= height +# * and + take precedence over >=, so the evaluation goes in this order: +height * 3 == 6 +6 + 1 == 7 +height == 2 +(7 >= 2) == True + +# %% [markdown] id="zJiaGIlZBN_P" +# 2. Run the code block below. Did you expect this result? Can you explain it? + +# %% id="_PrnFf2lioMB" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680614442403, "user_tz": -120, "elapsed": 208, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="9db936a2-9182-402b-bf50-207b3366496a" +1 == True + +# 1 and True are literally the same value. +# More interesting equalities and inequalities: + +0 == False +2 != True +None != False + +# %% [markdown] id="q8QviA70kdQE" +# 3. Replace one value in each of the following expressions so the expression becomes `True`. + +# %% id="qDQ9Ob5Zkrqm" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680614593732, "user_tz": -120, "elapsed": 320, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="af8cc847-7dbe-4ba7-fd7b-406e47d7b69d" +3 > height +0 == 0 +'Julian' < 'Julian' * 2 +name < 2 * name + +# %% [markdown] id="YbdV7SQVmDVV" +# 4. Replace one operator in each of the following expressions so the expression becomes `True`. + +# %% id="hzjXIwkAmChv" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680614702559, "user_tz": -120, "elapsed": 254, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="a1592d3e-971f-4c75-b496-13e6875e1a55" +5 > 4 +2 - 1 == 1 +3 + 3 == 3 * 2 + +# %% [markdown] id="SwyAMDzDn2_X" +# 5. The *Groot woordenboek van de Nederlandse taal* by Van Dale publishers, colloquially known as "De dikke Van Dale", is a three-tome Dutch dictionary. The first tome contains words starting a–i, the second j–q and the third r–z. In the code block below, write an expression that will tell you whether `word` should be in the second tome. Try different values of `word` to verify that your solution is correct. You do not need to check whether the word is actually Dutch! + +# %% id="lmCGfx_DqMWe" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680710351970, "user_tz": -120, "elapsed": 193, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="77da4795-3001-4582-ce37-58d289b7a181" +word = 'archaïsch' # try also 'idee', 'j', 'quasi', 'r', 'ziezo' + +# is it in the FIRST tome? +word < 'j' + +# is it in the THIRD tome? +word >= 'r' + +# is it in the SECOND tome? (opposites of the previous expressions) +word >= 'j' +word < 'r' + +# combining both conditions in a single expression +'j' <= word < 'r' + +# %% [markdown] id="jXSxbjf4q6q5" +# ## Next module +# +# [3. Conditionals](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw) - [solutions](https://colab.research.google.com/drive/1Nvvjc3fGnMg2tWvfw2W1gVn2oKKbWzGI) diff --git a/solutions/03 conditionals solutions.ipynb b/solutions/03 conditionals solutions.ipynb index 2e8fa46..c71a850 100644 --- a/solutions/03 conditionals solutions.ipynb +++ b/solutions/03 conditionals solutions.ipynb @@ -1 +1,447 @@ -{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"provenance":[],"toc_visible":true,"authorship_tag":"ABX9TyN+nbIqsypUJCun9Hzkloun"},"kernelspec":{"name":"python3","display_name":"Python 3"},"language_info":{"name":"python"}},"cells":[{"cell_type":"markdown","source":["# Module 3: Conditionals\n","\n","### Exercise solutions\n","\n","[Module 3](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw)\n","\n","### CDH course \"Programming in Python\"\n","\n","[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n","\n","Previous module: [2. Values and expressions](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp) - [solutions](https://colab.research.google.com/drive/1JKeNehBZ9hhHXPdVunQ_r9cv-lLR9Byy)"],"metadata":{"id":"fqMJHzNk5yXQ"}},{"cell_type":"markdown","source":["## Exercise 3.1: if/elif/else"],"metadata":{"id":"tvXa9KWXAwge"}},{"cell_type":"markdown","source":["1. Try to predict the output of the following code blocks. Can you explain any surprises?"],"metadata":{"id":"pKBuViM9u7ZC"}},{"cell_type":"code","source":["if 3 <= 2:\n"," # The first print is not executed,\n"," # because it is inside the conditional and\n"," # the condition is False.\n"," print('What a strange day.')\n","\n","# The second print is executed, because it is\n","# outside of the conditional.\n","print('What a strange day.')\n","\n","# Therefore, we see the sentence once."],"metadata":{"id":"sEQyw2xJvzQN","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680618391562,"user_tz":-120,"elapsed":232,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"f38ca572-f203-48ea-875e-a1d5b6b89fc6"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["What a strange day.\n"]}]},{"cell_type":"code","source":["if None == 0:\n"," # The condition is False, so this code never runs.\n"," # The pass statement makes no difference.\n"," pass\n"," print('I got nothing to do.')"],"metadata":{"id":"FnCFfEb_v-0Y"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["name = 'Julian'\n","\n","if name < 'Jelte':\n"," # This condition is False, so it does not execute.\n"," print('Alphabetically before Jelte')\n","elif name > 'Jelte':\n"," # This condition is True, so it does execute.\n"," print('Alphabetically after Jelte')\n","else:\n"," # One of the previous conditions was True, so\n"," # this block does not execute.\n"," print('Same name as Jelte')"],"metadata":{"id":"arta4ZQ0vDSC","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680618415623,"user_tz":-120,"elapsed":228,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"26955fab-9309-484f-c088-23bfc2d04480"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["Alphabetically after Jelte\n"]}]},{"cell_type":"markdown","source":["2. In the following code block, fill the condition in the `if` statement. If `value` is greater than `5`, `size` must be `'large'`; otherwise, `size` must be `'small'`. Change `value` a few times and rerun the code to check that your condition is correct."],"metadata":{"id":"4SVSiqHSu4WX"}},{"cell_type":"code","source":["value = 4\n","\n","if value > 5:\n"," size = 'large'\n","else:\n"," size = 'small'\n","\n","print(size)"],"metadata":{"id":"czmdFacjeUaL","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680618457948,"user_tz":-120,"elapsed":199,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"1d9e3543-af26-4b51-a511-3a575f11596f"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["small\n"]}]},{"cell_type":"markdown","source":["3. Write an `if`/`elif`/`else` statement like the previous, but with different cutoff points: if `value` is less than `4`, `size` must be `'small'`; if `value` is between `4` and `6` (inclusive), `size` must be `'medium'`; if `value` is greater than `6`, `size` must be `'large'`."],"metadata":{"id":"sEOwMi04e6WW"}},{"cell_type":"code","source":["value = 4\n","\n","# Option 1: multiple independent conditionals.\n","# This performs the same checks multiple times.\n","if value < 4:\n"," size = 'small'\n","if 4 <= value <= 6:\n"," size = 'medium'\n","if value > 6:\n"," size = 'large'\n","\n","# Option 2: a single if-elif-else. There is\n","# still a little bit of overlap between the first\n","# and the second condition.\n","if value < 4:\n"," size = 'small'\n","elif 4 <= value <= 6:\n"," size = 'medium'\n","else:\n"," size = 'large'\n","\n","# Option 3: a single if-elif-else, but now without\n","# any overlap between the conditions. We achieve this\n","# by changing the order in which we recognize the\n","# classifications. When the conditions are expensive\n","# to evaluate, this is generally the best option.\n","if value < 4:\n"," size = 'small'\n","elif value > 6:\n"," size = 'large'\n","else:\n"," size = 'medium'\n","\n","print(size)"],"metadata":{"id":"8HeaY6l9f9iA","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680618544259,"user_tz":-120,"elapsed":546,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"615a9d80-7dca-4e14-c9cc-2ee096f90f3f"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["medium\n"]}]},{"cell_type":"markdown","source":["4. Rewrite the conditional that checks if a letter is neither 'a' nor 'b', using a different notation."],"metadata":{"id":"TWzbez_2RUh4"}},{"cell_type":"code","source":["letter = 'c'\n","\n","# original\n","if not (letter == 'a' or letter == 'b'):\n"," print('neither a nor b')\n","\n","# a variant in which we change all operators\n","if letter != 'a' and letter != 'b':\n"," print('neither a nor b')\n","\n","# a variant using not\n","if not letter == 'a' and not letter == 'b':\n"," print('neither a nor b')\n","\n","# same as above, adding parentheses to clarify\n","# the order in which Python evaluates the operators\n","if (not (letter == 'a')) and (not (letter == 'b')):\n"," print('neither a nor b')"],"metadata":{"id":"XsIg1MD4JrPX","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680618957473,"user_tz":-120,"elapsed":10,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"87f54254-5a5f-47a4-bab8-7feb4703bf2f"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["neither a nor b\n"]}]},{"cell_type":"markdown","source":["## Exercise 3.2: Bonus"],"metadata":{"id":"-XEYQZJ1ya1j"}},{"cell_type":"markdown","source":["*FizzBuzz part 1* (advanced).\n","Write an `if`/`elif`/`else` statement that behaves as follows:\n","\n","- if `value` is divisible by 3 but not by 5, print `'Fizz'`;\n","- if `value` is divisible by 5 but not by 3, print `'Buzz'`;\n","- if `value` is divisible by 3 *and* by 5, print `'FizzBuzz'`;\n","- in all remaining cases, print `value`.\n","\n","Tip: use the result of [Exercise 2.4.1](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp#scrollTo=Exercise_2_4_Bonus)!"],"metadata":{"id":"POVFwRu_f91I"}},{"cell_type":"code","source":["value = 9\n","\n","# A straightforward variant, in which we literally\n","# translate the English description to Python code.\n","if value % 3 == 0 and value % 5 != 0:\n"," print('Fizz')\n","elif value % 5 == 0 and value % 3 != 0:\n"," print('Buzz')\n","elif value % 5 == 0 and value % 3 == 0:\n"," print('FizzBuzz')\n","else:\n"," print(value)\n","\n","# A more efficient variant, in which we use more \"tricks\":\n","# - We can start with an empty string and update it later.\n","result = ''\n","if value % 3 == 0:\n"," # - If value is divisible by 3, we ALWAYS want \"Fizz\",\n"," # even if value is ALSO divisible by 5.\n"," result = result + 'Fizz'\n","if value % 5 == 0:\n"," # - If value is divisible by 5, we can add \"Buzz\" to\n"," # the result regardless of whether it contains \"Fizz\".\n"," result = result + 'Buzz'\n","# - We use the short-circuiting behavior of and/or.\n","# - and: stop evaluating as soon as you find a False\n","# subexpression, because then the whole expression\n","# must be False as well.\n","# - or: stop evaluating as soon as you find a True\n","# subexpression, because then the whole expression\n","# must be True as well.\n","# If result is still the empty string, then it evaluates as\n","# False and we print value. Otherwise, or already knows that\n","# the expression will be True, so value is never evaluated\n","# and result is printed instead.\n","print(result or value)"],"metadata":{"id":"SZGeQtqEhiAK","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680782728257,"user_tz":-120,"elapsed":233,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"f067c099-61f7-4d68-851d-8e7db89ed04f"},"execution_count":2,"outputs":[{"output_type":"stream","name":"stdout","text":["Fizz\n"]}]},{"cell_type":"markdown","source":["## Next module\n","\n","[4. Datastructures](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF) - [solutions](https://colab.research.google.com/drive/1juOQFMlRmeVUfbKWy0wtZ9lWkSZmr2Gn)"],"metadata":{"id":"YBC4OfihzFho"}}]} \ No newline at end of file +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "fqMJHzNk5yXQ" + }, + "source": [ + "# Module 3: Conditionals\n", + "\n", + "### Exercise solutions\n", + "\n", + "[Module 3](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw)\n", + "\n", + "### CDH course \"Programming in Python\"\n", + "\n", + "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "\n", + "Previous module: [2. Values and expressions](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp) - [solutions](https://colab.research.google.com/drive/1JKeNehBZ9hhHXPdVunQ_r9cv-lLR9Byy)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tvXa9KWXAwge" + }, + "source": [ + "## Exercise 3.1: if/elif/else" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pKBuViM9u7ZC" + }, + "source": [ + "1. Try to predict the output of the following code blocks. Can you explain any surprises?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 232, + "status": "ok", + "timestamp": 1680618391562, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "sEQyw2xJvzQN", + "outputId": "f38ca572-f203-48ea-875e-a1d5b6b89fc6" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "What a strange day.\n" + ] + } + ], + "source": [ + "if 3 <= 2:\n", + " # The first print is not executed,\n", + " # because it is inside the conditional and\n", + " # the condition is False.\n", + " print('What a strange day.')\n", + "\n", + "# The second print is executed, because it is\n", + "# outside of the conditional.\n", + "print('What a strange day.')\n", + "\n", + "# Therefore, we see the sentence once." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "FnCFfEb_v-0Y" + }, + "outputs": [], + "source": [ + "if None == 0:\n", + " # The condition is False, so this code never runs.\n", + " # The pass statement makes no difference.\n", + " pass\n", + " print('I got nothing to do.')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 228, + "status": "ok", + "timestamp": 1680618415623, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "arta4ZQ0vDSC", + "outputId": "26955fab-9309-484f-c088-23bfc2d04480" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Alphabetically after Jelte\n" + ] + } + ], + "source": [ + "name = 'Julian'\n", + "\n", + "if name < 'Jelte':\n", + " # This condition is False, so it does not execute.\n", + " print('Alphabetically before Jelte')\n", + "elif name > 'Jelte':\n", + " # This condition is True, so it does execute.\n", + " print('Alphabetically after Jelte')\n", + "else:\n", + " # One of the previous conditions was True, so\n", + " # this block does not execute.\n", + " print('Same name as Jelte')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "4SVSiqHSu4WX" + }, + "source": [ + "2. In the following code block, fill the condition in the `if` statement. If `value` is greater than `5`, `size` must be `'large'`; otherwise, `size` must be `'small'`. Change `value` a few times and rerun the code to check that your condition is correct." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 199, + "status": "ok", + "timestamp": 1680618457948, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "czmdFacjeUaL", + "outputId": "1d9e3543-af26-4b51-a511-3a575f11596f" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "small\n" + ] + } + ], + "source": [ + "value = 4\n", + "\n", + "if value > 5:\n", + " size = 'large'\n", + "else:\n", + " size = 'small'\n", + "\n", + "print(size)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "sEOwMi04e6WW" + }, + "source": [ + "3. Write an `if`/`elif`/`else` statement like the previous, but with different cutoff points: if `value` is less than `4`, `size` must be `'small'`; if `value` is between `4` and `6` (inclusive), `size` must be `'medium'`; if `value` is greater than `6`, `size` must be `'large'`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 546, + "status": "ok", + "timestamp": 1680618544259, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "8HeaY6l9f9iA", + "outputId": "615a9d80-7dca-4e14-c9cc-2ee096f90f3f" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "medium\n" + ] + } + ], + "source": [ + "value = 4\n", + "\n", + "# Option 1: multiple independent conditionals.\n", + "# This performs the same checks multiple times.\n", + "if value < 4:\n", + " size = 'small'\n", + "if 4 <= value <= 6:\n", + " size = 'medium'\n", + "if value > 6:\n", + " size = 'large'\n", + "\n", + "# Option 2: a single if-elif-else. There is\n", + "# still a little bit of overlap between the first\n", + "# and the second condition.\n", + "if value < 4:\n", + " size = 'small'\n", + "elif 4 <= value <= 6:\n", + " size = 'medium'\n", + "else:\n", + " size = 'large'\n", + "\n", + "# Option 3: a single if-elif-else, but now without\n", + "# any overlap between the conditions. We achieve this\n", + "# by changing the order in which we recognize the\n", + "# classifications. When the conditions are expensive\n", + "# to evaluate, this is generally the best option.\n", + "if value < 4:\n", + " size = 'small'\n", + "elif value > 6:\n", + " size = 'large'\n", + "else:\n", + " size = 'medium'\n", + "\n", + "print(size)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "TWzbez_2RUh4" + }, + "source": [ + "4. Rewrite the conditional that checks if a letter is neither 'a' nor 'b', using a different notation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 10, + "status": "ok", + "timestamp": 1680618957473, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "XsIg1MD4JrPX", + "outputId": "87f54254-5a5f-47a4-bab8-7feb4703bf2f" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "neither a nor b\n" + ] + } + ], + "source": [ + "letter = 'c'\n", + "\n", + "# original\n", + "if not (letter == 'a' or letter == 'b'):\n", + " print('neither a nor b')\n", + "\n", + "# a variant in which we change all operators\n", + "if letter != 'a' and letter != 'b':\n", + " print('neither a nor b')\n", + "\n", + "# a variant using not\n", + "if not letter == 'a' and not letter == 'b':\n", + " print('neither a nor b')\n", + "\n", + "# same as above, adding parentheses to clarify\n", + "# the order in which Python evaluates the operators\n", + "if (not (letter == 'a')) and (not (letter == 'b')):\n", + " print('neither a nor b')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-XEYQZJ1ya1j" + }, + "source": [ + "## Exercise 3.2: Bonus" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "POVFwRu_f91I" + }, + "source": [ + "*FizzBuzz part 1* (advanced).\n", + "Write an `if`/`elif`/`else` statement that behaves as follows:\n", + "\n", + "- if `value` is divisible by 3 but not by 5, print `'Fizz'`;\n", + "- if `value` is divisible by 5 but not by 3, print `'Buzz'`;\n", + "- if `value` is divisible by 3 *and* by 5, print `'FizzBuzz'`;\n", + "- in all remaining cases, print `value`.\n", + "\n", + "Tip: use the result of [Exercise 2.4.1](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp#scrollTo=Exercise_2_4_Bonus)!" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 233, + "status": "ok", + "timestamp": 1680782728257, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "SZGeQtqEhiAK", + "outputId": "f067c099-61f7-4d68-851d-8e7db89ed04f" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Fizz\n" + ] + } + ], + "source": [ + "value = 9\n", + "\n", + "# A straightforward variant, in which we literally\n", + "# translate the English description to Python code.\n", + "if value % 3 == 0 and value % 5 != 0:\n", + " print('Fizz')\n", + "elif value % 5 == 0 and value % 3 != 0:\n", + " print('Buzz')\n", + "elif value % 5 == 0 and value % 3 == 0:\n", + " print('FizzBuzz')\n", + "else:\n", + " print(value)\n", + "\n", + "# A more efficient variant, in which we use more \"tricks\":\n", + "# - We can start with an empty string and update it later.\n", + "result = ''\n", + "if value % 3 == 0:\n", + " # - If value is divisible by 3, we ALWAYS want \"Fizz\",\n", + " # even if value is ALSO divisible by 5.\n", + " result = result + 'Fizz'\n", + "if value % 5 == 0:\n", + " # - If value is divisible by 5, we can add \"Buzz\" to\n", + " # the result regardless of whether it contains \"Fizz\".\n", + " result = result + 'Buzz'\n", + "# - We use the short-circuiting behavior of and/or.\n", + "# - and: stop evaluating as soon as you find a False\n", + "# subexpression, because then the whole expression\n", + "# must be False as well.\n", + "# - or: stop evaluating as soon as you find a True\n", + "# subexpression, because then the whole expression\n", + "# must be True as well.\n", + "# If result is still the empty string, then it evaluates as\n", + "# False and we print value. Otherwise, or already knows that\n", + "# the expression will be True, so value is never evaluated\n", + "# and result is printed instead.\n", + "print(result or value)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "YBC4OfihzFho" + }, + "source": [ + "## Next module\n", + "\n", + "[4. Datastructures](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF) - [solutions](https://colab.research.google.com/drive/1juOQFMlRmeVUfbKWy0wtZ9lWkSZmr2Gn)" + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyN+nbIqsypUJCun9Hzkloun", + "provenance": [], + "toc_visible": true + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/solutions/03 conditionals solutions.py b/solutions/03 conditionals solutions.py new file mode 100644 index 0000000..f991280 --- /dev/null +++ b/solutions/03 conditionals solutions.py @@ -0,0 +1,197 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# name: python3 +# --- + +# %% [markdown] id="fqMJHzNk5yXQ" +# # Module 3: Conditionals +# +# ### Exercise solutions +# +# [Module 3](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw) +# +# ### CDH course "Programming in Python" +# +# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# +# Previous module: [2. Values and expressions](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp) - [solutions](https://colab.research.google.com/drive/1JKeNehBZ9hhHXPdVunQ_r9cv-lLR9Byy) + +# %% [markdown] id="tvXa9KWXAwge" +# ## Exercise 3.1: if/elif/else + +# %% [markdown] id="pKBuViM9u7ZC" +# 1. Try to predict the output of the following code blocks. Can you explain any surprises? + +# %% id="sEQyw2xJvzQN" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680618391562, "user_tz": -120, "elapsed": 232, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="f38ca572-f203-48ea-875e-a1d5b6b89fc6" +if 3 <= 2: + # The first print is not executed, + # because it is inside the conditional and + # the condition is False. + print('What a strange day.') + +# The second print is executed, because it is +# outside of the conditional. +print('What a strange day.') + +# Therefore, we see the sentence once. + +# %% id="FnCFfEb_v-0Y" +if None == 0: + # The condition is False, so this code never runs. + # The pass statement makes no difference. + pass + print('I got nothing to do.') + +# %% id="arta4ZQ0vDSC" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680618415623, "user_tz": -120, "elapsed": 228, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="26955fab-9309-484f-c088-23bfc2d04480" +name = 'Julian' + +if name < 'Jelte': + # This condition is False, so it does not execute. + print('Alphabetically before Jelte') +elif name > 'Jelte': + # This condition is True, so it does execute. + print('Alphabetically after Jelte') +else: + # One of the previous conditions was True, so + # this block does not execute. + print('Same name as Jelte') + +# %% [markdown] id="4SVSiqHSu4WX" +# 2. In the following code block, fill the condition in the `if` statement. If `value` is greater than `5`, `size` must be `'large'`; otherwise, `size` must be `'small'`. Change `value` a few times and rerun the code to check that your condition is correct. + +# %% id="czmdFacjeUaL" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680618457948, "user_tz": -120, "elapsed": 199, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="1d9e3543-af26-4b51-a511-3a575f11596f" +value = 4 + +if value > 5: + size = 'large' +else: + size = 'small' + +print(size) + +# %% [markdown] id="sEOwMi04e6WW" +# 3. Write an `if`/`elif`/`else` statement like the previous, but with different cutoff points: if `value` is less than `4`, `size` must be `'small'`; if `value` is between `4` and `6` (inclusive), `size` must be `'medium'`; if `value` is greater than `6`, `size` must be `'large'`. + +# %% id="8HeaY6l9f9iA" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680618544259, "user_tz": -120, "elapsed": 546, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="615a9d80-7dca-4e14-c9cc-2ee096f90f3f" +value = 4 + +# Option 1: multiple independent conditionals. +# This performs the same checks multiple times. +if value < 4: + size = 'small' +if 4 <= value <= 6: + size = 'medium' +if value > 6: + size = 'large' + +# Option 2: a single if-elif-else. There is +# still a little bit of overlap between the first +# and the second condition. +if value < 4: + size = 'small' +elif 4 <= value <= 6: + size = 'medium' +else: + size = 'large' + +# Option 3: a single if-elif-else, but now without +# any overlap between the conditions. We achieve this +# by changing the order in which we recognize the +# classifications. When the conditions are expensive +# to evaluate, this is generally the best option. +if value < 4: + size = 'small' +elif value > 6: + size = 'large' +else: + size = 'medium' + +print(size) + +# %% [markdown] id="TWzbez_2RUh4" +# 4. Rewrite the conditional that checks if a letter is neither 'a' nor 'b', using a different notation. + +# %% id="XsIg1MD4JrPX" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680618957473, "user_tz": -120, "elapsed": 10, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="87f54254-5a5f-47a4-bab8-7feb4703bf2f" +letter = 'c' + +# original +if not (letter == 'a' or letter == 'b'): + print('neither a nor b') + +# a variant in which we change all operators +if letter != 'a' and letter != 'b': + print('neither a nor b') + +# a variant using not +if not letter == 'a' and not letter == 'b': + print('neither a nor b') + +# same as above, adding parentheses to clarify +# the order in which Python evaluates the operators +if (not (letter == 'a')) and (not (letter == 'b')): + print('neither a nor b') + +# %% [markdown] id="-XEYQZJ1ya1j" +# ## Exercise 3.2: Bonus + +# %% [markdown] id="POVFwRu_f91I" +# *FizzBuzz part 1* (advanced). +# Write an `if`/`elif`/`else` statement that behaves as follows: +# +# - if `value` is divisible by 3 but not by 5, print `'Fizz'`; +# - if `value` is divisible by 5 but not by 3, print `'Buzz'`; +# - if `value` is divisible by 3 *and* by 5, print `'FizzBuzz'`; +# - in all remaining cases, print `value`. +# +# Tip: use the result of [Exercise 2.4.1](https://colab.research.google.com/drive/17K6C_EZoeGtRxoTbYQvygFEdpWgQ2QFp#scrollTo=Exercise_2_4_Bonus)! + +# %% id="SZGeQtqEhiAK" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680782728257, "user_tz": -120, "elapsed": 233, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="f067c099-61f7-4d68-851d-8e7db89ed04f" +value = 9 + +# A straightforward variant, in which we literally +# translate the English description to Python code. +if value % 3 == 0 and value % 5 != 0: + print('Fizz') +elif value % 5 == 0 and value % 3 != 0: + print('Buzz') +elif value % 5 == 0 and value % 3 == 0: + print('FizzBuzz') +else: + print(value) + +# A more efficient variant, in which we use more "tricks": +# - We can start with an empty string and update it later. +result = '' +if value % 3 == 0: + # - If value is divisible by 3, we ALWAYS want "Fizz", + # even if value is ALSO divisible by 5. + result = result + 'Fizz' +if value % 5 == 0: + # - If value is divisible by 5, we can add "Buzz" to + # the result regardless of whether it contains "Fizz". + result = result + 'Buzz' +# - We use the short-circuiting behavior of and/or. +# - and: stop evaluating as soon as you find a False +# subexpression, because then the whole expression +# must be False as well. +# - or: stop evaluating as soon as you find a True +# subexpression, because then the whole expression +# must be True as well. +# If result is still the empty string, then it evaluates as +# False and we print value. Otherwise, or already knows that +# the expression will be True, so value is never evaluated +# and result is printed instead. +print(result or value) + +# %% [markdown] id="YBC4OfihzFho" +# ## Next module +# +# [4. Datastructures](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF) - [solutions](https://colab.research.google.com/drive/1juOQFMlRmeVUfbKWy0wtZ9lWkSZmr2Gn) diff --git a/solutions/04 datastructures solutions.ipynb b/solutions/04 datastructures solutions.ipynb index 4ef1399..927cdb4 100644 --- a/solutions/04 datastructures solutions.ipynb +++ b/solutions/04 datastructures solutions.ipynb @@ -1 +1,312 @@ -{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"provenance":[],"toc_visible":true,"authorship_tag":"ABX9TyOIQlgg0FQF3naGWr9eJVVL"},"kernelspec":{"name":"python3","display_name":"Python 3"},"language_info":{"name":"python"}},"cells":[{"cell_type":"markdown","source":["# Module 4: Datastructures\n","\n","### Exercise solutions\n","\n","[Module 4](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF)\n","\n","### CDH course \"Programming in Python\"\n","\n","[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n","\n","Previous module: [3. Conditionals](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw) - [solutions](https://colab.research.google.com/drive/1Nvvjc3fGnMg2tWvfw2W1gVn2oKKbWzGI)"],"metadata":{"id":"fqMJHzNk5yXQ"}},{"cell_type":"markdown","source":["## Exercise 4.1: Lists\n","\n","1. For each of the `print` statements below, what do you expect is printed? Run the lines to check predictions"],"metadata":{"id":"70aMsClGPRy9"}},{"cell_type":"code","source":["countries = ['japan', 'hungary', 'maldives', 'gabon', 'bhutan']\n","\n","print(countries[0])\n","# Index 0 is the FIRST element of the list, so 'japan'.\n","\n","print(countries[-3])\n","# Third element from the end (where 'bhutan' is first),\n","# so 'maldives'.\n","\n","print(countries[0:1] + countries[2:4])\n","# In a slice, the first index is inclusive while the last\n","# index is exclusive. Hence, we have two sublists:\n","# 1. starting at the first element, up to but not including the second\n","# ['japan']\n","# 2. starting at the third element, up to but not including the fifth\n","# ['maldives', 'gabon']\n","# and then we concatenate those to ['japan', 'maldives', 'gabon'].\n","\n","more_countries = countries + ['mexico', 'haiti']\n","print(more_countries)\n","# By concatenating two lists, we create a new, longer list,\n","# with all elements of the original lists at the same level.\n","\n","countries.append(['mexico', 'haiti'])\n","print(countries)\n","# Appending a list to another list makes the former\n","# a nested single element of the latter."],"metadata":{"id":"KMUxwcSqPlU1","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1680783691341,"user_tz":-120,"elapsed":8,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"523705a3-8ae0-47ab-ace7-e48ed326a404"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["japan\n","maldives\n","['japan', 'maldives', 'gabon']\n","['japan', 'hungary', 'maldives', 'gabon', 'bhutan', 'mexico', 'haiti']\n","['japan', 'hungary', 'maldives', 'gabon', 'bhutan', ['mexico', 'haiti']]\n"]}]},{"cell_type":"markdown","source":["2. Transform the list below into `['jasmin', 'john', 'ravi']` in one line of code. \n","\n"],"metadata":{"id":"TyebsOIpU6hv"}},{"cell_type":"code","source":["students = ['jasmin', 'ravi', 'john']\n","\n","# Option 1: modify students in place by\n","# reassinging a slice of it.\n","students[1:3] = [students[2], students[1]]\n","\n","# Option 2: create a new list based on students.\n","new_students = students[:1] + students[2:] + students[1:2]\n","\n","# If you figured out stepped slices in the bonus exercise,\n","# you have a few more interesting options:\n","\n","# Variant of option 1\n","students[1:3] = students[:-3:-1]\n","\n","# Variant of option 2\n","new_students = students[:1] + students[:-3:-1]"],"metadata":{"id":"H8o6vsHKVKoq"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["3. For each of the print statements below, what do you expect is printed? Run the lines to check predictions."],"metadata":{"id":"HMU5X7XFWbCw"}},{"cell_type":"code","source":["random_fruit = 'pineapple'\n","fruits = ['apple', 'pear', random_fruit]\n","print(fruits)\n","# ['apple', 'pear', 'pineapple']\n","\n","random_fruit = 'blueberry'\n","print(fruits)\n","# Still the same. The value of random_fruit was\n","# copied into fruits on line 2, but there is no\n","# permanent tie between the variables.\n","\n","random_veggie = ['brussel sprouts']\n","veggies = ['broccoli', 'green beans', random_veggie]\n","print(veggies)\n","# There is a subtle difference with lines 1-3 here:\n","# the last element of veggies is not a string, but\n","# a nested list with a single string as element.\n","\n","random_veggie.append('kale')\n","print(veggies)\n","# BOOM! Somehow, veggies changed along with the content\n","# of the random_veggie list. How can that be?\n","# The reason is that lists are values that can change\n","# by themselves. If we had reassigned random_veggie with\n","# a new value, similar to lines 6-7, we would not have\n","# seen it in veggies:\n","\n","random_veggie = 'pumpkin'\n","print(veggies)"],"metadata":{"id":"u_RWc8wBWgMT"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["## Exercise 4.2: Bonus\n","\n","Below we introduce another parameter in the list slice. Try to explain what it does."],"metadata":{"id":"3BfUO-jKS_u1"}},{"cell_type":"code","source":["countries = ['japan', 'hungary', 'maldives', 'gabon', 'bhutan']\n","\n","print(countries[0:5:1])\n","# This does not behave differently from countries[0:5].\n","# From this, you could deduce that the third parameter is 1\n","# by default.\n","\n","print(countries[0:5:2])\n","# This skips every second country.\n","\n","print(countries[-1::-1])\n","# This prints countries in reverse.\n","\n","print(countries[-1::-2])\n","# This prints countries in reverse, skipping every second element.\n","\n","# Conclusion: the third parameter sets the step distance, which\n","# defaults to 1. If you give Python a slice [BEGIN:END:STEP], it\n","# visits the indices as follows:\n","# 1. Set the index to BEGIN.\n","# 2. If (STEP < 0 and index not after END) \n","# or (STEP >= 0 and index not before END): stop.\n","# 3. Visit the index in the given list or other iterable.\n","# 4. Set the index to index + STEP.\n","# 5. Repeat from step 2."],"metadata":{"id":"Y9oxyQb7TIPI"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["The piece of code below is supposed to recognize \"fancy\" words: words that are longer than 5 characters, contain at least one copy of the letter 'a' and start with an uppercase. However, the code is broken. It does not recognize any of our fancy example words.\n","\n","1. Change the value of `word` into each of the examples in the comments on the first two lines and then run the code. See for yourself that the code considers none of the example words fancy. Try some other words as well.\n","3. Try to understand why the code is giving the wrong result. Can you come up with a word that the code does consider fancy?\n","4. Repair the code so that it gives the right result for all examples, and any other words that you come up with."],"metadata":{"id":"Mb6CvHt3CaA0"}},{"cell_type":"markdown","source":["> In step 2, you should find two reasons for failure which we highlight in code comments below. Once you understand these reasons, you should be able to construct words that the code does consider fancy: they must have length greater than 5, an uppercase letter in the *second* position, and a *lowercase* `'a'`. For example `'aRthur'`.\n","\n","> This exercise holds an important lesson: you need to fully understand why code is broken, before you can fix it!"],"metadata":{"id":"n8q0tzvRpzWz"}},{"cell_type":"code","source":["# fancy: Alhambra, Arthur, Jasmine, Turandot\n","# not so fancy: Jeep, paper, Python, Ada\n","word = 'Alhambra'\n","\n","# The first subcondition was entirely correct.\n","lengthy = len(word) > 5\n","\n","# The second subcondition did not take into account\n","# that the 'a' might be uppercase.\n","has_a = 'a' in word\n","# Fix for this subcondition:\n","has_a = 'a' in word or 'A' in word\n","\n","# The third subcondition used the wrong index: 1 instead of 0.\n","first_uppercase = 'A' <= word[1] <= 'Z'\n","# Fix for this subcondition:\n","first_uppercase = 'A' <= word[0] <= 'Z'\n","\n","# The condition as a whole was formulated correctly.\n","if lengthy and has_a and first_uppercase:\n"," print('The word is fancy')\n","else:\n"," print('The word is not so fancy')"],"metadata":{"id":"QQyGzsqCCe3o"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["## Next module\n","\n","[5. Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM) - [solutions](https://colab.research.google.com/drive/1tqjXsHnnJeEAivrxqRoOSW9eNESCwLIK)"],"metadata":{"id":"HiEWGB1V1W4U"}}]} \ No newline at end of file +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "fqMJHzNk5yXQ" + }, + "source": [ + "# Module 4: Datastructures\n", + "\n", + "### Exercise solutions\n", + "\n", + "[Module 4](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF)\n", + "\n", + "### CDH course \"Programming in Python\"\n", + "\n", + "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "\n", + "Previous module: [3. Conditionals](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw) - [solutions](https://colab.research.google.com/drive/1Nvvjc3fGnMg2tWvfw2W1gVn2oKKbWzGI)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "70aMsClGPRy9" + }, + "source": [ + "## Exercise 4.1: Lists\n", + "\n", + "1. For each of the `print` statements below, what do you expect is printed? Run the lines to check predictions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 8, + "status": "ok", + "timestamp": 1680783691341, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "KMUxwcSqPlU1", + "outputId": "523705a3-8ae0-47ab-ace7-e48ed326a404" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "japan\n", + "maldives\n", + "['japan', 'maldives', 'gabon']\n", + "['japan', 'hungary', 'maldives', 'gabon', 'bhutan', 'mexico', 'haiti']\n", + "['japan', 'hungary', 'maldives', 'gabon', 'bhutan', ['mexico', 'haiti']]\n" + ] + } + ], + "source": [ + "countries = ['japan', 'hungary', 'maldives', 'gabon', 'bhutan']\n", + "\n", + "print(countries[0])\n", + "# Index 0 is the FIRST element of the list, so 'japan'.\n", + "\n", + "print(countries[-3])\n", + "# Third element from the end (where 'bhutan' is first),\n", + "# so 'maldives'.\n", + "\n", + "print(countries[0:1] + countries[2:4])\n", + "# In a slice, the first index is inclusive while the last\n", + "# index is exclusive. Hence, we have two sublists:\n", + "# 1. starting at the first element, up to but not including the second\n", + "# ['japan']\n", + "# 2. starting at the third element, up to but not including the fifth\n", + "# ['maldives', 'gabon']\n", + "# and then we concatenate those to ['japan', 'maldives', 'gabon'].\n", + "\n", + "more_countries = countries + ['mexico', 'haiti']\n", + "print(more_countries)\n", + "# By concatenating two lists, we create a new, longer list,\n", + "# with all elements of the original lists at the same level.\n", + "\n", + "countries.append(['mexico', 'haiti'])\n", + "print(countries)\n", + "# Appending a list to another list makes the former\n", + "# a nested single element of the latter." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "TyebsOIpU6hv" + }, + "source": [ + "2. Transform the list below into `['jasmin', 'john', 'ravi']` in one line of code. \n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "H8o6vsHKVKoq" + }, + "outputs": [], + "source": [ + "students = ['jasmin', 'ravi', 'john']\n", + "\n", + "# Option 1: modify students in place by\n", + "# reassinging a slice of it.\n", + "students[1:3] = [students[2], students[1]]\n", + "\n", + "# Option 2: create a new list based on students.\n", + "new_students = students[:1] + students[2:] + students[1:2]\n", + "\n", + "# If you figured out stepped slices in the bonus exercise,\n", + "# you have a few more interesting options:\n", + "\n", + "# Variant of option 1\n", + "students[1:3] = students[:-3:-1]\n", + "\n", + "# Variant of option 2\n", + "new_students = students[:1] + students[:-3:-1]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HMU5X7XFWbCw" + }, + "source": [ + "3. For each of the print statements below, what do you expect is printed? Run the lines to check predictions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "u_RWc8wBWgMT" + }, + "outputs": [], + "source": [ + "random_fruit = 'pineapple'\n", + "fruits = ['apple', 'pear', random_fruit]\n", + "print(fruits)\n", + "# ['apple', 'pear', 'pineapple']\n", + "\n", + "random_fruit = 'blueberry'\n", + "print(fruits)\n", + "# Still the same. The value of random_fruit was\n", + "# copied into fruits on line 2, but there is no\n", + "# permanent tie between the variables.\n", + "\n", + "random_veggie = ['brussel sprouts']\n", + "veggies = ['broccoli', 'green beans', random_veggie]\n", + "print(veggies)\n", + "# There is a subtle difference with lines 1-3 here:\n", + "# the last element of veggies is not a string, but\n", + "# a nested list with a single string as element.\n", + "\n", + "random_veggie.append('kale')\n", + "print(veggies)\n", + "# BOOM! Somehow, veggies changed along with the content\n", + "# of the random_veggie list. How can that be?\n", + "# The reason is that lists are values that can change\n", + "# by themselves. If we had reassigned random_veggie with\n", + "# a new value, similar to lines 6-7, we would not have\n", + "# seen it in veggies:\n", + "\n", + "random_veggie = 'pumpkin'\n", + "print(veggies)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3BfUO-jKS_u1" + }, + "source": [ + "## Exercise 4.2: Bonus\n", + "\n", + "Below we introduce another parameter in the list slice. Try to explain what it does." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Y9oxyQb7TIPI" + }, + "outputs": [], + "source": [ + "countries = ['japan', 'hungary', 'maldives', 'gabon', 'bhutan']\n", + "\n", + "print(countries[0:5:1])\n", + "# This does not behave differently from countries[0:5].\n", + "# From this, you could deduce that the third parameter is 1\n", + "# by default.\n", + "\n", + "print(countries[0:5:2])\n", + "# This skips every second country.\n", + "\n", + "print(countries[-1::-1])\n", + "# This prints countries in reverse.\n", + "\n", + "print(countries[-1::-2])\n", + "# This prints countries in reverse, skipping every second element.\n", + "\n", + "# Conclusion: the third parameter sets the step distance, which\n", + "# defaults to 1. If you give Python a slice [BEGIN:END:STEP], it\n", + "# visits the indices as follows:\n", + "# 1. Set the index to BEGIN.\n", + "# 2. If (STEP < 0 and index not after END) \n", + "# or (STEP >= 0 and index not before END): stop.\n", + "# 3. Visit the index in the given list or other iterable.\n", + "# 4. Set the index to index + STEP.\n", + "# 5. Repeat from step 2." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Mb6CvHt3CaA0" + }, + "source": [ + "The piece of code below is supposed to recognize \"fancy\" words: words that are longer than 5 characters, contain at least one copy of the letter 'a' and start with an uppercase. However, the code is broken. It does not recognize any of our fancy example words.\n", + "\n", + "1. Change the value of `word` into each of the examples in the comments on the first two lines and then run the code. See for yourself that the code considers none of the example words fancy. Try some other words as well.\n", + "3. Try to understand why the code is giving the wrong result. Can you come up with a word that the code does consider fancy?\n", + "4. Repair the code so that it gives the right result for all examples, and any other words that you come up with." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "n8q0tzvRpzWz" + }, + "source": [ + "> In step 2, you should find two reasons for failure which we highlight in code comments below. Once you understand these reasons, you should be able to construct words that the code does consider fancy: they must have length greater than 5, an uppercase letter in the *second* position, and a *lowercase* `'a'`. For example `'aRthur'`.\n", + "\n", + "> This exercise holds an important lesson: you need to fully understand why code is broken, before you can fix it!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "QQyGzsqCCe3o" + }, + "outputs": [], + "source": [ + "# fancy: Alhambra, Arthur, Jasmine, Turandot\n", + "# not so fancy: Jeep, paper, Python, Ada\n", + "word = 'Alhambra'\n", + "\n", + "# The first subcondition was entirely correct.\n", + "lengthy = len(word) > 5\n", + "\n", + "# The second subcondition did not take into account\n", + "# that the 'a' might be uppercase.\n", + "has_a = 'a' in word\n", + "# Fix for this subcondition:\n", + "has_a = 'a' in word or 'A' in word\n", + "\n", + "# The third subcondition used the wrong index: 1 instead of 0.\n", + "first_uppercase = 'A' <= word[1] <= 'Z'\n", + "# Fix for this subcondition:\n", + "first_uppercase = 'A' <= word[0] <= 'Z'\n", + "\n", + "# The condition as a whole was formulated correctly.\n", + "if lengthy and has_a and first_uppercase:\n", + " print('The word is fancy')\n", + "else:\n", + " print('The word is not so fancy')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HiEWGB1V1W4U" + }, + "source": [ + "## Next module\n", + "\n", + "[5. Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM) - [solutions](https://colab.research.google.com/drive/1tqjXsHnnJeEAivrxqRoOSW9eNESCwLIK)" + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyOIQlgg0FQF3naGWr9eJVVL", + "provenance": [], + "toc_visible": true + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/solutions/04 datastructures solutions.py b/solutions/04 datastructures solutions.py new file mode 100644 index 0000000..cd9d588 --- /dev/null +++ b/solutions/04 datastructures solutions.py @@ -0,0 +1,191 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# name: python3 +# --- + +# %% [markdown] id="fqMJHzNk5yXQ" +# # Module 4: Datastructures +# +# ### Exercise solutions +# +# [Module 4](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF) +# +# ### CDH course "Programming in Python" +# +# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# +# Previous module: [3. Conditionals](https://colab.research.google.com/drive/1KkZ2fS75o7giccQJakRhyvYBV7JdFnVw) - [solutions](https://colab.research.google.com/drive/1Nvvjc3fGnMg2tWvfw2W1gVn2oKKbWzGI) + +# %% [markdown] id="70aMsClGPRy9" +# ## Exercise 4.1: Lists +# +# 1. For each of the `print` statements below, what do you expect is printed? Run the lines to check predictions + +# %% id="KMUxwcSqPlU1" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1680783691341, "user_tz": -120, "elapsed": 8, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="523705a3-8ae0-47ab-ace7-e48ed326a404" +countries = ['japan', 'hungary', 'maldives', 'gabon', 'bhutan'] + +print(countries[0]) +# Index 0 is the FIRST element of the list, so 'japan'. + +print(countries[-3]) +# Third element from the end (where 'bhutan' is first), +# so 'maldives'. + +print(countries[0:1] + countries[2:4]) +# In a slice, the first index is inclusive while the last +# index is exclusive. Hence, we have two sublists: +# 1. starting at the first element, up to but not including the second +# ['japan'] +# 2. starting at the third element, up to but not including the fifth +# ['maldives', 'gabon'] +# and then we concatenate those to ['japan', 'maldives', 'gabon']. + +more_countries = countries + ['mexico', 'haiti'] +print(more_countries) +# By concatenating two lists, we create a new, longer list, +# with all elements of the original lists at the same level. + +countries.append(['mexico', 'haiti']) +print(countries) +# Appending a list to another list makes the former +# a nested single element of the latter. + +# %% [markdown] id="TyebsOIpU6hv" +# 2. Transform the list below into `['jasmin', 'john', 'ravi']` in one line of code. +# +# + +# %% id="H8o6vsHKVKoq" +students = ['jasmin', 'ravi', 'john'] + +# Option 1: modify students in place by +# reassinging a slice of it. +students[1:3] = [students[2], students[1]] + +# Option 2: create a new list based on students. +new_students = students[:1] + students[2:] + students[1:2] + +# If you figured out stepped slices in the bonus exercise, +# you have a few more interesting options: + +# Variant of option 1 +students[1:3] = students[:-3:-1] + +# Variant of option 2 +new_students = students[:1] + students[:-3:-1] + +# %% [markdown] id="HMU5X7XFWbCw" +# 3. For each of the print statements below, what do you expect is printed? Run the lines to check predictions. + +# %% id="u_RWc8wBWgMT" +random_fruit = 'pineapple' +fruits = ['apple', 'pear', random_fruit] +print(fruits) +# ['apple', 'pear', 'pineapple'] + +random_fruit = 'blueberry' +print(fruits) +# Still the same. The value of random_fruit was +# copied into fruits on line 2, but there is no +# permanent tie between the variables. + +random_veggie = ['brussel sprouts'] +veggies = ['broccoli', 'green beans', random_veggie] +print(veggies) +# There is a subtle difference with lines 1-3 here: +# the last element of veggies is not a string, but +# a nested list with a single string as element. + +random_veggie.append('kale') +print(veggies) +# BOOM! Somehow, veggies changed along with the content +# of the random_veggie list. How can that be? +# The reason is that lists are values that can change +# by themselves. If we had reassigned random_veggie with +# a new value, similar to lines 6-7, we would not have +# seen it in veggies: + +random_veggie = 'pumpkin' +print(veggies) + +# %% [markdown] id="3BfUO-jKS_u1" +# ## Exercise 4.2: Bonus +# +# Below we introduce another parameter in the list slice. Try to explain what it does. + +# %% id="Y9oxyQb7TIPI" +countries = ['japan', 'hungary', 'maldives', 'gabon', 'bhutan'] + +print(countries[0:5:1]) +# This does not behave differently from countries[0:5]. +# From this, you could deduce that the third parameter is 1 +# by default. + +print(countries[0:5:2]) +# This skips every second country. + +print(countries[-1::-1]) +# This prints countries in reverse. + +print(countries[-1::-2]) +# This prints countries in reverse, skipping every second element. + +# Conclusion: the third parameter sets the step distance, which +# defaults to 1. If you give Python a slice [BEGIN:END:STEP], it +# visits the indices as follows: +# 1. Set the index to BEGIN. +# 2. If (STEP < 0 and index not after END) +# or (STEP >= 0 and index not before END): stop. +# 3. Visit the index in the given list or other iterable. +# 4. Set the index to index + STEP. +# 5. Repeat from step 2. + +# %% [markdown] id="Mb6CvHt3CaA0" +# The piece of code below is supposed to recognize "fancy" words: words that are longer than 5 characters, contain at least one copy of the letter 'a' and start with an uppercase. However, the code is broken. It does not recognize any of our fancy example words. +# +# 1. Change the value of `word` into each of the examples in the comments on the first two lines and then run the code. See for yourself that the code considers none of the example words fancy. Try some other words as well. +# 3. Try to understand why the code is giving the wrong result. Can you come up with a word that the code does consider fancy? +# 4. Repair the code so that it gives the right result for all examples, and any other words that you come up with. + +# %% [markdown] id="n8q0tzvRpzWz" +# > In step 2, you should find two reasons for failure which we highlight in code comments below. Once you understand these reasons, you should be able to construct words that the code does consider fancy: they must have length greater than 5, an uppercase letter in the *second* position, and a *lowercase* `'a'`. For example `'aRthur'`. +# +# > This exercise holds an important lesson: you need to fully understand why code is broken, before you can fix it! + +# %% id="QQyGzsqCCe3o" +# fancy: Alhambra, Arthur, Jasmine, Turandot +# not so fancy: Jeep, paper, Python, Ada +word = 'Alhambra' + +# The first subcondition was entirely correct. +lengthy = len(word) > 5 + +# The second subcondition did not take into account +# that the 'a' might be uppercase. +has_a = 'a' in word +# Fix for this subcondition: +has_a = 'a' in word or 'A' in word + +# The third subcondition used the wrong index: 1 instead of 0. +first_uppercase = 'A' <= word[1] <= 'Z' +# Fix for this subcondition: +first_uppercase = 'A' <= word[0] <= 'Z' + +# The condition as a whole was formulated correctly. +if lengthy and has_a and first_uppercase: + print('The word is fancy') +else: + print('The word is not so fancy') + +# %% [markdown] id="HiEWGB1V1W4U" +# ## Next module +# +# [5. Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM) - [solutions](https://colab.research.google.com/drive/1tqjXsHnnJeEAivrxqRoOSW9eNESCwLIK) diff --git a/solutions/05 assertions solutions.ipynb b/solutions/05 assertions solutions.ipynb index 92f27ab..dc73f1f 100644 --- a/solutions/05 assertions solutions.ipynb +++ b/solutions/05 assertions solutions.ipynb @@ -1 +1,354 @@ -{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"provenance":[{"file_id":"1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM","timestamp":1681202721640}],"toc_visible":true},"kernelspec":{"name":"python3","display_name":"Python 3"},"language_info":{"name":"python"}},"cells":[{"cell_type":"markdown","source":["# Module 5: Assertions\n","\n","### Exercise solutions\n","\n","[5. Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM)\n","\n","### CDH course \"Programming in Python\"\n","\n","[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n","\n","Previous module: [4. Datastructures](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF) - [solutions](https://colab.research.google.com/drive/1juOQFMlRmeVUfbKWy0wtZ9lWkSZmr2Gn)"],"metadata":{"id":"fqMJHzNk5yXQ"}},{"cell_type":"markdown","source":["## Exercise 5.1: Assertions\n","In each of the code blocks below, try to predict what will be the output, then run the code. If your guess was incorrect, try to figure out why the result is different. If your guess was correct, celebrate!"],"metadata":{"id":"m_FYfvXbbZXe"}},{"cell_type":"code","source":["assert True\n","# The assertion passes, so no output!"],"metadata":{"id":"ztDylwg9biL5","executionInfo":{"status":"ok","timestamp":1681309651463,"user_tz":-120,"elapsed":4,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}}},"execution_count":2,"outputs":[]},{"cell_type":"code","source":["assert False, \"The assertion fails because the value is False\""],"metadata":{"id":"0Uk4w2DBbxfD","colab":{"base_uri":"https://localhost:8080/","height":164},"executionInfo":{"status":"error","timestamp":1681309645640,"user_tz":-120,"elapsed":45,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"fd5ed9ba-c7ea-488a-b537-a6641517d2c0"},"execution_count":1,"outputs":[{"output_type":"error","ename":"AssertionError","evalue":"ignored","traceback":["\u001b[0;31m---------------------------------------------------------------------------\u001b[0m","\u001b[0;31mAssertionError\u001b[0m Traceback (most recent call last)","\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32massert\u001b[0m \u001b[0;32mFalse\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"The assertion fails because the value is False\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m","\u001b[0;31mAssertionError\u001b[0m: The assertion fails because the value is False"]}]},{"cell_type":"code","source":["assert \"True\"\n","\n","# You can think of assert as implicitly wrapping its argument in bool():\n","assert bool(\"True\")\n","# bool() converts anything that looks like \"empty\" or \"nothing\",\n","# such as the integer 0 (zero), None, the empty string and the empty list,\n","# to False, and everything else to True.\n","# Hence, the string \"True\" is converted to the boolean True, the assertion\n","# passes and there is no output."],"metadata":{"id":"orOWCpWVbzKf","executionInfo":{"status":"ok","timestamp":1681309654788,"user_tz":-120,"elapsed":874,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}}},"execution_count":3,"outputs":[]},{"cell_type":"code","source":["assert \"False\", \"The assertion fails because the value is False\"\n","# The string \"False\" is nonempty, so bool() converts it to True\n","# (which, yes, is very confusing!). Hence, the assertion passes\n","# and we see no output.\n","\n","# For comparison, the empty string results in a failing assertion:\n","assert bool(\"\"), \"The assertion fails because the value is False\"\n","# while a string with just a space passes:\n","assert bool(\" \"), \"The assertion fails because the value is False\""],"metadata":{"id":"F6NjZ7gOb05u","executionInfo":{"status":"ok","timestamp":1681310066688,"user_tz":-120,"elapsed":186,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}}},"execution_count":4,"outputs":[]},{"cell_type":"code","source":["assert 1\n","# bool(1) is True, so pass, so no output"],"metadata":{"id":"KB_YkNSIb2KT","executionInfo":{"status":"ok","timestamp":1681310068748,"user_tz":-120,"elapsed":309,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}}},"execution_count":5,"outputs":[]},{"cell_type":"code","source":["assert 1 == True, \"The number 1 is not True\"\n","# As we saw in module 2, 1 is indeed the same value as True"],"metadata":{"id":"1iUK81Nvb3Ri","executionInfo":{"status":"ok","timestamp":1681310100358,"user_tz":-120,"elapsed":191,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}}},"execution_count":7,"outputs":[]},{"cell_type":"code","source":["assert 0\n","# bool(0) is False, so fail, so we see an AssertionError.\n","# The AssertionError lacks a description because we did not\n","# provide one."],"metadata":{"id":"Tje6e-Jgb4rn","colab":{"base_uri":"https://localhost:8080/","height":164},"executionInfo":{"status":"error","timestamp":1681310102072,"user_tz":-120,"elapsed":10,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"2ffb1ccc-c2ce-4625-baea-c02403cdf947"},"execution_count":8,"outputs":[{"output_type":"error","ename":"AssertionError","evalue":"ignored","traceback":["\u001b[0;31m---------------------------------------------------------------------------\u001b[0m","\u001b[0;31mAssertionError\u001b[0m Traceback (most recent call last)","\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32massert\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m","\u001b[0;31mAssertionError\u001b[0m: "]}]},{"cell_type":"markdown","source":["## Exercise 5.2: Bonus - Test-driven development"],"metadata":{"id":"xgqh3r7Bcj_F"}},{"cell_type":"markdown","source":["In the code block below, change the value for `b`, and create an expression for `c` using `a` and `b` so that the assert statements succeed.\n"],"metadata":{"id":"TMWSMWg7dQqB"}},{"cell_type":"code","source":["# One possible solution\n","a = 12\n","b = 30\n","\n","c = b - a\n","\n","# Another possible solution\n","a = 12\n","b = 24\n","\n","c = (a + b) / 2\n","\n","assert a < b, 'a should be smaller than b'\n","assert a != b, 'a and b should not be equal'\n","assert c == 18, 'c should be 18'"],"metadata":{"id":"Q_yIUKSRdVjF","executionInfo":{"status":"ok","timestamp":1681310291983,"user_tz":-120,"elapsed":195,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}}},"execution_count":13,"outputs":[]},{"cell_type":"markdown","source":["In the code block below, change `students` so that it satisfies the `assert` statements. Also, add messages to the assert statements that explain what they test (in human language)."],"metadata":{"id":"1u_bBUpSfQr5"}},{"cell_type":"code","source":["students = ['bert', 'ernie', 'pino']\n","\n","\n","assert len(students) == 3, 'We need three student names'\n","assert students[0] < students[1] < students[2], 'The students must be sorted'"],"metadata":{"id":"UOp8NFVOfR6Z","executionInfo":{"status":"ok","timestamp":1681310446308,"user_tz":-120,"elapsed":428,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}}},"execution_count":14,"outputs":[]},{"cell_type":"markdown","source":["## Next module\n","\n","[6. Loops](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV) - [solutions](https://colab.research.google.com/drive/1vy2gKHtBUMk60u2bmCYlcqmhIFpI2PI-)"],"metadata":{"id":"JaaguG-D3k_i"}}]} \ No newline at end of file +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "fqMJHzNk5yXQ" + }, + "source": [ + "# Module 5: Assertions\n", + "\n", + "### Exercise solutions\n", + "\n", + "[5. Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM)\n", + "\n", + "### CDH course \"Programming in Python\"\n", + "\n", + "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "\n", + "Previous module: [4. Datastructures](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF) - [solutions](https://colab.research.google.com/drive/1juOQFMlRmeVUfbKWy0wtZ9lWkSZmr2Gn)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "m_FYfvXbbZXe" + }, + "source": [ + "## Exercise 5.1: Assertions\n", + "In each of the code blocks below, try to predict what will be the output, then run the code. If your guess was incorrect, try to figure out why the result is different. If your guess was correct, celebrate!" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "executionInfo": { + "elapsed": 4, + "status": "ok", + "timestamp": 1681309651463, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "ztDylwg9biL5" + }, + "outputs": [], + "source": [ + "assert True\n", + "# The assertion passes, so no output!" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 164 + }, + "executionInfo": { + "elapsed": 45, + "status": "error", + "timestamp": 1681309645640, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "0Uk4w2DBbxfD", + "outputId": "fd5ed9ba-c7ea-488a-b537-a6641517d2c0" + }, + "outputs": [ + { + "ename": "AssertionError", + "evalue": "ignored", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAssertionError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32massert\u001b[0m \u001b[0;32mFalse\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"The assertion fails because the value is False\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mAssertionError\u001b[0m: The assertion fails because the value is False" + ] + } + ], + "source": [ + "assert False, \"The assertion fails because the value is False\"" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "executionInfo": { + "elapsed": 874, + "status": "ok", + "timestamp": 1681309654788, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "orOWCpWVbzKf" + }, + "outputs": [], + "source": [ + "assert \"True\"\n", + "\n", + "# You can think of assert as implicitly wrapping its argument in bool():\n", + "assert bool(\"True\")\n", + "# bool() converts anything that looks like \"empty\" or \"nothing\",\n", + "# such as the integer 0 (zero), None, the empty string and the empty list,\n", + "# to False, and everything else to True.\n", + "# Hence, the string \"True\" is converted to the boolean True, the assertion\n", + "# passes and there is no output." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "executionInfo": { + "elapsed": 186, + "status": "ok", + "timestamp": 1681310066688, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "F6NjZ7gOb05u" + }, + "outputs": [], + "source": [ + "assert \"False\", \"The assertion fails because the value is False\"\n", + "# The string \"False\" is nonempty, so bool() converts it to True\n", + "# (which, yes, is very confusing!). Hence, the assertion passes\n", + "# and we see no output.\n", + "\n", + "# For comparison, the empty string results in a failing assertion:\n", + "assert bool(\"\"), \"The assertion fails because the value is False\"\n", + "# while a string with just a space passes:\n", + "assert bool(\" \"), \"The assertion fails because the value is False\"" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "executionInfo": { + "elapsed": 309, + "status": "ok", + "timestamp": 1681310068748, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "KB_YkNSIb2KT" + }, + "outputs": [], + "source": [ + "assert 1\n", + "# bool(1) is True, so pass, so no output" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "executionInfo": { + "elapsed": 191, + "status": "ok", + "timestamp": 1681310100358, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "1iUK81Nvb3Ri" + }, + "outputs": [], + "source": [ + "assert 1 == True, \"The number 1 is not True\"\n", + "# As we saw in module 2, 1 is indeed the same value as True" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 164 + }, + "executionInfo": { + "elapsed": 10, + "status": "error", + "timestamp": 1681310102072, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "Tje6e-Jgb4rn", + "outputId": "2ffb1ccc-c2ce-4625-baea-c02403cdf947" + }, + "outputs": [ + { + "ename": "AssertionError", + "evalue": "ignored", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAssertionError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32massert\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mAssertionError\u001b[0m: " + ] + } + ], + "source": [ + "assert 0\n", + "# bool(0) is False, so fail, so we see an AssertionError.\n", + "# The AssertionError lacks a description because we did not\n", + "# provide one." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xgqh3r7Bcj_F" + }, + "source": [ + "## Exercise 5.2: Bonus - Test-driven development" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "TMWSMWg7dQqB" + }, + "source": [ + "In the code block below, change the value for `b`, and create an expression for `c` using `a` and `b` so that the assert statements succeed.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "executionInfo": { + "elapsed": 195, + "status": "ok", + "timestamp": 1681310291983, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "Q_yIUKSRdVjF" + }, + "outputs": [], + "source": [ + "# One possible solution\n", + "a = 12\n", + "b = 30\n", + "\n", + "c = b - a\n", + "\n", + "# Another possible solution\n", + "a = 12\n", + "b = 24\n", + "\n", + "c = (a + b) / 2\n", + "\n", + "assert a < b, 'a should be smaller than b'\n", + "assert a != b, 'a and b should not be equal'\n", + "assert c == 18, 'c should be 18'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1u_bBUpSfQr5" + }, + "source": [ + "In the code block below, change `students` so that it satisfies the `assert` statements. Also, add messages to the assert statements that explain what they test (in human language)." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "executionInfo": { + "elapsed": 428, + "status": "ok", + "timestamp": 1681310446308, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "UOp8NFVOfR6Z" + }, + "outputs": [], + "source": [ + "students = ['bert', 'ernie', 'pino']\n", + "\n", + "\n", + "assert len(students) == 3, 'We need three student names'\n", + "assert students[0] < students[1] < students[2], 'The students must be sorted'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "JaaguG-D3k_i" + }, + "source": [ + "## Next module\n", + "\n", + "[6. Loops](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV) - [solutions](https://colab.research.google.com/drive/1vy2gKHtBUMk60u2bmCYlcqmhIFpI2PI-)" + ] + } + ], + "metadata": { + "colab": { + "provenance": [ + { + "file_id": "1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM", + "timestamp": 1681202721640 + } + ], + "toc_visible": true + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/solutions/05 assertions solutions.py b/solutions/05 assertions solutions.py new file mode 100644 index 0000000..bd801e0 --- /dev/null +++ b/solutions/05 assertions solutions.py @@ -0,0 +1,111 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# name: python3 +# --- + +# %% [markdown] id="fqMJHzNk5yXQ" +# # Module 5: Assertions +# +# ### Exercise solutions +# +# [5. Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM) +# +# ### CDH course "Programming in Python" +# +# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# +# Previous module: [4. Datastructures](https://colab.research.google.com/drive/1JxzmIzwcipnwFBntv0WZOlT-d2fUjoRF) - [solutions](https://colab.research.google.com/drive/1juOQFMlRmeVUfbKWy0wtZ9lWkSZmr2Gn) + +# %% [markdown] id="m_FYfvXbbZXe" +# ## Exercise 5.1: Assertions +# In each of the code blocks below, try to predict what will be the output, then run the code. If your guess was incorrect, try to figure out why the result is different. If your guess was correct, celebrate! + +# %% id="ztDylwg9biL5" executionInfo={"status": "ok", "timestamp": 1681309651463, "user_tz": -120, "elapsed": 4, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} +assert True +# The assertion passes, so no output! + +# %% id="0Uk4w2DBbxfD" colab={"base_uri": "https://localhost:8080/", "height": 164} executionInfo={"status": "error", "timestamp": 1681309645640, "user_tz": -120, "elapsed": 45, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="fd5ed9ba-c7ea-488a-b537-a6641517d2c0" +assert False, "The assertion fails because the value is False" + +# %% id="orOWCpWVbzKf" executionInfo={"status": "ok", "timestamp": 1681309654788, "user_tz": -120, "elapsed": 874, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} +assert "True" + +# You can think of assert as implicitly wrapping its argument in bool(): +assert bool("True") +# bool() converts anything that looks like "empty" or "nothing", +# such as the integer 0 (zero), None, the empty string and the empty list, +# to False, and everything else to True. +# Hence, the string "True" is converted to the boolean True, the assertion +# passes and there is no output. + +# %% id="F6NjZ7gOb05u" executionInfo={"status": "ok", "timestamp": 1681310066688, "user_tz": -120, "elapsed": 186, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} +assert "False", "The assertion fails because the value is False" +# The string "False" is nonempty, so bool() converts it to True +# (which, yes, is very confusing!). Hence, the assertion passes +# and we see no output. + +# For comparison, the empty string results in a failing assertion: +assert bool(""), "The assertion fails because the value is False" +# while a string with just a space passes: +assert bool(" "), "The assertion fails because the value is False" + +# %% id="KB_YkNSIb2KT" executionInfo={"status": "ok", "timestamp": 1681310068748, "user_tz": -120, "elapsed": 309, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} +assert 1 +# bool(1) is True, so pass, so no output + +# %% id="1iUK81Nvb3Ri" executionInfo={"status": "ok", "timestamp": 1681310100358, "user_tz": -120, "elapsed": 191, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} +assert 1 == True, "The number 1 is not True" +# As we saw in module 2, 1 is indeed the same value as True + +# %% id="Tje6e-Jgb4rn" colab={"base_uri": "https://localhost:8080/", "height": 164} executionInfo={"status": "error", "timestamp": 1681310102072, "user_tz": -120, "elapsed": 10, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="2ffb1ccc-c2ce-4625-baea-c02403cdf947" +assert 0 +# bool(0) is False, so fail, so we see an AssertionError. +# The AssertionError lacks a description because we did not +# provide one. + +# %% [markdown] id="xgqh3r7Bcj_F" +# ## Exercise 5.2: Bonus - Test-driven development + +# %% [markdown] id="TMWSMWg7dQqB" +# In the code block below, change the value for `b`, and create an expression for `c` using `a` and `b` so that the assert statements succeed. +# + +# %% id="Q_yIUKSRdVjF" executionInfo={"status": "ok", "timestamp": 1681310291983, "user_tz": -120, "elapsed": 195, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} +# One possible solution +a = 12 +b = 30 + +c = b - a + +# Another possible solution +a = 12 +b = 24 + +c = (a + b) / 2 + +assert a < b, 'a should be smaller than b' +assert a != b, 'a and b should not be equal' +assert c == 18, 'c should be 18' + +# %% [markdown] id="1u_bBUpSfQr5" +# In the code block below, change `students` so that it satisfies the `assert` statements. Also, add messages to the assert statements that explain what they test (in human language). + +# %% id="UOp8NFVOfR6Z" executionInfo={"status": "ok", "timestamp": 1681310446308, "user_tz": -120, "elapsed": 428, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} +students = ['bert', 'ernie', 'pino'] + + +assert len(students) == 3, 'We need three student names' +assert students[0] < students[1] < students[2], 'The students must be sorted' + +# %% [markdown] id="JaaguG-D3k_i" +# ## Next module +# +# [6. Loops](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV) - [solutions](https://colab.research.google.com/drive/1vy2gKHtBUMk60u2bmCYlcqmhIFpI2PI-) diff --git a/solutions/06 Loops - Solutions.ipynb b/solutions/06 Loops - Solutions.ipynb index e09e500..a20476b 100644 --- a/solutions/06 Loops - Solutions.ipynb +++ b/solutions/06 Loops - Solutions.ipynb @@ -1 +1,461 @@ -{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"provenance":[],"toc_visible":true},"kernelspec":{"name":"python3","display_name":"Python 3"},"language_info":{"name":"python"}},"cells":[{"cell_type":"markdown","source":["# CDH course \"Programming in Python\"\n","\n","### Exercise Solutions\n","[Solutions](https://colab.research.google.com/drive/1vy2gKHtBUMk60u2bmCYlcqmhIFpI2PI-#scrollTo=fqMJHzNk5yXQ)\n","\n","### CDH course \"Programming in Python\"\n","\n","[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n","\n","Previous module: [5. Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM?usp=share_link) - [solutions](https://colab.research.google.com/drive/1tqjXsHnnJeEAivrxqRoOSW9eNESCwLIK)"],"metadata":{"id":"fqMJHzNk5yXQ"}},{"cell_type":"markdown","source":["## Exercise 1: basic `for` loops\n","\n","1. Make a small change to the following code block (copied from above), so that it prints the `full_text` only at the end, when it is complete."],"metadata":{"id":"0Gun_3cX1ey8"}},{"cell_type":"code","source":["words = ['A', 'very', 'short', 'sentence']\n","full_text = ''\n","\n","for word in words:\n"," full_text = full_text + word + ' '\n"," # print(word)\n","print(full_text)"],"metadata":{"id":"nMF8WE3F19HC","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1681144232455,"user_tz":-120,"elapsed":5,"user":{"displayName":"Mees van Stiphout","userId":"10520931415894572279"}},"outputId":"7e9843ae-f3d5-43f1-b0a4-64a3c2c309c7"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["A\n","very\n","short\n","sentence\n","A very short sentence \n"]}]},{"cell_type":"markdown","source":["2. Using a `for` loop, create a copy of the list of fruits below that has the fruits in the reverse order. Change the contents of the list on the first line to check that your solution is general."],"metadata":{"id":"8zB10pLC2ZaT"}},{"cell_type":"code","source":["fruits = ['apricot', 'banana', 'cherry', 'date']\n","\n","# insert your code here\n","# SHOULD output ['date', 'cherry', 'banana', 'apricot']\n","\n","#solution 1:\n","reverse_fruits = []\n","for fruit in fruits[-1::-1]:\n"," reverse_fruits.append(fruit)\n","print(reverse_fruits)\n","\n","#solution 2:\n","reverse_fruits = []\n","for fruit in fruits:\n"," reverse_fruits = [fruit] + reverse_fruits\n","print(reverse_fruits)\n","\n","#solution 3:\n","reverse_fruits = []\n","for fruit in fruits:\n"," reverse_fruits[0:0] = [fruit]\n","print(reverse_fruits)\n","\n","#solution 4:\n","reverse_fruits = []\n","position = -1\n","for fruit in fruits:\n"," reverse_fruits.append(fruits[position])\n"," position = position - 1\n","print(reverse_fruits)\n","\n","#solution 5:\n","reverse_fruits = []\n","position = len(fruits) - 1\n","for fruit in fruits:\n"," reverse_fruits.append(fruits[position])\n"," position = position - 1\n","print(reverse_fruits)\n"],"metadata":{"id":"bAwZ_ipU28AY","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1681311205916,"user_tz":-120,"elapsed":208,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"34d5cb4c-d675-413e-adb6-9d3f7bc41908"},"execution_count":2,"outputs":[{"output_type":"stream","name":"stdout","text":["['date', 'cherry', 'banana', 'apricot']\n","['date', 'cherry', 'banana', 'apricot']\n","['date', 'cherry', 'banana', 'apricot']\n","['date', 'cherry', 'banana', 'apricot']\n","['date', 'cherry', 'banana', 'apricot']\n"]}]},{"cell_type":"markdown","source":["## Exercise 2: more loops"],"metadata":{"id":"-K0CvNkOLEYE"}},{"cell_type":"markdown","source":["1. The code block below is written to help you explore what kind of \"stuff\" comes out of `range` and `enumerate`. Read the code from top to bottom. What stands out to you? Why do you think it was written this way?"],"metadata":{"id":"Qj9LsMfw-RMT"}},{"cell_type":"markdown","source":["> Things that you might notice:\n","> \n","> - It is possible to call `range` with one, two or three arguments.\n","> - `enumerate` is always called with only one argument.\n","> - Some noticed that you can write the string `'pirate'` and pass it directly to `enumerate`, without storing the string in a variable first. This is a good observation, and it is possible with all values and functions in general!\n","> - The program is counting iterations. The variable `iteration_count` is initially set to `0`, incremented at every loop iteration and finally printed at the end.\n","> - When the `iteration_count` is greater than `2`, the loop does not print the current `value`. In other words, the program only prints the first two values in each iterable.\n","> - The program prints the type of the first value in the iterable only.\n","> \n","> Reasons:\n","> \n","> - All the various versions of `miracle` are there to illustrate the behavior of `range` and `enumerate`.\n","> - We print the type of the iterable as a whole at the start, so you can see what kind of value comes out of the call to `range` or `enumerate`.\n","> - We keep track of the number of iterations so you can see how many values are produced by the iterable. We could have used `enumerate` for this, but this would be confusing, since the behavior of `enumerate` itself is the topic of this exercise. Therefore, we use custom logic with the `iteration_count` variable instead.\n","> - We only print the first two values in the iterable because this is enough to see a pattern. If we printed all of them, it could potentially produce a lot of output.\n","> - We only print the type of the first value, on the assumption that all values in the iterable have the same type.\n","\n"],"metadata":{"id":"otBq-xEi-SiC"}},{"cell_type":"markdown","source":["2. Run the code block for each example value of `miracle`. Based on the results, describe in your own words what `range` and `enumerate` do."],"metadata":{"id":"EjmxzbeY-USj"}},{"cell_type":"markdown","source":["> Possible elements of a good answer for `range`:\n","> \n","> - If you pass only one value, this sets the exclusive end. It generates whole numbers from `0` (zero) up to but not including the end.\n","> - If you pass two values, the first sets the inclusive start and the second sets the exclusive end.\n","> - If you pass three values, the first sets the inclusive start, the second the exclusive end and the third a step size.\n","> - If you omit the step size (so you pass only one or two arguments), it defaults to `1`.\n","> - A negative step size causes the range to count \"backwards\" (from high numbers to low numbers).\n","> - You are not allowed to pass floats; the function only supports integers.\n","> \n","> Possible elements of a good answer for `enumerate`:\n","> \n","> - It requires another iterable as argument (we see a list, a string and a range in the examples).\n","> - The same values from the input iterable also appear in the output iterable, but wrapped in a tuple.\n","> - In the output iterable, every tuple has a numerical index in addition to the corresponding value from the input iterable.\n","> - The numerical index is the first element of the tuple, while the value is the second element.\n","> - The numerical indices start at `0` (zero).\n"],"metadata":{"id":"ZyllJHKCBtoy"}},{"cell_type":"code","source":["basket = ['apricot', 'banana', 'cherry', 'date']\n","\n","miracle = range(100)\n","# miracle = range(2, 11)\n","# miracle = range(2, 11, 3)\n","# miracle = range(11, 2, -2)\n","# miracle = range(0.1, 1.0, 0.1)\n","# miracle = enumerate(basket)\n","# miracle = enumerate('pirate')\n","# miracle = enumerate(range(10))\n","\n","print('type of miracle:', type(miracle))\n","\n","iteration_count = 0\n","for value in miracle:\n"," iteration_count = iteration_count + 1\n"," if iteration_count > 2:\n"," continue\n"," elif iteration_count == 1:\n"," print('type of first iteration:', type(value))\n"," print('value of current iteration:', value)\n","\n","print('number of iterations:', iteration_count)"],"metadata":{"id":"7gappYcLOrsu","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1681311440820,"user_tz":-120,"elapsed":189,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"039590e8-bbae-4ad4-c622-95110c3e9a8d"},"execution_count":3,"outputs":[{"output_type":"stream","name":"stdout","text":["type of miracle: \n","type of first iteration: \n","value of current iteration: 0\n","value of current iteration: 1\n","number of iterations: 100\n"]}]},{"cell_type":"markdown","source":["3. The `input` function, demonstrated below, lets you ask the human user for a string and then store it in a variable. Write a program that keeps asking the user for words until the word is `'stop'`. For each word, report to the user how many characters it contains."],"metadata":{"id":"2Haq4E95bN6T"}},{"cell_type":"code","source":["#solution 1:\n","word = ''\n","while word != 'stop':\n"," word = input('Please give me a word: ')\n"," print('you wrote', word)\n"," print('the length is', len(word))\n","\n","#solution 2:\n","while True:\n"," word = input('Please give me a word: ')\n"," if word == 'stop':\n"," break\n"," print('you wrote', word)\n"," print('the length is', len(word))"],"metadata":{"id":"0k_YQbBccyC_","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1681313320536,"user_tz":-120,"elapsed":6762,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"ef5102e8-4248-47f3-a59c-87a5958b41db"},"execution_count":6,"outputs":[{"name":"stdout","output_type":"stream","text":["Please give me a word: banana\n","you wrote banana\n","the length is 6\n","Please give me a word: cherry\n","you wrote cherry\n","the length is 6\n","Please give me a word: stop\n"]}]},{"cell_type":"markdown","source":["4. *FizzBuzz part 2* (advanced). Look back at your solution to exercise 5.3 from day 1 (*FizzBuzz part 1*). Now write a program that does the following: for each integer from `1` up to and including `100`, print `Fizz`, `Buzz`, `FizzBuzz` or the number itself, following the same rules as before. Separate the words and numbers by commas. Add a newline after every tenth number, so you print ten lines in total. The first line should start like `1, 2, Fizz, 4, Buzz, Fizz, ...` and the last line should be `91, 92, Fizz, 94, Buzz, Fizz, 97, 98, Fizz, Buzz, `."],"metadata":{"id":"uyqbuhKsUlhG"}},{"cell_type":"code","source":["# Variant 1\n","\n","for n in range(1, 101):\n"," if n % 15 == 0:\n"," print('FizzBuzz', end=', ')\n"," elif n % 3 == 0:\n"," print('Fizz', end=', ')\n"," elif n % 5 == 0:\n"," print('Buzz', end=', ')\n"," else:\n"," print(n, end=', ')\n"," if n % 10 == 0:\n"," print()\n","\n","# Variant 2\n","\n","numbers = range(1, 101)\n","for number in numbers:\n"," if not number % 15:\n"," output = 'FizzBuzz'\n"," elif not number % 3:\n"," output = 'Fizz'\n"," elif not number % 5:\n"," output = 'Buzz'\n"," else:\n"," output = number\n"," if not number % 10:\n"," print(output, end=',\\n')\n"," else:\n"," print(output, end=', ')\n"],"metadata":{"id":"BUeMXIQXaKna","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1681313256562,"user_tz":-120,"elapsed":1068,"user":{"displayName":"Julian Gonggrijp","userId":"06467962548183964912"}},"outputId":"0c5ddcc6-e2d0-40bb-9456-892f751320b6"},"execution_count":5,"outputs":[{"output_type":"stream","name":"stdout","text":["1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz,\n","11, Fizz, 13, 14, FizzBuzz, 16, 17, Fizz, 19, Buzz,\n","Fizz, 22, 23, Fizz, Buzz, 26, Fizz, 28, 29, FizzBuzz,\n","31, 32, Fizz, 34, Buzz, Fizz, 37, 38, Fizz, Buzz,\n","41, Fizz, 43, 44, FizzBuzz, 46, 47, Fizz, 49, Buzz,\n","Fizz, 52, 53, Fizz, Buzz, 56, Fizz, 58, 59, FizzBuzz,\n","61, 62, Fizz, 64, Buzz, Fizz, 67, 68, Fizz, Buzz,\n","71, Fizz, 73, 74, FizzBuzz, 76, 77, Fizz, 79, Buzz,\n","Fizz, 82, 83, Fizz, Buzz, 86, Fizz, 88, 89, FizzBuzz,\n","91, 92, Fizz, 94, Buzz, Fizz, 97, 98, Fizz, Buzz,\n"]}]},{"cell_type":"markdown","source":["## Next module\n","\n","[7. Functions](https://colab.research.google.com/drive/1APhegD_KpLRFn5bC6Ater2wFpT8_Opfr)"],"metadata":{"id":"0eGibfk04LI0"}}]} \ No newline at end of file +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "fqMJHzNk5yXQ" + }, + "source": [ + "# CDH course \"Programming in Python\"\n", + "\n", + "### Exercise Solutions\n", + "[Solutions](https://colab.research.google.com/drive/1vy2gKHtBUMk60u2bmCYlcqmhIFpI2PI-#scrollTo=fqMJHzNk5yXQ)\n", + "\n", + "### CDH course \"Programming in Python\"\n", + "\n", + "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "\n", + "Previous module: [5. Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM?usp=share_link) - [solutions](https://colab.research.google.com/drive/1tqjXsHnnJeEAivrxqRoOSW9eNESCwLIK)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0Gun_3cX1ey8" + }, + "source": [ + "## Exercise 1: basic `for` loops\n", + "\n", + "1. Make a small change to the following code block (copied from above), so that it prints the `full_text` only at the end, when it is complete." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 5, + "status": "ok", + "timestamp": 1681144232455, + "user": { + "displayName": "Mees van Stiphout", + "userId": "10520931415894572279" + }, + "user_tz": -120 + }, + "id": "nMF8WE3F19HC", + "outputId": "7e9843ae-f3d5-43f1-b0a4-64a3c2c309c7" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "A\n", + "very\n", + "short\n", + "sentence\n", + "A very short sentence \n" + ] + } + ], + "source": [ + "words = ['A', 'very', 'short', 'sentence']\n", + "full_text = ''\n", + "\n", + "for word in words:\n", + " full_text = full_text + word + ' '\n", + " # print(word)\n", + "print(full_text)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8zB10pLC2ZaT" + }, + "source": [ + "2. Using a `for` loop, create a copy of the list of fruits below that has the fruits in the reverse order. Change the contents of the list on the first line to check that your solution is general." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 208, + "status": "ok", + "timestamp": 1681311205916, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "bAwZ_ipU28AY", + "outputId": "34d5cb4c-d675-413e-adb6-9d3f7bc41908" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['date', 'cherry', 'banana', 'apricot']\n", + "['date', 'cherry', 'banana', 'apricot']\n", + "['date', 'cherry', 'banana', 'apricot']\n", + "['date', 'cherry', 'banana', 'apricot']\n", + "['date', 'cherry', 'banana', 'apricot']\n" + ] + } + ], + "source": [ + "fruits = ['apricot', 'banana', 'cherry', 'date']\n", + "\n", + "# insert your code here\n", + "# SHOULD output ['date', 'cherry', 'banana', 'apricot']\n", + "\n", + "#solution 1:\n", + "reverse_fruits = []\n", + "for fruit in fruits[-1::-1]:\n", + " reverse_fruits.append(fruit)\n", + "print(reverse_fruits)\n", + "\n", + "#solution 2:\n", + "reverse_fruits = []\n", + "for fruit in fruits:\n", + " reverse_fruits = [fruit] + reverse_fruits\n", + "print(reverse_fruits)\n", + "\n", + "#solution 3:\n", + "reverse_fruits = []\n", + "for fruit in fruits:\n", + " reverse_fruits[0:0] = [fruit]\n", + "print(reverse_fruits)\n", + "\n", + "#solution 4:\n", + "reverse_fruits = []\n", + "position = -1\n", + "for fruit in fruits:\n", + " reverse_fruits.append(fruits[position])\n", + " position = position - 1\n", + "print(reverse_fruits)\n", + "\n", + "#solution 5:\n", + "reverse_fruits = []\n", + "position = len(fruits) - 1\n", + "for fruit in fruits:\n", + " reverse_fruits.append(fruits[position])\n", + " position = position - 1\n", + "print(reverse_fruits)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-K0CvNkOLEYE" + }, + "source": [ + "## Exercise 2: more loops" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Qj9LsMfw-RMT" + }, + "source": [ + "1. The code block below is written to help you explore what kind of \"stuff\" comes out of `range` and `enumerate`. Read the code from top to bottom. What stands out to you? Why do you think it was written this way?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "otBq-xEi-SiC" + }, + "source": [ + "> Things that you might notice:\n", + "> \n", + "> - It is possible to call `range` with one, two or three arguments.\n", + "> - `enumerate` is always called with only one argument.\n", + "> - Some noticed that you can write the string `'pirate'` and pass it directly to `enumerate`, without storing the string in a variable first. This is a good observation, and it is possible with all values and functions in general!\n", + "> - The program is counting iterations. The variable `iteration_count` is initially set to `0`, incremented at every loop iteration and finally printed at the end.\n", + "> - When the `iteration_count` is greater than `2`, the loop does not print the current `value`. In other words, the program only prints the first two values in each iterable.\n", + "> - The program prints the type of the first value in the iterable only.\n", + "> \n", + "> Reasons:\n", + "> \n", + "> - All the various versions of `miracle` are there to illustrate the behavior of `range` and `enumerate`.\n", + "> - We print the type of the iterable as a whole at the start, so you can see what kind of value comes out of the call to `range` or `enumerate`.\n", + "> - We keep track of the number of iterations so you can see how many values are produced by the iterable. We could have used `enumerate` for this, but this would be confusing, since the behavior of `enumerate` itself is the topic of this exercise. Therefore, we use custom logic with the `iteration_count` variable instead.\n", + "> - We only print the first two values in the iterable because this is enough to see a pattern. If we printed all of them, it could potentially produce a lot of output.\n", + "> - We only print the type of the first value, on the assumption that all values in the iterable have the same type.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "EjmxzbeY-USj" + }, + "source": [ + "2. Run the code block for each example value of `miracle`. Based on the results, describe in your own words what `range` and `enumerate` do." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ZyllJHKCBtoy" + }, + "source": [ + "> Possible elements of a good answer for `range`:\n", + "> \n", + "> - If you pass only one value, this sets the exclusive end. It generates whole numbers from `0` (zero) up to but not including the end.\n", + "> - If you pass two values, the first sets the inclusive start and the second sets the exclusive end.\n", + "> - If you pass three values, the first sets the inclusive start, the second the exclusive end and the third a step size.\n", + "> - If you omit the step size (so you pass only one or two arguments), it defaults to `1`.\n", + "> - A negative step size causes the range to count \"backwards\" (from high numbers to low numbers).\n", + "> - You are not allowed to pass floats; the function only supports integers.\n", + "> \n", + "> Possible elements of a good answer for `enumerate`:\n", + "> \n", + "> - It requires another iterable as argument (we see a list, a string and a range in the examples).\n", + "> - The same values from the input iterable also appear in the output iterable, but wrapped in a tuple.\n", + "> - In the output iterable, every tuple has a numerical index in addition to the corresponding value from the input iterable.\n", + "> - The numerical index is the first element of the tuple, while the value is the second element.\n", + "> - The numerical indices start at `0` (zero).\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 189, + "status": "ok", + "timestamp": 1681311440820, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "7gappYcLOrsu", + "outputId": "039590e8-bbae-4ad4-c622-95110c3e9a8d" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "type of miracle: \n", + "type of first iteration: \n", + "value of current iteration: 0\n", + "value of current iteration: 1\n", + "number of iterations: 100\n" + ] + } + ], + "source": [ + "basket = ['apricot', 'banana', 'cherry', 'date']\n", + "\n", + "miracle = range(100)\n", + "# miracle = range(2, 11)\n", + "# miracle = range(2, 11, 3)\n", + "# miracle = range(11, 2, -2)\n", + "# miracle = range(0.1, 1.0, 0.1)\n", + "# miracle = enumerate(basket)\n", + "# miracle = enumerate('pirate')\n", + "# miracle = enumerate(range(10))\n", + "\n", + "print('type of miracle:', type(miracle))\n", + "\n", + "iteration_count = 0\n", + "for value in miracle:\n", + " iteration_count = iteration_count + 1\n", + " if iteration_count > 2:\n", + " continue\n", + " elif iteration_count == 1:\n", + " print('type of first iteration:', type(value))\n", + " print('value of current iteration:', value)\n", + "\n", + "print('number of iterations:', iteration_count)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2Haq4E95bN6T" + }, + "source": [ + "3. The `input` function, demonstrated below, lets you ask the human user for a string and then store it in a variable. Write a program that keeps asking the user for words until the word is `'stop'`. For each word, report to the user how many characters it contains." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 6762, + "status": "ok", + "timestamp": 1681313320536, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "0k_YQbBccyC_", + "outputId": "ef5102e8-4248-47f3-a59c-87a5958b41db" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Please give me a word: banana\n", + "you wrote banana\n", + "the length is 6\n", + "Please give me a word: cherry\n", + "you wrote cherry\n", + "the length is 6\n", + "Please give me a word: stop\n" + ] + } + ], + "source": [ + "#solution 1:\n", + "word = ''\n", + "while word != 'stop':\n", + " word = input('Please give me a word: ')\n", + " print('you wrote', word)\n", + " print('the length is', len(word))\n", + "\n", + "#solution 2:\n", + "while True:\n", + " word = input('Please give me a word: ')\n", + " if word == 'stop':\n", + " break\n", + " print('you wrote', word)\n", + " print('the length is', len(word))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "uyqbuhKsUlhG" + }, + "source": [ + "4. *FizzBuzz part 2* (advanced). Look back at your solution to exercise 5.3 from day 1 (*FizzBuzz part 1*). Now write a program that does the following: for each integer from `1` up to and including `100`, print `Fizz`, `Buzz`, `FizzBuzz` or the number itself, following the same rules as before. Separate the words and numbers by commas. Add a newline after every tenth number, so you print ten lines in total. The first line should start like `1, 2, Fizz, 4, Buzz, Fizz, ...` and the last line should be `91, 92, Fizz, 94, Buzz, Fizz, 97, 98, Fizz, Buzz, `." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 1068, + "status": "ok", + "timestamp": 1681313256562, + "user": { + "displayName": "Julian Gonggrijp", + "userId": "06467962548183964912" + }, + "user_tz": -120 + }, + "id": "BUeMXIQXaKna", + "outputId": "0c5ddcc6-e2d0-40bb-9456-892f751320b6" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz,\n", + "11, Fizz, 13, 14, FizzBuzz, 16, 17, Fizz, 19, Buzz,\n", + "Fizz, 22, 23, Fizz, Buzz, 26, Fizz, 28, 29, FizzBuzz,\n", + "31, 32, Fizz, 34, Buzz, Fizz, 37, 38, Fizz, Buzz,\n", + "41, Fizz, 43, 44, FizzBuzz, 46, 47, Fizz, 49, Buzz,\n", + "Fizz, 52, 53, Fizz, Buzz, 56, Fizz, 58, 59, FizzBuzz,\n", + "61, 62, Fizz, 64, Buzz, Fizz, 67, 68, Fizz, Buzz,\n", + "71, Fizz, 73, 74, FizzBuzz, 76, 77, Fizz, 79, Buzz,\n", + "Fizz, 82, 83, Fizz, Buzz, 86, Fizz, 88, 89, FizzBuzz,\n", + "91, 92, Fizz, 94, Buzz, Fizz, 97, 98, Fizz, Buzz,\n" + ] + } + ], + "source": [ + "# Variant 1\n", + "\n", + "for n in range(1, 101):\n", + " if n % 15 == 0:\n", + " print('FizzBuzz', end=', ')\n", + " elif n % 3 == 0:\n", + " print('Fizz', end=', ')\n", + " elif n % 5 == 0:\n", + " print('Buzz', end=', ')\n", + " else:\n", + " print(n, end=', ')\n", + " if n % 10 == 0:\n", + " print()\n", + "\n", + "# Variant 2\n", + "\n", + "numbers = range(1, 101)\n", + "for number in numbers:\n", + " if not number % 15:\n", + " output = 'FizzBuzz'\n", + " elif not number % 3:\n", + " output = 'Fizz'\n", + " elif not number % 5:\n", + " output = 'Buzz'\n", + " else:\n", + " output = number\n", + " if not number % 10:\n", + " print(output, end=',\\n')\n", + " else:\n", + " print(output, end=', ')\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0eGibfk04LI0" + }, + "source": [ + "## Next module\n", + "\n", + "[7. Functions](https://colab.research.google.com/drive/1APhegD_KpLRFn5bC6Ater2wFpT8_Opfr)" + ] + } + ], + "metadata": { + "colab": { + "provenance": [], + "toc_visible": true + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/solutions/06 Loops - Solutions.py b/solutions/06 Loops - Solutions.py new file mode 100644 index 0000000..4a576a0 --- /dev/null +++ b/solutions/06 Loops - Solutions.py @@ -0,0 +1,215 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# name: python3 +# --- + +# %% [markdown] id="fqMJHzNk5yXQ" +# # CDH course "Programming in Python" +# +# ### Exercise Solutions +# [Solutions](https://colab.research.google.com/drive/1vy2gKHtBUMk60u2bmCYlcqmhIFpI2PI-#scrollTo=fqMJHzNk5yXQ) +# +# ### CDH course "Programming in Python" +# +# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# +# Previous module: [5. Assertions](https://colab.research.google.com/drive/1Nv6vgkH2zjJes7LXUCVhsDwFOGCtTbHM?usp=share_link) - [solutions](https://colab.research.google.com/drive/1tqjXsHnnJeEAivrxqRoOSW9eNESCwLIK) + +# %% [markdown] id="0Gun_3cX1ey8" +# ## Exercise 1: basic `for` loops +# +# 1. Make a small change to the following code block (copied from above), so that it prints the `full_text` only at the end, when it is complete. + +# %% id="nMF8WE3F19HC" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1681144232455, "user_tz": -120, "elapsed": 5, "user": {"displayName": "Mees van Stiphout", "userId": "10520931415894572279"}} outputId="7e9843ae-f3d5-43f1-b0a4-64a3c2c309c7" +words = ['A', 'very', 'short', 'sentence'] +full_text = '' + +for word in words: + full_text = full_text + word + ' ' + # print(word) +print(full_text) + +# %% [markdown] id="8zB10pLC2ZaT" +# 2. Using a `for` loop, create a copy of the list of fruits below that has the fruits in the reverse order. Change the contents of the list on the first line to check that your solution is general. + +# %% id="bAwZ_ipU28AY" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1681311205916, "user_tz": -120, "elapsed": 208, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="34d5cb4c-d675-413e-adb6-9d3f7bc41908" +fruits = ['apricot', 'banana', 'cherry', 'date'] + +# insert your code here +# SHOULD output ['date', 'cherry', 'banana', 'apricot'] + +#solution 1: +reverse_fruits = [] +for fruit in fruits[-1::-1]: + reverse_fruits.append(fruit) +print(reverse_fruits) + +#solution 2: +reverse_fruits = [] +for fruit in fruits: + reverse_fruits = [fruit] + reverse_fruits +print(reverse_fruits) + +#solution 3: +reverse_fruits = [] +for fruit in fruits: + reverse_fruits[0:0] = [fruit] +print(reverse_fruits) + +#solution 4: +reverse_fruits = [] +position = -1 +for fruit in fruits: + reverse_fruits.append(fruits[position]) + position = position - 1 +print(reverse_fruits) + +#solution 5: +reverse_fruits = [] +position = len(fruits) - 1 +for fruit in fruits: + reverse_fruits.append(fruits[position]) + position = position - 1 +print(reverse_fruits) + + +# %% [markdown] id="-K0CvNkOLEYE" +# ## Exercise 2: more loops + +# %% [markdown] id="Qj9LsMfw-RMT" +# 1. The code block below is written to help you explore what kind of "stuff" comes out of `range` and `enumerate`. Read the code from top to bottom. What stands out to you? Why do you think it was written this way? + +# %% [markdown] id="otBq-xEi-SiC" +# > Things that you might notice: +# > +# > - It is possible to call `range` with one, two or three arguments. +# > - `enumerate` is always called with only one argument. +# > - Some noticed that you can write the string `'pirate'` and pass it directly to `enumerate`, without storing the string in a variable first. This is a good observation, and it is possible with all values and functions in general! +# > - The program is counting iterations. The variable `iteration_count` is initially set to `0`, incremented at every loop iteration and finally printed at the end. +# > - When the `iteration_count` is greater than `2`, the loop does not print the current `value`. In other words, the program only prints the first two values in each iterable. +# > - The program prints the type of the first value in the iterable only. +# > +# > Reasons: +# > +# > - All the various versions of `miracle` are there to illustrate the behavior of `range` and `enumerate`. +# > - We print the type of the iterable as a whole at the start, so you can see what kind of value comes out of the call to `range` or `enumerate`. +# > - We keep track of the number of iterations so you can see how many values are produced by the iterable. We could have used `enumerate` for this, but this would be confusing, since the behavior of `enumerate` itself is the topic of this exercise. Therefore, we use custom logic with the `iteration_count` variable instead. +# > - We only print the first two values in the iterable because this is enough to see a pattern. If we printed all of them, it could potentially produce a lot of output. +# > - We only print the type of the first value, on the assumption that all values in the iterable have the same type. +# +# + +# %% [markdown] id="EjmxzbeY-USj" +# 2. Run the code block for each example value of `miracle`. Based on the results, describe in your own words what `range` and `enumerate` do. + +# %% [markdown] id="ZyllJHKCBtoy" +# > Possible elements of a good answer for `range`: +# > +# > - If you pass only one value, this sets the exclusive end. It generates whole numbers from `0` (zero) up to but not including the end. +# > - If you pass two values, the first sets the inclusive start and the second sets the exclusive end. +# > - If you pass three values, the first sets the inclusive start, the second the exclusive end and the third a step size. +# > - If you omit the step size (so you pass only one or two arguments), it defaults to `1`. +# > - A negative step size causes the range to count "backwards" (from high numbers to low numbers). +# > - You are not allowed to pass floats; the function only supports integers. +# > +# > Possible elements of a good answer for `enumerate`: +# > +# > - It requires another iterable as argument (we see a list, a string and a range in the examples). +# > - The same values from the input iterable also appear in the output iterable, but wrapped in a tuple. +# > - In the output iterable, every tuple has a numerical index in addition to the corresponding value from the input iterable. +# > - The numerical index is the first element of the tuple, while the value is the second element. +# > - The numerical indices start at `0` (zero). +# + +# %% id="7gappYcLOrsu" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1681311440820, "user_tz": -120, "elapsed": 189, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="039590e8-bbae-4ad4-c622-95110c3e9a8d" +basket = ['apricot', 'banana', 'cherry', 'date'] + +miracle = range(100) +# miracle = range(2, 11) +# miracle = range(2, 11, 3) +# miracle = range(11, 2, -2) +# miracle = range(0.1, 1.0, 0.1) +# miracle = enumerate(basket) +# miracle = enumerate('pirate') +# miracle = enumerate(range(10)) + +print('type of miracle:', type(miracle)) + +iteration_count = 0 +for value in miracle: + iteration_count = iteration_count + 1 + if iteration_count > 2: + continue + elif iteration_count == 1: + print('type of first iteration:', type(value)) + print('value of current iteration:', value) + +print('number of iterations:', iteration_count) + +# %% [markdown] id="2Haq4E95bN6T" +# 3. The `input` function, demonstrated below, lets you ask the human user for a string and then store it in a variable. Write a program that keeps asking the user for words until the word is `'stop'`. For each word, report to the user how many characters it contains. + +# %% id="0k_YQbBccyC_" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1681313320536, "user_tz": -120, "elapsed": 6762, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="ef5102e8-4248-47f3-a59c-87a5958b41db" +#solution 1: +word = '' +while word != 'stop': + word = input('Please give me a word: ') + print('you wrote', word) + print('the length is', len(word)) + +#solution 2: +while True: + word = input('Please give me a word: ') + if word == 'stop': + break + print('you wrote', word) + print('the length is', len(word)) + +# %% [markdown] id="uyqbuhKsUlhG" +# 4. *FizzBuzz part 2* (advanced). Look back at your solution to exercise 5.3 from day 1 (*FizzBuzz part 1*). Now write a program that does the following: for each integer from `1` up to and including `100`, print `Fizz`, `Buzz`, `FizzBuzz` or the number itself, following the same rules as before. Separate the words and numbers by commas. Add a newline after every tenth number, so you print ten lines in total. The first line should start like `1, 2, Fizz, 4, Buzz, Fizz, ...` and the last line should be `91, 92, Fizz, 94, Buzz, Fizz, 97, 98, Fizz, Buzz, `. + +# %% id="BUeMXIQXaKna" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1681313256562, "user_tz": -120, "elapsed": 1068, "user": {"displayName": "Julian Gonggrijp", "userId": "06467962548183964912"}} outputId="0c5ddcc6-e2d0-40bb-9456-892f751320b6" +# Variant 1 + +for n in range(1, 101): + if n % 15 == 0: + print('FizzBuzz', end=', ') + elif n % 3 == 0: + print('Fizz', end=', ') + elif n % 5 == 0: + print('Buzz', end=', ') + else: + print(n, end=', ') + if n % 10 == 0: + print() + +# Variant 2 + +numbers = range(1, 101) +for number in numbers: + if not number % 15: + output = 'FizzBuzz' + elif not number % 3: + output = 'Fizz' + elif not number % 5: + output = 'Buzz' + else: + output = number + if not number % 10: + print(output, end=',\n') + else: + print(output, end=', ') + + +# %% [markdown] id="0eGibfk04LI0" +# ## Next module +# +# [7. Functions](https://colab.research.google.com/drive/1APhegD_KpLRFn5bC6Ater2wFpT8_Opfr) diff --git a/solutions/07 Functions solutions.ipynb b/solutions/07 Functions solutions.ipynb index 2672a79..f1061dd 100644 --- a/solutions/07 Functions solutions.ipynb +++ b/solutions/07 Functions solutions.ipynb @@ -1 +1,1677 @@ -{"cells":[{"cell_type":"markdown","metadata":{"id":"fqMJHzNk5yXQ"},"source":["# Module 7: Functions\n","\n","### CDH course \"Programming in Python\"\n","\n","[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n","\n","Previous module: [6. Loops](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV)\n","\n","### This module\n","\n","- Storing code in a variable so you can reuse it.\n","- Being explicit about the purpose of your code."]},{"cell_type":"markdown","metadata":{"id":"aPFGhEVz40JP"},"source":["## Exercise 7.1: functions"]},{"cell_type":"markdown","metadata":{"id":"hfcz-cSEKZWW"},"source":["1. We have seen several standard functions during the course, such as `ord` and `len`. List as many as you can from memory. Try to list the types of the parameters and return values of each function as well. Do any of these functions have side effects?"]},{"cell_type":"markdown","metadata":{"id":"ZG0lv6MhVP8L"},"source":["Some examples:\n","- `ord`: Has 1 parameter: a string of 1 character. The return value is an integer. No side effects.\n","- `len`: Has 1 parameter: a list, tuple or other iterable. The return value is an integer. No side effects.\n","- `input`: Has 1 optional parameter `prompt`, which should be a string. The function has a side effect, which is to wait for the user to type something. Its return value is a string (namely the text that you have entered)."]},{"cell_type":"markdown","metadata":{"id":"BUnMsiUzKbws"},"source":["2. For exercise purposes, code in the following blocks may call `print` inside a function and omit a docstring. Predict the output, then run the code to see whether your prediction was right. Try to explain any discrepancies."]},{"cell_type":"code","execution_count":2,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":366,"status":"ok","timestamp":1681904959646,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"_3va9jT5O0H7","outputId":"0187c43c-58c4-45c6-935f-a22d2bf5c864"},"outputs":[{"name":"stdout","output_type":"stream","text":["Hello, Berit!\n"]}],"source":["def greet(name):\n"," return 'Hello, ' + name + '!'\n","\n","print(greet('Berit'))"]},{"cell_type":"code","execution_count":4,"metadata":{"executionInfo":{"elapsed":5,"status":"ok","timestamp":1681905000500,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"vdTIwoGxM_JV"},"outputs":[],"source":["name = 'Luka'\n","\n","def exclaim(name):\n"," print(name + '!')\n","\n","# nothing happens: we have created a function but we haven't called it!"]},{"cell_type":"code","execution_count":5,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":5,"status":"ok","timestamp":1681905065618,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"30fv8SAMOblV","outputId":"99fd2054-9609-4646-c321-2f4513b6cc5f"},"outputs":[{"name":"stdout","output_type":"stream","text":["False\n"]}],"source":["def false():\n"," return True\n","\n","print(False)\n","\n","# we are printing the literal value `False`, which is not the same as our function `false` (all lower case)\n","# Capitalisation matters in python!"]},{"cell_type":"code","execution_count":8,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":13,"status":"ok","timestamp":1681905150776,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"ongrNaZFNNmV","outputId":"45d7e513-7157-4c54-ab92-c4380fb59f02"},"outputs":[{"name":"stdout","output_type":"stream","text":["None\n"]}],"source":["length = 5\n","width = 2\n","\n","def calculate_area():\n"," area = length * width\n","\n","print(calculate_area())\n","\n","# calculate_area calculates something, but it does not make a return statement.\n","# So the returned value of `calculate_area()` is None"]},{"cell_type":"code","execution_count":9,"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":35},"executionInfo":{"elapsed":378,"status":"ok","timestamp":1681905166543,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"MSkOCMMyNoUO","outputId":"a9fca128-9415-4ef7-97cb-1546e36f67db"},"outputs":[{"data":{"application/vnd.google.colaboratory.intrinsic+json":{"type":"string"},"text/plain":["'Who is Jelte?'"]},"execution_count":9,"metadata":{},"output_type":"execute_result"}],"source":["def question(name):\n"," return 'Who is ' + name + '?'\n","\n","question('Jelte')"]},{"cell_type":"code","execution_count":11,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":6,"status":"ok","timestamp":1681905320174,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"72DDRhD5OQ0g","outputId":"3cd844dd-d4ca-4418-db9e-8c3065a9d3b7"},"outputs":[{"name":"stdout","output_type":"stream","text":["sweetadditionsweetaddition\n"]}],"source":["def add(left, right):\n"," return left + right\n","\n","print(add('sweet', 'addition') * add(1, 1))\n","\n","# add('sweet', 'addition') will return 'sweet' + addition', which concatenates\n","# the strings to 'sweetaddition'\n","\n","# add(1, 1) becomes 1 + 1, so 2\n","\n","# then 'sweetaddition' * 2 means \"repeat 'sweetaddition' twice\"\n","# hence 'sweetadditionsweetaddition'"]},{"cell_type":"code","execution_count":15,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":360,"status":"ok","timestamp":1681906368916,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"L0GlJecf8ntf","outputId":"893d9d77-378d-4d5c-8a17-fa14fdbd0322"},"outputs":[{"name":"stdout","output_type":"stream","text":["Julian\n","Julian\n","\n","Julian\n","Jelte\n","Berit\n","36\n","Julian\n","36\n"]}],"source":["# I've added some comments to each print statement, to explain what's happening and in what order.\n","\n","name = 'Julian'\n","age = 36\n","\n","print(name) # 1st print\n","\n","# make a function. the code in the body will not be executed until we call the function below.\n","def example(name):\n"," print(name) # 5th print: we are printing the *parameter* `name` that we\n"," # passed on to the function when we called it, which is 'Jelte'. There is a \n"," # global parameter which is also called `name` (with value 'Julian'), but\n"," # the parameter takes precedence.\n"," \n"," name = 'Berit' # update the value of the *parameter*\n","\n"," print(name) # 6th print: print the new value of the parameter\n","\n"," print(age) # 7th print: print age. There is no `age` variable specific\n"," # to the function, so we refer to the global variable (36)\n","\n","print(name) # 2nd print: name is unchanged. `name = 'Berit'` was part of the\n","# function body, it's not yet executed\n","\n","print(example) # 3rd print: we are printing the function itself, not the\n","# returned value!\n","\n","print(name) # 4th print: name is still unchanged.\n","\n","example('Jelte') # we are running the function here, and now we will execute the\n","# print statements within its body\n","\n","print(name) # 8th print: the function `example` did some stuff with its\n","# parameter called name, but did not do anything with the global variable name.\n","# So `name` is still 'Julian'\n","\n","print(age) # 9th print\n","\n"]},{"cell_type":"markdown","metadata":{"id":"Rwvwlpp0-Hrt"},"source":["3. In each of the following code blocks, write a docstring for the function, then add assertions and run the block to check that your documentation is correct."]},{"cell_type":"code","execution_count":19,"metadata":{"executionInfo":{"elapsed":366,"status":"ok","timestamp":1681906464925,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"ajcRnvzQQ9c5"},"outputs":[],"source":["def odd(number):\n"," '''\n"," Checks whether a number is odd\n","\n"," Input should be an integer. Output is a boolean, True if the number is not\n"," divisible by two.\n"," '''\n"," return number % 2 == 1\n","\n","assert odd(1) == True\n","assert odd(6) == False\n","assert odd(0) == False"]},{"cell_type":"code","execution_count":23,"metadata":{"executionInfo":{"elapsed":393,"status":"ok","timestamp":1681906766119,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"giU_bIKrRME4"},"outputs":[],"source":["def magic(word):\n"," '''\n"," Checks that the input is a nonempty string and starts with a capital letter.\n","\n"," Input can be of any type: anything other than a string will return False.\n"," For strings, checks that the string is not empty and the first character\n"," is an uppercase latin character. \n"," '''\n"," if not word or type(word) != str:\n"," return False\n"," if 'A' <= word[0] <= 'Z':\n"," return True\n"," return False\n","\n","assert magic(123) == False\n","assert magic('Hello python programmers!') == True\n","assert magic('python python python') == False\n","assert magic('🤔') == False\n","assert magic('') == False"]},{"cell_type":"code","execution_count":28,"metadata":{"executionInfo":{"elapsed":8,"status":"ok","timestamp":1681907208099,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"pAChbRWn-SKS"},"outputs":[],"source":["def join_commas(words):\n"," '''\n"," Joins a iterable of strings into a single string, with ', ' between each item.\n","\n"," Input should be an iterable of strings with at least 1 item.\n"," '''\n"," first, *rest = list(words)\n"," text = first\n"," for word in rest:\n"," text = text + ', ' + word\n"," return text\n","\n","assert join_commas(['a', 'b', 'c']) == 'a, b, c'\n","assert join_commas(['a']) == 'a'\n","assert join_commas(('a', 'b', 'c')) == 'a, b, c'"]},{"cell_type":"markdown","metadata":{"id":"mc9RtAeATiHw"},"source":["4. Write a function `is_number` that takes one argument. If the argument is a number, it should return `True`; otherwise, it should return `False`. Make sure to include a docstring!"]},{"cell_type":"code","execution_count":29,"metadata":{"executionInfo":{"elapsed":399,"status":"ok","timestamp":1681907269669,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"Ap33-rF-UbsB"},"outputs":[],"source":["# Your definition of is_number here\n","\n","def is_number(value):\n"," '''\n"," Checks if the input is a number (int or float)\n"," '''\n","\n"," number_types = [int, float]\n"," return type(value) in number_types\n","\n","# The following lines will check your solution (no output is good)\n","assert is_number(0)\n","assert is_number(10)\n","assert is_number(0.5)\n","assert is_number(8 / 5)\n","assert not is_number(None)\n","assert not is_number('dear')\n","assert not is_number('123')"]},{"cell_type":"markdown","metadata":{"id":"apA7o120TYRl"},"source":["## Exercise 7.2: bonus"]},{"cell_type":"markdown","metadata":{"id":"nM43w3VlB3-O"},"source":["1. Write a function `last_a_index` that, given a string, returns the **last** index of the letter `'a'` in the string. If there is no `'a'` in the string, the function should return `None`."]},{"cell_type":"code","execution_count":32,"metadata":{"executionInfo":{"elapsed":10,"status":"ok","timestamp":1681907469796,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"I5s4_a53ENJC"},"outputs":[],"source":["# Define last_a_index here\n","\n","def last_a_index(text):\n"," '''\n"," Gives the index of the last 'a' in the string.\n","\n"," If there is no 'a' in the string, returns None\n"," '''\n","\n"," a_index = None\n","\n"," for (index, character) in enumerate(text):\n"," if character == 'a':\n"," a_index = index\n"," \n"," return a_index\n","\n","assert last_a_index('banana') == 5\n","assert last_a_index('cherry') == None\n","assert last_a_index('Once upon a time, there was a dragon') == 32"]},{"cell_type":"markdown","metadata":{"id":"z4z3-dOaVROx"},"source":["2. Write a function `replace` with three string arguments: `text`, `query` and `replacement`. It should return a copy of `text` in which all occurrences of `query` are replaced by `replacement`. For example, `replace('small', 'a', 'e')` should return `'smell'`. You may assume that `query` is always a single character. For an additional challenge, you can try to also handle multi-character query strings. For ultimate challenge, try to think of a way to handle the empty string!"]},{"cell_type":"code","execution_count":34,"metadata":{"executionInfo":{"elapsed":2,"status":"ok","timestamp":1681907653952,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"kyQwfz-mYvfW"},"outputs":[],"source":["# Define replace here\n","\n","# solution one: does not work with multi-character strings\n","def replace(text, query, replacement):\n"," '''\n"," Replace each occurence of a character in a string\n"," \n"," Input:\n"," - text: a string in which you want to replace something\n"," - query: the character you wish to replace\n"," - replacement: the string that should replace `query`\n"," '''\n","\n"," replaced = ''\n","\n"," for character in text:\n"," if character == query:\n"," replaced = replaced + replacement\n"," else:\n"," replaced = replaced + character\n"," \n"," return replaced\n","\n","assert replace('small', 'a', 'e') == 'smell'\n","assert replace('banana', 'a', 'o') == 'bonono'\n","assert replace('cherry', 'a', 'x') == 'cherry'"]},{"cell_type":"code","execution_count":41,"metadata":{"executionInfo":{"elapsed":350,"status":"ok","timestamp":1681908669621,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"2ajcWTx1gi2r"},"outputs":[],"source":["# solution 2: handle multi-character strings and empty strings\n","\n","# There no clear answer for what should happen in the case of empty strings!\n","# It's a question that does not really make sense: replace every insstance of\n","# nothing?\n","\n","# Here we just return `None` when query is empty. You could also return the\n","# original string.\n","\n","def replace(text, query, replacement):\n"," '''\n"," Replace each occurence of a substring in a string\n"," \n"," Input:\n"," - text: a string in which you want to replace something\n"," - query: the string you wish to replace\n"," - replacement: the string that should replace `query`\n","\n"," If the query is empty, returns None.\n"," '''\n","\n"," if not query:\n"," return None\n"," \n"," size = len(query)\n"," new_text = '' # where we build our new text\n"," position = 0 # our position as we move through the text\n","\n"," while position < len(text):\n"," possible_match = text[position:position+size] # slice of text from here, the same size as the query\n"," if possible_match == query: #if this matches...\n"," new_text += replacement # put the replacement in our new string\n"," position += size # and skip ahead until after this slice\n"," else:\n"," # if it doesn't match, move ahead by one character\n"," new_text += text[position]\n"," position += 1\n"," \n"," return new_text\n"," \n","\n","assert replace('small', 'a', 'e') == 'smell'\n","assert replace('banana', 'a', 'o') == 'bonono'\n","assert replace('cherry', 'a', 'x') == 'cherry'\n","assert replace('small', '', '?') == None\n","assert replace('banana', 'an', 'ab') == 'bababa'\n","assert replace('banana', 'banana', 'apple') == 'apple' \n","assert replace('banana', 'e', 'o') == 'banana'"]},{"cell_type":"markdown","metadata":{"id":"1xUD2ZNjcqEZ"},"source":["## Exercise 7.3: function tricks"]},{"cell_type":"markdown","metadata":{"id":"hxRC5Nx-cyM4"},"source":["1. In each of the code blocks below, predict the output, check your prediction by running the code, and try to explain any surprises."]},{"cell_type":"code","execution_count":1,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":392,"status":"ok","timestamp":1681908707519,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"HPxio0q6c-M9","outputId":"8542c5de-15ae-4841-b438-1211d999241f"},"outputs":[{"name":"stdout","output_type":"stream","text":["Welcome to my office. How can I help you?\n","Please make yourself at home, Sheean!\n"]}],"source":["def welcome(name=None):\n"," if name is None:\n"," return 'Welcome to my office. How can I help you?'\n"," return 'Please make yourself at home, ' + name + '!'\n","\n","print(welcome()) # default option\n","print(welcome('Sheean'))"]},{"cell_type":"code","execution_count":45,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":6,"status":"ok","timestamp":1681908774383,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"NxXqK4ROdxva","outputId":"08744e41-cbe2-4c45-9566-9174466aaf88"},"outputs":[{"name":"stdout","output_type":"stream","text":["15\n"]}],"source":["def table(number):\n"," return number, number * 2, number * 3 #return three numbers\n","\n","first, second, third = table(5) # table(5) is (5, 10, 15) ; unpack these into three variables\n","\n","print(third) #print the third"]},{"cell_type":"code","execution_count":48,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":14,"status":"ok","timestamp":1681908875617,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"EVCf3w9qfBCU","outputId":"8883c815-c80f-4f0a-f185-4d6d935e54cb"},"outputs":[{"name":"stdout","output_type":"stream","text":["For naming, contact Job.\n"]}],"source":["def contact(name, job):\n"," return 'For ' + job + ', contact ' + name + '.'\n","\n","contact_line = contact(job='naming', name='Job') # we use named arguments, so we can supply `name` and `job` in any order\n","print(contact_line)"]},{"cell_type":"code","execution_count":50,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":4,"status":"ok","timestamp":1681909054548,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"OUSpe-hPl6-G","outputId":"fa03386e-6570-43ce-a65b-424d5cbfb193"},"outputs":[{"name":"stdout","output_type":"stream","text":["As my mother would say: Julian?\n"]}],"source":["def exclaim(name):\n"," return name + '!'\n","\n","def request(name):\n"," return name + '?'\n","\n","def tell(how):\n"," return 'As my mother would say: ' + how('Julian')\n","\n","print(tell(request))\n","\n","# tell(request) will return `'As my mother would say: ' + how('Julian')`\n","# `how` is whatever we pass on as the argument to `tell`, in this case the function `request`\n","# so we can fill in `how('Julian')` as `request('Julian')`\n","# now we can look at the body of `request` to see what it does (it puts a\n","# question mark behind the name)"]},{"cell_type":"code","execution_count":52,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":9,"status":"ok","timestamp":1681909174217,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"uSN17qnvmoWv","outputId":"f36d7049-e723-4036-e0b3-4b48a9b2103f"},"outputs":[{"name":"stdout","output_type":"stream","text":["apricot; banana; cherry\n"]}],"source":["def make_enumerate(separator):\n"," def enumerate(items):\n"," if not len(items):\n"," return ''\n"," result = items[0]\n"," for item in items[1:]:\n"," result = result + separator + item\n"," return result\n"," return enumerate\n","\n","with_semicolon = make_enumerate('; ')\n","fruits = with_semicolon(['apricot', 'banana', 'cherry'])\n","print(fruits)\n","\n","# with_semicolon is the output of make_enumerate: another function.\n","# This function takes a list of strings as input and joins them with a semicolon\n","# between them. we can then call `with_semicolon` on the list of fruits."]},{"cell_type":"markdown","metadata":{"id":"CKma4p6Egkwb"},"source":["2. In each of the following code blocks, something is missing in the function definition. Add the missing element."]},{"cell_type":"code","execution_count":53,"metadata":{"executionInfo":{"elapsed":6,"status":"ok","timestamp":1681909191045,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"SaygnjMygwNF"},"outputs":[],"source":["def combine(left, right):\n"," \"\"\" Compute the sum and the product of the arguments. \"\"\"\n"," sum = left + right\n"," product = left * right\n"," return sum, product #return both the sum and the product\n","\n","assert combine(2, 3) == (5, 6)"]},{"cell_type":"code","execution_count":55,"metadata":{"executionInfo":{"elapsed":6,"status":"ok","timestamp":1681909211878,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"GqIgTcG6hMuG"},"outputs":[],"source":["def announce_time(hour, minute=0): # minute should be optional, so we add a default value.\n"," \"\"\" Announce the time in a speaking clock manner. \"\"\"\n"," if minute < 10:\n"," minute = '0' + str(minute)\n"," time = str(hour) + ':' + str(minute)\n"," return 'At the next beep, the time will be ' + time + '. BEEP!'\n","\n","assert announce_time(12) == 'At the next beep, the time will be 12:00. BEEP!'"]},{"cell_type":"code","execution_count":56,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":395,"status":"ok","timestamp":1681909264360,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"nH77gJdOkS5b","outputId":"f70a5f41-1916-4deb-da38-248ea056a749"},"outputs":[{"name":"stdout","output_type":"stream","text":["holiday\n"]}],"source":["def echo(value):\n"," print(value)\n"," return value\n","\n","assert echo('holiday') == 'holiday'"]},{"cell_type":"markdown","metadata":{"id":"YG5XrO1PoIJx"},"source":["3. In the following code block, write a function `month`, which returns a string name for given numeric month. Month `1` is `'January'` and month `12` is `December`. When no argument is passed, it should default to the current month. **Tip:** you can avoid writing a big `if`/`elif`/`else` tree by using a list of month names and using the month number as a list index."]},{"cell_type":"code","execution_count":73,"metadata":{"executionInfo":{"elapsed":662,"status":"ok","timestamp":1681911393494,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"TnuU_I0Tq9wQ"},"outputs":[],"source":["# You may pretend that it is forever April\n","current_month = 4\n","\n","months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']\n","\n","def month(number=current_month):\n"," '''\n"," Return the name of a month based on an number.\n","\n"," Input should be an integer between 1 and 12. Returns None if this is not the case.\n"," If no month number is given, defaults to April.\n","\n"," Returns a string with the name of the Month.\n"," '''\n","\n"," if type(number) is not int or number < 1 or number > 12:\n"," return None\n","\n"," index = number - 1\n"," return months[index]\n","\n","# Your definition of month here\n","\n","assert month(3) == 'March'\n","assert month(4) == 'April'\n","assert month(11) == 'November'\n","assert month() == 'April'"]},{"cell_type":"markdown","metadata":{"id":"WuRrElhUsD40"},"source":["4. In the following code block, write a function `weekday`, which is analogous to `month` in the previous exercise. Day `1` is `'Monday'` while day `0` and day `7` are both `'Sunday'`. Can you avoid writing the string `'Sunday'` twice in your code?"]},{"cell_type":"code","execution_count":74,"metadata":{"executionInfo":{"elapsed":8,"status":"ok","timestamp":1681911394923,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"WUGQqmJysrqS"},"outputs":[],"source":["# You may pretend it is forever Tuesday\n","current_weekday = 2\n","\n","weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']\n","\n","def weekday(number=current_weekday):\n"," '''\n"," Return the name of a weekday based on an number.\n","\n"," Input should be an integer between 0 and 7. Returns None if this is not the case.\n"," If no weekday number is given, defaults to Tuesday.\n","\n"," Returns a string with the name of the weekday. Day 1 is 'Monday' while day 0\n"," and day 7 are both 'Sunday'.\n"," '''\n"," if type(number) is not int or number < 0 or number > 7:\n"," return None\n"," \n"," index = (number - 1) % 7\n"," return weekdays[index]\n","\n","# Your definition of weekday here\n","\n","assert weekday() == 'Tuesday'\n","assert weekday(0) == 'Sunday'\n","assert weekday(7) == 'Sunday'\n","assert weekday(4) == 'Thursday'"]},{"cell_type":"markdown","metadata":{"id":"ZvfEq3NctoOo"},"source":["## Exercise 7.4: bonus"]},{"cell_type":"markdown","metadata":{"id":"8au2fNRutw8i"},"source":["1. In exercises 7.3.4 and 7.3.5, where you just implemented `month` and `weekday`, your code was likely very similar. How could you implement both functions without repeating yourself? Check your solution with assertions."]},{"cell_type":"code","execution_count":78,"metadata":{"executionInfo":{"elapsed":444,"status":"ok","timestamp":1681911460720,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"JFhOX_Z5uVfC"},"outputs":[],"source":["def make_namer(names, default):\n"," '''\n"," Make a function to give names (months, weekdays, etc) based on a number.\n","\n"," Input:\n"," - A list of names (strings)\n"," - A default input value for the function\n","\n"," Returns a function that takes a number and returns the corresponding name.\n"," 1 is the first list in the name, etc.\n"," '''\n"," size = len(names)\n","\n"," def name(number=default):\n"," '''\n"," Gives the name corresponding to a number\n"," '''\n"," if type(number) is not int or number < 0 or number > size:\n"," return None\n"," \n"," index = (number - 1) % size\n"," return names[index]\n"," \n"," return name\n","\n","# now we can make our month and weekday functions based on make_namer\n","\n","current_month = 4\n","months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']\n","\n","current_weekday = 2\n","weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']\n","\n","month = make_namer(months, current_month)\n","weekday = make_namer(weekdays, current_weekday)\n","\n","# repeat the assert statements above to show everything still works\n","\n","assert month(3) == 'March'\n","assert month(4) == 'April'\n","assert month(11) == 'November'\n","assert month() == 'April'\n","\n","assert weekday() == 'Tuesday'\n","assert weekday(0) == 'Sunday'\n","assert weekday(7) == 'Sunday'\n","assert weekday(4) == 'Thursday'"]},{"cell_type":"markdown","metadata":{"id":"Gx54DrgJuWKg"},"source":["2. Write a function `create_date_format`, which takes two arguments that determine how a date should be displayed. The first argument is function that transforms a weekday number to a string suitable for display, the second argument is a function that transforms a month number. Both arguments default to just displaying the number as-is. `create_date_format` returns a new function. The latter function takes any date defined by a year, month, day and weekday, and returns a string suitable for displaying that date as a whole."]},{"cell_type":"code","execution_count":80,"metadata":{"executionInfo":{"elapsed":9,"status":"ok","timestamp":1681912144802,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"0KyXFHEWwZ45"},"outputs":[],"source":["# For this code block, you need to have `month` and `weekday`\n","# in your runtime from previous exercises.\n","\n","def create_date_format(weekday_formatter=str, month_formatter=str):\n"," '''\n"," Creates a function that formats dates.\n","\n"," Input:\n"," - weekday_formatter: a function that converts the day of the week (number) to a string\n"," - month_formatter: a function that converts the month (number) to a string\n","\n"," If these functions are not supplied, the month and day are shown as-is.\n","\n"," Output: A function that takes integers for the year, month, day of the\n"," month, and day of the week, and returns a string with the formatted date.\n"," '''\n","\n"," def format_date(year, month, day_of_month, day_of_week):\n"," '''\n"," Format a date in a nice string.\n"," '''\n","\n"," day_string = weekday_formatter(day_of_week)\n"," month_string = month_formatter(month)\n","\n"," nice_string = day_string + ', ' + month_string + ' ' + str(day_of_month) + ', ' + str(year)\n"," return nice_string\n"," \n"," return format_date\n","\n","# Your definition of create_date_format here\n","\n","wordy_format = create_date_format(weekday, month)\n","cryptic_format = create_date_format()\n","\n","assert wordy_format(2023, 4, 11, 2) == 'Tuesday, April 11, 2023'\n","assert cryptic_format(2023, 4, 11, 2) == '2, 4 11, 2023'"]},{"cell_type":"markdown","metadata":{"id":"b-p6Ct5_0I-x"},"source":["## Exercise 7.5: ultimate FizzBuzz (more bonus)\n","\n","1. Write two functions `fizz(number)` and `buzz(number)`. If `number` is divisible by 3, `fizz` should return the string `'Fizz'`; otherwise, it should return the empty string `''`. `buzz` should work like `fizz`, but for divisibility by 5 and the string `'Buzz'`."]},{"cell_type":"code","execution_count":81,"metadata":{"executionInfo":{"elapsed":11,"status":"ok","timestamp":1681912282529,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"407dPPK966R9"},"outputs":[],"source":["# Your solution here\n","\n","def fizz(number):\n"," '''\n"," Returns 'Fizz' if a number is divisible by 3, otherwise the empty string\n"," '''\n","\n"," if number % 3 == 0:\n"," return 'Fizz'\n"," else:\n"," return ''\n","\n","def buzz(number):\n"," '''\n"," Returns 'Buzz' if a number is divisible by 5, otherwise the empty string\n"," '''\n","\n"," if number % 5 == 0:\n"," return 'Buzz'\n"," else:\n"," return ''\n","\n","for number in [1, 2, 4, 5, 7, 8, 10, 11, 13, 14]:\n"," assert fizz(number) == '', str(number) + ' is not divisible by 3'\n","for number in [3, 6, 9, 12, 15, 18, 21, 24, 27]:\n"," assert fizz(number) == 'Fizz', str(number) + ' is divisible by 3'\n","for number in [1, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14]:\n"," assert buzz(number) == '', str(number) + ' is not divisible by 5'\n","for number in [5, 10, 15, 20, 25, 30, 35, 40]:\n"," assert buzz(number) == 'Buzz', str(number) + ' is divisible by 5'"]},{"cell_type":"markdown","metadata":{"id":"QCKc52-r9DrX"},"source":["2. Using your functions `fizz` and `buzz` from step 1, write the function `fizzbuzz(number)`, which implements the well-known rules from before, but with a slight **twist**:\n"," - If the number is divisible by 3 and 5, return `'FizzBuzz'`.\n"," - If the number is divisible by 3 but not by 5, return `'Fizz'`.\n"," - If the number is divisible by 5 but not by 3, return `'Buzz'`.\n"," - In all other cases, return the number itself **as a string**."]},{"cell_type":"code","execution_count":83,"metadata":{"executionInfo":{"elapsed":3,"status":"ok","timestamp":1681912437888,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"NFADyIW3-7qt"},"outputs":[],"source":["# Your solution here\n","\n","def fizzbuzz(number):\n"," '''\n"," Turns a number into a fizzbuzz string.\n","\n"," - If the number is divisible by 3 and 5, returns 'FizzBuzz'.\n"," - If the number is divisible by 3 but not by 5, returns 'Fizz'.\n"," - If the number is divisible by 5 but not by 3, returns 'Buzz'.\n"," - In all other cases, returns the number itself as a string.\n"," '''\n","\n"," fizzbuzzed = fizz(number) + buzz(number)\n","\n"," if fizzbuzzed:\n"," return fizzbuzzed\n"," else:\n"," return str(number)\n","\n","for number in [1, 2, 4, 7, 8, 11, 13, 14]:\n"," assert fizzbuzz(number) == str(number), str(number)\n","for number in [3, 6, 9, 12, 18, 21, 24, 27]:\n"," assert fizzbuzz(number) == 'Fizz', str(number)\n","for number in [5, 10, 20, 25, 35, 40]:\n"," assert fizzbuzz(number) == 'Buzz', str(number)\n","for number in [15, 30, 45, 60, 75]:\n"," assert fizzbuzz(number) == 'FizzBuzz', str(number)"]},{"cell_type":"markdown","metadata":{"id":"o_3wq4agCCZH"},"source":["3. Write a function `chunk10(sequence)` that takes an iterable `sequence` and returns a list of lists: the first sublist contains the first ten elements of `sequence`, the second sublist contains the next ten elements of `sequence`, and so on. You may assume that the number of values in `sequence` is an exact multiple of 10."]},{"cell_type":"code","execution_count":88,"metadata":{"executionInfo":{"elapsed":8,"status":"ok","timestamp":1681912825413,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"4c0A4kMfDdvt"},"outputs":[],"source":["# Your solution here\n","\n","def chunk10(sequence):\n"," '''\n"," Splits a sequence into chunks of 10 elements\n"," '''\n","\n"," items = list(sequence)\n"," chunks = []\n","\n"," for position in range(0, len(sequence), 10):\n"," chunk = items[position: position + 10]\n"," chunks.append(chunk)\n"," \n"," return chunks\n","\n","assert chunk10('Jelte!!!!!') == [list('Jelte!!!!!')]\n","assert chunk10('Hey Julian, let us have lunch!') == [\n"," list('Hey Julian'),\n"," list(', let us h'),\n"," list('ave lunch!'),\n","]\n","assert chunk10(range(20)) == [list(range(10)), list(range(10, 20))]"]},{"cell_type":"markdown","metadata":{"id":"HBA4z4yVIhsn"},"source":["4. In step 1, you may have written definitions for `fizz` and `buzz` that look very similar to each other. Can you think of a way to avoid repeating the common elements?"]},{"cell_type":"code","execution_count":91,"metadata":{"executionInfo":{"elapsed":9,"status":"ok","timestamp":1681913046725,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"5g4BRdpLJLIc"},"outputs":[],"source":["# Your solution here\n","\n","def fizz_or_buzz(number, message='Fizz', denominator=3):\n"," '''\n"," If number is divisible by denominator, return the message. Otherwise, return the empty string.\n"," '''\n"," if number % denominator == 0:\n"," return message\n"," else:\n"," return ''\n","\n","def fizz(number):\n"," return fizz_or_buzz(number)\n","\n","def buzz(number):\n"," return fizz_or_buzz(number, 'Buzz', 5)\n","\n","for number in [1, 2, 4, 5, 7, 8, 10, 11, 13, 14]:\n"," assert fizz(number) == '', str(number) + ' is not divisible by 3'\n","for number in [3, 6, 9, 12, 15, 18, 21, 24, 27]:\n"," assert fizz(number) == 'Fizz', str(number) + ' is divisible by 3'\n","for number in [1, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14]:\n"," assert buzz(number) == '', str(number) + ' is not divisible by 5'\n","for number in [5, 10, 15, 20, 25, 30, 35, 40]:\n"," assert buzz(number) == 'Buzz', str(number) + ' is divisible by 5'"]},{"cell_type":"markdown","metadata":{"id":"BXzFYNGmPeO6"},"source":["5. In exercise 7.1.3, you documented the function `join_commas`. We did not actually need to write that function, because it is built into Python, although it goes by the name `', '.join`. That notation also works with other strings, as we demonstrate below. We also remind you of `map`, which appeared in the lecture.

\n","Using these functions, as well as your own `fizzbuzz` from step 2 and your own `chunk10` from step 3, try to recreate (roughly) the same `FizzBuzz` output for the numbers 1 through 100 as in [exercise 6.2.4](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV#scrollTo=uyqbuhKsUlhG), in as little code as you can."]},{"cell_type":"code","execution_count":92,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":597,"status":"ok","timestamp":1681913126765,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"nDGUS26IMApD","outputId":"a78e10b9-f6d3-433f-f06c-d78ee65f65e4"},"outputs":[{"name":"stdout","output_type":"stream","text":["apricot, banana, cherry\n","apricot\n","banana\n","cherry\n","[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n","['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']\n"]}],"source":["# The following code is just for illustration.\n","# You do not need it in your solution.\n","fruits = ['apricot', 'banana', 'cherry']\n","print(', '.join(fruits))\n","print('\\n'.join(fruits))\n","numbers = list(range(10))\n","print(numbers)\n","strings = map(str, numbers)\n","print(list(strings))"]},{"cell_type":"code","execution_count":97,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":12,"status":"ok","timestamp":1681913265047,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"},"user_tz":-120},"id":"G87eCYM61P3a","outputId":"23e8acb6-b66b-4871-e44a-8d37413bd1dc"},"outputs":[{"name":"stdout","output_type":"stream","text":["FizzBuzz, 1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz\n","Buzz, 11, Fizz, 13, 14, FizzBuzz, 16, 17, Fizz, 19\n","Buzz, Fizz, 22, 23, Fizz, Buzz, 26, Fizz, 28, 29\n","FizzBuzz, 31, 32, Fizz, 34, Buzz, Fizz, 37, 38, Fizz\n","Buzz, 41, Fizz, 43, 44, FizzBuzz, 46, 47, Fizz, 49\n","Buzz, Fizz, 52, 53, Fizz, Buzz, 56, Fizz, 58, 59\n","FizzBuzz, 61, 62, Fizz, 64, Buzz, Fizz, 67, 68, Fizz\n","Buzz, 71, Fizz, 73, 74, FizzBuzz, 76, 77, Fizz, 79\n","Buzz, Fizz, 82, 83, Fizz, Buzz, 86, Fizz, 88, 89\n","FizzBuzz, 91, 92, Fizz, 94, Buzz, Fizz, 97, 98, Fizz\n"]}],"source":["chunks = chunk10(range(100))\n","\n","for chunk in chunks:\n"," fizzbuzzed = map(fizzbuzz, chunk)\n"," print(', '.join(fizzbuzzed))"]},{"cell_type":"markdown","metadata":{"id":"Dntbbioh29xm"},"source":["## Next module\n","\n","[8. Debugging](https://colab.research.google.com/drive/1yQskT6SyKvXtXewx5kCla2NOmasgP8Vi)"]}],"metadata":{"colab":{"provenance":[{"file_id":"1APhegD_KpLRFn5bC6Ater2wFpT8_Opfr","timestamp":1681904575537}],"toc_visible":true},"kernelspec":{"display_name":"Python 3","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.10.12"}},"nbformat":4,"nbformat_minor":0} +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "fqMJHzNk5yXQ" + }, + "source": [ + "# Module 7: Functions\n", + "\n", + "### CDH course \"Programming in Python\"\n", + "\n", + "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "\n", + "Previous module: [6. Loops](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV)\n", + "\n", + "### This module\n", + "\n", + "- Storing code in a variable so you can reuse it.\n", + "- Being explicit about the purpose of your code." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "aPFGhEVz40JP" + }, + "source": [ + "## Exercise 7.1: functions" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "hfcz-cSEKZWW" + }, + "source": [ + "1. We have seen several standard functions during the course, such as `ord` and `len`. List as many as you can from memory. Try to list the types of the parameters and return values of each function as well. Do any of these functions have side effects?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ZG0lv6MhVP8L" + }, + "source": [ + "Some examples:\n", + "- `ord`: Has 1 parameter: a string of 1 character. The return value is an integer. No side effects.\n", + "- `len`: Has 1 parameter: a list, tuple or other iterable. The return value is an integer. No side effects.\n", + "- `input`: Has 1 optional parameter `prompt`, which should be a string. The function has a side effect, which is to wait for the user to type something. Its return value is a string (namely the text that you have entered)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BUnMsiUzKbws" + }, + "source": [ + "2. For exercise purposes, code in the following blocks may call `print` inside a function and omit a docstring. Predict the output, then run the code to see whether your prediction was right. Try to explain any discrepancies." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 366, + "status": "ok", + "timestamp": 1681904959646, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "_3va9jT5O0H7", + "outputId": "0187c43c-58c4-45c6-935f-a22d2bf5c864" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello, Berit!\n" + ] + } + ], + "source": [ + "def greet(name):\n", + " return 'Hello, ' + name + '!'\n", + "\n", + "print(greet('Berit'))" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "executionInfo": { + "elapsed": 5, + "status": "ok", + "timestamp": 1681905000500, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "vdTIwoGxM_JV" + }, + "outputs": [], + "source": [ + "name = 'Luka'\n", + "\n", + "def exclaim(name):\n", + " print(name + '!')\n", + "\n", + "# nothing happens: we have created a function but we haven't called it!" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 5, + "status": "ok", + "timestamp": 1681905065618, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "30fv8SAMOblV", + "outputId": "99fd2054-9609-4646-c321-2f4513b6cc5f" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "False\n" + ] + } + ], + "source": [ + "def false():\n", + " return True\n", + "\n", + "print(False)\n", + "\n", + "# we are printing the literal value `False`, which is not the same as our function `false` (all lower case)\n", + "# Capitalisation matters in python!" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 13, + "status": "ok", + "timestamp": 1681905150776, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "ongrNaZFNNmV", + "outputId": "45d7e513-7157-4c54-ab92-c4380fb59f02" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "None\n" + ] + } + ], + "source": [ + "length = 5\n", + "width = 2\n", + "\n", + "def calculate_area():\n", + " area = length * width\n", + "\n", + "print(calculate_area())\n", + "\n", + "# calculate_area calculates something, but it does not make a return statement.\n", + "# So the returned value of `calculate_area()` is None" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 35 + }, + "executionInfo": { + "elapsed": 378, + "status": "ok", + "timestamp": 1681905166543, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "MSkOCMMyNoUO", + "outputId": "a9fca128-9415-4ef7-97cb-1546e36f67db" + }, + "outputs": [ + { + "data": { + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + }, + "text/plain": [ + "'Who is Jelte?'" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def question(name):\n", + " return 'Who is ' + name + '?'\n", + "\n", + "question('Jelte')" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 6, + "status": "ok", + "timestamp": 1681905320174, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "72DDRhD5OQ0g", + "outputId": "3cd844dd-d4ca-4418-db9e-8c3065a9d3b7" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "sweetadditionsweetaddition\n" + ] + } + ], + "source": [ + "def add(left, right):\n", + " return left + right\n", + "\n", + "print(add('sweet', 'addition') * add(1, 1))\n", + "\n", + "# add('sweet', 'addition') will return 'sweet' + addition', which concatenates\n", + "# the strings to 'sweetaddition'\n", + "\n", + "# add(1, 1) becomes 1 + 1, so 2\n", + "\n", + "# then 'sweetaddition' * 2 means \"repeat 'sweetaddition' twice\"\n", + "# hence 'sweetadditionsweetaddition'" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 360, + "status": "ok", + "timestamp": 1681906368916, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "L0GlJecf8ntf", + "outputId": "893d9d77-378d-4d5c-8a17-fa14fdbd0322" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Julian\n", + "Julian\n", + "\n", + "Julian\n", + "Jelte\n", + "Berit\n", + "36\n", + "Julian\n", + "36\n" + ] + } + ], + "source": [ + "# I've added some comments to each print statement, to explain what's happening and in what order.\n", + "\n", + "name = 'Julian'\n", + "age = 36\n", + "\n", + "print(name) # 1st print\n", + "\n", + "# make a function. the code in the body will not be executed until we call the function below.\n", + "def example(name):\n", + " print(name) # 5th print: we are printing the *parameter* `name` that we\n", + " # passed on to the function when we called it, which is 'Jelte'. There is a \n", + " # global parameter which is also called `name` (with value 'Julian'), but\n", + " # the parameter takes precedence.\n", + " \n", + " name = 'Berit' # update the value of the *parameter*\n", + "\n", + " print(name) # 6th print: print the new value of the parameter\n", + "\n", + " print(age) # 7th print: print age. There is no `age` variable specific\n", + " # to the function, so we refer to the global variable (36)\n", + "\n", + "print(name) # 2nd print: name is unchanged. `name = 'Berit'` was part of the\n", + "# function body, it's not yet executed\n", + "\n", + "print(example) # 3rd print: we are printing the function itself, not the\n", + "# returned value!\n", + "\n", + "print(name) # 4th print: name is still unchanged.\n", + "\n", + "example('Jelte') # we are running the function here, and now we will execute the\n", + "# print statements within its body\n", + "\n", + "print(name) # 8th print: the function `example` did some stuff with its\n", + "# parameter called name, but did not do anything with the global variable name.\n", + "# So `name` is still 'Julian'\n", + "\n", + "print(age) # 9th print\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Rwvwlpp0-Hrt" + }, + "source": [ + "3. In each of the following code blocks, write a docstring for the function, then add assertions and run the block to check that your documentation is correct." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "executionInfo": { + "elapsed": 366, + "status": "ok", + "timestamp": 1681906464925, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "ajcRnvzQQ9c5" + }, + "outputs": [], + "source": [ + "def odd(number):\n", + " '''\n", + " Checks whether a number is odd\n", + "\n", + " Input should be an integer. Output is a boolean, True if the number is not\n", + " divisible by two.\n", + " '''\n", + " return number % 2 == 1\n", + "\n", + "assert odd(1) == True\n", + "assert odd(6) == False\n", + "assert odd(0) == False" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "executionInfo": { + "elapsed": 393, + "status": "ok", + "timestamp": 1681906766119, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "giU_bIKrRME4" + }, + "outputs": [], + "source": [ + "def magic(word):\n", + " '''\n", + " Checks that the input is a nonempty string and starts with a capital letter.\n", + "\n", + " Input can be of any type: anything other than a string will return False.\n", + " For strings, checks that the string is not empty and the first character\n", + " is an uppercase latin character. \n", + " '''\n", + " if not word or type(word) != str:\n", + " return False\n", + " if 'A' <= word[0] <= 'Z':\n", + " return True\n", + " return False\n", + "\n", + "assert magic(123) == False\n", + "assert magic('Hello python programmers!') == True\n", + "assert magic('python python python') == False\n", + "assert magic('🤔') == False\n", + "assert magic('') == False" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "executionInfo": { + "elapsed": 8, + "status": "ok", + "timestamp": 1681907208099, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "pAChbRWn-SKS" + }, + "outputs": [], + "source": [ + "def join_commas(words):\n", + " '''\n", + " Joins a iterable of strings into a single string, with ', ' between each item.\n", + "\n", + " Input should be an iterable of strings with at least 1 item.\n", + " '''\n", + " first, *rest = list(words)\n", + " text = first\n", + " for word in rest:\n", + " text = text + ', ' + word\n", + " return text\n", + "\n", + "assert join_commas(['a', 'b', 'c']) == 'a, b, c'\n", + "assert join_commas(['a']) == 'a'\n", + "assert join_commas(('a', 'b', 'c')) == 'a, b, c'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "mc9RtAeATiHw" + }, + "source": [ + "4. Write a function `is_number` that takes one argument. If the argument is a number, it should return `True`; otherwise, it should return `False`. Make sure to include a docstring!" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "executionInfo": { + "elapsed": 399, + "status": "ok", + "timestamp": 1681907269669, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "Ap33-rF-UbsB" + }, + "outputs": [], + "source": [ + "# Your definition of is_number here\n", + "\n", + "def is_number(value):\n", + " '''\n", + " Checks if the input is a number (int or float)\n", + " '''\n", + "\n", + " number_types = [int, float]\n", + " return type(value) in number_types\n", + "\n", + "# The following lines will check your solution (no output is good)\n", + "assert is_number(0)\n", + "assert is_number(10)\n", + "assert is_number(0.5)\n", + "assert is_number(8 / 5)\n", + "assert not is_number(None)\n", + "assert not is_number('dear')\n", + "assert not is_number('123')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "apA7o120TYRl" + }, + "source": [ + "## Exercise 7.2: bonus" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "nM43w3VlB3-O" + }, + "source": [ + "1. Write a function `last_a_index` that, given a string, returns the **last** index of the letter `'a'` in the string. If there is no `'a'` in the string, the function should return `None`." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "executionInfo": { + "elapsed": 10, + "status": "ok", + "timestamp": 1681907469796, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "I5s4_a53ENJC" + }, + "outputs": [], + "source": [ + "# Define last_a_index here\n", + "\n", + "def last_a_index(text):\n", + " '''\n", + " Gives the index of the last 'a' in the string.\n", + "\n", + " If there is no 'a' in the string, returns None\n", + " '''\n", + "\n", + " a_index = None\n", + "\n", + " for (index, character) in enumerate(text):\n", + " if character == 'a':\n", + " a_index = index\n", + " \n", + " return a_index\n", + "\n", + "assert last_a_index('banana') == 5\n", + "assert last_a_index('cherry') == None\n", + "assert last_a_index('Once upon a time, there was a dragon') == 32" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "z4z3-dOaVROx" + }, + "source": [ + "2. Write a function `replace` with three string arguments: `text`, `query` and `replacement`. It should return a copy of `text` in which all occurrences of `query` are replaced by `replacement`. For example, `replace('small', 'a', 'e')` should return `'smell'`. You may assume that `query` is always a single character. For an additional challenge, you can try to also handle multi-character query strings. For ultimate challenge, try to think of a way to handle the empty string!" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "executionInfo": { + "elapsed": 2, + "status": "ok", + "timestamp": 1681907653952, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "kyQwfz-mYvfW" + }, + "outputs": [], + "source": [ + "# Define replace here\n", + "\n", + "# solution one: does not work with multi-character strings\n", + "def replace(text, query, replacement):\n", + " '''\n", + " Replace each occurence of a character in a string\n", + " \n", + " Input:\n", + " - text: a string in which you want to replace something\n", + " - query: the character you wish to replace\n", + " - replacement: the string that should replace `query`\n", + " '''\n", + "\n", + " replaced = ''\n", + "\n", + " for character in text:\n", + " if character == query:\n", + " replaced = replaced + replacement\n", + " else:\n", + " replaced = replaced + character\n", + " \n", + " return replaced\n", + "\n", + "assert replace('small', 'a', 'e') == 'smell'\n", + "assert replace('banana', 'a', 'o') == 'bonono'\n", + "assert replace('cherry', 'a', 'x') == 'cherry'" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "executionInfo": { + "elapsed": 350, + "status": "ok", + "timestamp": 1681908669621, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "2ajcWTx1gi2r" + }, + "outputs": [], + "source": [ + "# solution 2: handle multi-character strings and empty strings\n", + "\n", + "# There no clear answer for what should happen in the case of empty strings!\n", + "# It's a question that does not really make sense: replace every insstance of\n", + "# nothing?\n", + "\n", + "# Here we just return `None` when query is empty. You could also return the\n", + "# original string.\n", + "\n", + "def replace(text, query, replacement):\n", + " '''\n", + " Replace each occurence of a substring in a string\n", + " \n", + " Input:\n", + " - text: a string in which you want to replace something\n", + " - query: the string you wish to replace\n", + " - replacement: the string that should replace `query`\n", + "\n", + " If the query is empty, returns None.\n", + " '''\n", + "\n", + " if not query:\n", + " return None\n", + " \n", + " size = len(query)\n", + " new_text = '' # where we build our new text\n", + " position = 0 # our position as we move through the text\n", + "\n", + " while position < len(text):\n", + " possible_match = text[position:position+size] # slice of text from here, the same size as the query\n", + " if possible_match == query: #if this matches...\n", + " new_text += replacement # put the replacement in our new string\n", + " position += size # and skip ahead until after this slice\n", + " else:\n", + " # if it doesn't match, move ahead by one character\n", + " new_text += text[position]\n", + " position += 1\n", + " \n", + " return new_text\n", + " \n", + "\n", + "assert replace('small', 'a', 'e') == 'smell'\n", + "assert replace('banana', 'a', 'o') == 'bonono'\n", + "assert replace('cherry', 'a', 'x') == 'cherry'\n", + "assert replace('small', '', '?') == None\n", + "assert replace('banana', 'an', 'ab') == 'bababa'\n", + "assert replace('banana', 'banana', 'apple') == 'apple' \n", + "assert replace('banana', 'e', 'o') == 'banana'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1xUD2ZNjcqEZ" + }, + "source": [ + "## Exercise 7.3: function tricks" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "hxRC5Nx-cyM4" + }, + "source": [ + "1. In each of the code blocks below, predict the output, check your prediction by running the code, and try to explain any surprises." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 392, + "status": "ok", + "timestamp": 1681908707519, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "HPxio0q6c-M9", + "outputId": "8542c5de-15ae-4841-b438-1211d999241f" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Welcome to my office. How can I help you?\n", + "Please make yourself at home, Sheean!\n" + ] + } + ], + "source": [ + "def welcome(name=None):\n", + " if name is None:\n", + " return 'Welcome to my office. How can I help you?'\n", + " return 'Please make yourself at home, ' + name + '!'\n", + "\n", + "print(welcome()) # default option\n", + "print(welcome('Sheean'))" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 6, + "status": "ok", + "timestamp": 1681908774383, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "NxXqK4ROdxva", + "outputId": "08744e41-cbe2-4c45-9566-9174466aaf88" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "15\n" + ] + } + ], + "source": [ + "def table(number):\n", + " return number, number * 2, number * 3 #return three numbers\n", + "\n", + "first, second, third = table(5) # table(5) is (5, 10, 15) ; unpack these into three variables\n", + "\n", + "print(third) #print the third" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 14, + "status": "ok", + "timestamp": 1681908875617, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "EVCf3w9qfBCU", + "outputId": "8883c815-c80f-4f0a-f185-4d6d935e54cb" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "For naming, contact Job.\n" + ] + } + ], + "source": [ + "def contact(name, job):\n", + " return 'For ' + job + ', contact ' + name + '.'\n", + "\n", + "contact_line = contact(job='naming', name='Job') # we use named arguments, so we can supply `name` and `job` in any order\n", + "print(contact_line)" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 4, + "status": "ok", + "timestamp": 1681909054548, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "OUSpe-hPl6-G", + "outputId": "fa03386e-6570-43ce-a65b-424d5cbfb193" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "As my mother would say: Julian?\n" + ] + } + ], + "source": [ + "def exclaim(name):\n", + " return name + '!'\n", + "\n", + "def request(name):\n", + " return name + '?'\n", + "\n", + "def tell(how):\n", + " return 'As my mother would say: ' + how('Julian')\n", + "\n", + "print(tell(request))\n", + "\n", + "# tell(request) will return `'As my mother would say: ' + how('Julian')`\n", + "# `how` is whatever we pass on as the argument to `tell`, in this case the function `request`\n", + "# so we can fill in `how('Julian')` as `request('Julian')`\n", + "# now we can look at the body of `request` to see what it does (it puts a\n", + "# question mark behind the name)" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 9, + "status": "ok", + "timestamp": 1681909174217, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "uSN17qnvmoWv", + "outputId": "f36d7049-e723-4036-e0b3-4b48a9b2103f" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "apricot; banana; cherry\n" + ] + } + ], + "source": [ + "def make_enumerate(separator):\n", + " def enumerate(items):\n", + " if not len(items):\n", + " return ''\n", + " result = items[0]\n", + " for item in items[1:]:\n", + " result = result + separator + item\n", + " return result\n", + " return enumerate\n", + "\n", + "with_semicolon = make_enumerate('; ')\n", + "fruits = with_semicolon(['apricot', 'banana', 'cherry'])\n", + "print(fruits)\n", + "\n", + "# with_semicolon is the output of make_enumerate: another function.\n", + "# This function takes a list of strings as input and joins them with a semicolon\n", + "# between them. we can then call `with_semicolon` on the list of fruits." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "CKma4p6Egkwb" + }, + "source": [ + "2. In each of the following code blocks, something is missing in the function definition. Add the missing element." + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "executionInfo": { + "elapsed": 6, + "status": "ok", + "timestamp": 1681909191045, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "SaygnjMygwNF" + }, + "outputs": [], + "source": [ + "def combine(left, right):\n", + " \"\"\" Compute the sum and the product of the arguments. \"\"\"\n", + " sum = left + right\n", + " product = left * right\n", + " return sum, product #return both the sum and the product\n", + "\n", + "assert combine(2, 3) == (5, 6)" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "executionInfo": { + "elapsed": 6, + "status": "ok", + "timestamp": 1681909211878, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "GqIgTcG6hMuG" + }, + "outputs": [], + "source": [ + "def announce_time(hour, minute=0): # minute should be optional, so we add a default value.\n", + " \"\"\" Announce the time in a speaking clock manner. \"\"\"\n", + " if minute < 10:\n", + " minute = '0' + str(minute)\n", + " time = str(hour) + ':' + str(minute)\n", + " return 'At the next beep, the time will be ' + time + '. BEEP!'\n", + "\n", + "assert announce_time(12) == 'At the next beep, the time will be 12:00. BEEP!'" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 395, + "status": "ok", + "timestamp": 1681909264360, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "nH77gJdOkS5b", + "outputId": "f70a5f41-1916-4deb-da38-248ea056a749" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "holiday\n" + ] + } + ], + "source": [ + "def echo(value):\n", + " print(value)\n", + " return value\n", + "\n", + "assert echo('holiday') == 'holiday'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "YG5XrO1PoIJx" + }, + "source": [ + "3. In the following code block, write a function `month`, which returns a string name for given numeric month. Month `1` is `'January'` and month `12` is `December`. When no argument is passed, it should default to the current month. **Tip:** you can avoid writing a big `if`/`elif`/`else` tree by using a list of month names and using the month number as a list index." + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": { + "executionInfo": { + "elapsed": 662, + "status": "ok", + "timestamp": 1681911393494, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "TnuU_I0Tq9wQ" + }, + "outputs": [], + "source": [ + "# You may pretend that it is forever April\n", + "current_month = 4\n", + "\n", + "months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']\n", + "\n", + "def month(number=current_month):\n", + " '''\n", + " Return the name of a month based on an number.\n", + "\n", + " Input should be an integer between 1 and 12. Returns None if this is not the case.\n", + " If no month number is given, defaults to April.\n", + "\n", + " Returns a string with the name of the Month.\n", + " '''\n", + "\n", + " if type(number) is not int or number < 1 or number > 12:\n", + " return None\n", + "\n", + " index = number - 1\n", + " return months[index]\n", + "\n", + "# Your definition of month here\n", + "\n", + "assert month(3) == 'March'\n", + "assert month(4) == 'April'\n", + "assert month(11) == 'November'\n", + "assert month() == 'April'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "WuRrElhUsD40" + }, + "source": [ + "4. In the following code block, write a function `weekday`, which is analogous to `month` in the previous exercise. Day `1` is `'Monday'` while day `0` and day `7` are both `'Sunday'`. Can you avoid writing the string `'Sunday'` twice in your code?" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": { + "executionInfo": { + "elapsed": 8, + "status": "ok", + "timestamp": 1681911394923, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "WUGQqmJysrqS" + }, + "outputs": [], + "source": [ + "# You may pretend it is forever Tuesday\n", + "current_weekday = 2\n", + "\n", + "weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']\n", + "\n", + "def weekday(number=current_weekday):\n", + " '''\n", + " Return the name of a weekday based on an number.\n", + "\n", + " Input should be an integer between 0 and 7. Returns None if this is not the case.\n", + " If no weekday number is given, defaults to Tuesday.\n", + "\n", + " Returns a string with the name of the weekday. Day 1 is 'Monday' while day 0\n", + " and day 7 are both 'Sunday'.\n", + " '''\n", + " if type(number) is not int or number < 0 or number > 7:\n", + " return None\n", + " \n", + " index = (number - 1) % 7\n", + " return weekdays[index]\n", + "\n", + "# Your definition of weekday here\n", + "\n", + "assert weekday() == 'Tuesday'\n", + "assert weekday(0) == 'Sunday'\n", + "assert weekday(7) == 'Sunday'\n", + "assert weekday(4) == 'Thursday'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ZvfEq3NctoOo" + }, + "source": [ + "## Exercise 7.4: bonus" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8au2fNRutw8i" + }, + "source": [ + "1. In exercises 7.3.4 and 7.3.5, where you just implemented `month` and `weekday`, your code was likely very similar. How could you implement both functions without repeating yourself? Check your solution with assertions." + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "metadata": { + "executionInfo": { + "elapsed": 444, + "status": "ok", + "timestamp": 1681911460720, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "JFhOX_Z5uVfC" + }, + "outputs": [], + "source": [ + "def make_namer(names, default):\n", + " '''\n", + " Make a function to give names (months, weekdays, etc) based on a number.\n", + "\n", + " Input:\n", + " - A list of names (strings)\n", + " - A default input value for the function\n", + "\n", + " Returns a function that takes a number and returns the corresponding name.\n", + " 1 is the first list in the name, etc.\n", + " '''\n", + " size = len(names)\n", + "\n", + " def name(number=default):\n", + " '''\n", + " Gives the name corresponding to a number\n", + " '''\n", + " if type(number) is not int or number < 0 or number > size:\n", + " return None\n", + " \n", + " index = (number - 1) % size\n", + " return names[index]\n", + " \n", + " return name\n", + "\n", + "# now we can make our month and weekday functions based on make_namer\n", + "\n", + "current_month = 4\n", + "months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']\n", + "\n", + "current_weekday = 2\n", + "weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']\n", + "\n", + "month = make_namer(months, current_month)\n", + "weekday = make_namer(weekdays, current_weekday)\n", + "\n", + "# repeat the assert statements above to show everything still works\n", + "\n", + "assert month(3) == 'March'\n", + "assert month(4) == 'April'\n", + "assert month(11) == 'November'\n", + "assert month() == 'April'\n", + "\n", + "assert weekday() == 'Tuesday'\n", + "assert weekday(0) == 'Sunday'\n", + "assert weekday(7) == 'Sunday'\n", + "assert weekday(4) == 'Thursday'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Gx54DrgJuWKg" + }, + "source": [ + "2. Write a function `create_date_format`, which takes two arguments that determine how a date should be displayed. The first argument is function that transforms a weekday number to a string suitable for display, the second argument is a function that transforms a month number. Both arguments default to just displaying the number as-is. `create_date_format` returns a new function. The latter function takes any date defined by a year, month, day and weekday, and returns a string suitable for displaying that date as a whole." + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": { + "executionInfo": { + "elapsed": 9, + "status": "ok", + "timestamp": 1681912144802, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "0KyXFHEWwZ45" + }, + "outputs": [], + "source": [ + "# For this code block, you need to have `month` and `weekday`\n", + "# in your runtime from previous exercises.\n", + "\n", + "def create_date_format(weekday_formatter=str, month_formatter=str):\n", + " '''\n", + " Creates a function that formats dates.\n", + "\n", + " Input:\n", + " - weekday_formatter: a function that converts the day of the week (number) to a string\n", + " - month_formatter: a function that converts the month (number) to a string\n", + "\n", + " If these functions are not supplied, the month and day are shown as-is.\n", + "\n", + " Output: A function that takes integers for the year, month, day of the\n", + " month, and day of the week, and returns a string with the formatted date.\n", + " '''\n", + "\n", + " def format_date(year, month, day_of_month, day_of_week):\n", + " '''\n", + " Format a date in a nice string.\n", + " '''\n", + "\n", + " day_string = weekday_formatter(day_of_week)\n", + " month_string = month_formatter(month)\n", + "\n", + " nice_string = day_string + ', ' + month_string + ' ' + str(day_of_month) + ', ' + str(year)\n", + " return nice_string\n", + " \n", + " return format_date\n", + "\n", + "# Your definition of create_date_format here\n", + "\n", + "wordy_format = create_date_format(weekday, month)\n", + "cryptic_format = create_date_format()\n", + "\n", + "assert wordy_format(2023, 4, 11, 2) == 'Tuesday, April 11, 2023'\n", + "assert cryptic_format(2023, 4, 11, 2) == '2, 4 11, 2023'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "b-p6Ct5_0I-x" + }, + "source": [ + "## Exercise 7.5: ultimate FizzBuzz (more bonus)\n", + "\n", + "1. Write two functions `fizz(number)` and `buzz(number)`. If `number` is divisible by 3, `fizz` should return the string `'Fizz'`; otherwise, it should return the empty string `''`. `buzz` should work like `fizz`, but for divisibility by 5 and the string `'Buzz'`." + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": { + "executionInfo": { + "elapsed": 11, + "status": "ok", + "timestamp": 1681912282529, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "407dPPK966R9" + }, + "outputs": [], + "source": [ + "# Your solution here\n", + "\n", + "def fizz(number):\n", + " '''\n", + " Returns 'Fizz' if a number is divisible by 3, otherwise the empty string\n", + " '''\n", + "\n", + " if number % 3 == 0:\n", + " return 'Fizz'\n", + " else:\n", + " return ''\n", + "\n", + "def buzz(number):\n", + " '''\n", + " Returns 'Buzz' if a number is divisible by 5, otherwise the empty string\n", + " '''\n", + "\n", + " if number % 5 == 0:\n", + " return 'Buzz'\n", + " else:\n", + " return ''\n", + "\n", + "for number in [1, 2, 4, 5, 7, 8, 10, 11, 13, 14]:\n", + " assert fizz(number) == '', str(number) + ' is not divisible by 3'\n", + "for number in [3, 6, 9, 12, 15, 18, 21, 24, 27]:\n", + " assert fizz(number) == 'Fizz', str(number) + ' is divisible by 3'\n", + "for number in [1, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14]:\n", + " assert buzz(number) == '', str(number) + ' is not divisible by 5'\n", + "for number in [5, 10, 15, 20, 25, 30, 35, 40]:\n", + " assert buzz(number) == 'Buzz', str(number) + ' is divisible by 5'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "QCKc52-r9DrX" + }, + "source": [ + "2. Using your functions `fizz` and `buzz` from step 1, write the function `fizzbuzz(number)`, which implements the well-known rules from before, but with a slight **twist**:\n", + " - If the number is divisible by 3 and 5, return `'FizzBuzz'`.\n", + " - If the number is divisible by 3 but not by 5, return `'Fizz'`.\n", + " - If the number is divisible by 5 but not by 3, return `'Buzz'`.\n", + " - In all other cases, return the number itself **as a string**." + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": { + "executionInfo": { + "elapsed": 3, + "status": "ok", + "timestamp": 1681912437888, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "NFADyIW3-7qt" + }, + "outputs": [], + "source": [ + "# Your solution here\n", + "\n", + "def fizzbuzz(number):\n", + " '''\n", + " Turns a number into a fizzbuzz string.\n", + "\n", + " - If the number is divisible by 3 and 5, returns 'FizzBuzz'.\n", + " - If the number is divisible by 3 but not by 5, returns 'Fizz'.\n", + " - If the number is divisible by 5 but not by 3, returns 'Buzz'.\n", + " - In all other cases, returns the number itself as a string.\n", + " '''\n", + "\n", + " fizzbuzzed = fizz(number) + buzz(number)\n", + "\n", + " if fizzbuzzed:\n", + " return fizzbuzzed\n", + " else:\n", + " return str(number)\n", + "\n", + "for number in [1, 2, 4, 7, 8, 11, 13, 14]:\n", + " assert fizzbuzz(number) == str(number), str(number)\n", + "for number in [3, 6, 9, 12, 18, 21, 24, 27]:\n", + " assert fizzbuzz(number) == 'Fizz', str(number)\n", + "for number in [5, 10, 20, 25, 35, 40]:\n", + " assert fizzbuzz(number) == 'Buzz', str(number)\n", + "for number in [15, 30, 45, 60, 75]:\n", + " assert fizzbuzz(number) == 'FizzBuzz', str(number)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "o_3wq4agCCZH" + }, + "source": [ + "3. Write a function `chunk10(sequence)` that takes an iterable `sequence` and returns a list of lists: the first sublist contains the first ten elements of `sequence`, the second sublist contains the next ten elements of `sequence`, and so on. You may assume that the number of values in `sequence` is an exact multiple of 10." + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": { + "executionInfo": { + "elapsed": 8, + "status": "ok", + "timestamp": 1681912825413, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "4c0A4kMfDdvt" + }, + "outputs": [], + "source": [ + "# Your solution here\n", + "\n", + "def chunk10(sequence):\n", + " '''\n", + " Splits a sequence into chunks of 10 elements\n", + " '''\n", + "\n", + " items = list(sequence)\n", + " chunks = []\n", + "\n", + " for position in range(0, len(sequence), 10):\n", + " chunk = items[position: position + 10]\n", + " chunks.append(chunk)\n", + " \n", + " return chunks\n", + "\n", + "assert chunk10('Jelte!!!!!') == [list('Jelte!!!!!')]\n", + "assert chunk10('Hey Julian, let us have lunch!') == [\n", + " list('Hey Julian'),\n", + " list(', let us h'),\n", + " list('ave lunch!'),\n", + "]\n", + "assert chunk10(range(20)) == [list(range(10)), list(range(10, 20))]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HBA4z4yVIhsn" + }, + "source": [ + "4. In step 1, you may have written definitions for `fizz` and `buzz` that look very similar to each other. Can you think of a way to avoid repeating the common elements?" + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "metadata": { + "executionInfo": { + "elapsed": 9, + "status": "ok", + "timestamp": 1681913046725, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "5g4BRdpLJLIc" + }, + "outputs": [], + "source": [ + "# Your solution here\n", + "\n", + "def fizz_or_buzz(number, message='Fizz', denominator=3):\n", + " '''\n", + " If number is divisible by denominator, return the message. Otherwise, return the empty string.\n", + " '''\n", + " if number % denominator == 0:\n", + " return message\n", + " else:\n", + " return ''\n", + "\n", + "def fizz(number):\n", + " return fizz_or_buzz(number)\n", + "\n", + "def buzz(number):\n", + " return fizz_or_buzz(number, 'Buzz', 5)\n", + "\n", + "for number in [1, 2, 4, 5, 7, 8, 10, 11, 13, 14]:\n", + " assert fizz(number) == '', str(number) + ' is not divisible by 3'\n", + "for number in [3, 6, 9, 12, 15, 18, 21, 24, 27]:\n", + " assert fizz(number) == 'Fizz', str(number) + ' is divisible by 3'\n", + "for number in [1, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14]:\n", + " assert buzz(number) == '', str(number) + ' is not divisible by 5'\n", + "for number in [5, 10, 15, 20, 25, 30, 35, 40]:\n", + " assert buzz(number) == 'Buzz', str(number) + ' is divisible by 5'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BXzFYNGmPeO6" + }, + "source": [ + "5. In exercise 7.1.3, you documented the function `join_commas`. We did not actually need to write that function, because it is built into Python, although it goes by the name `', '.join`. That notation also works with other strings, as we demonstrate below. We also remind you of `map`, which appeared in the lecture.

\n", + "Using these functions, as well as your own `fizzbuzz` from step 2 and your own `chunk10` from step 3, try to recreate (roughly) the same `FizzBuzz` output for the numbers 1 through 100 as in [exercise 6.2.4](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV#scrollTo=uyqbuhKsUlhG), in as little code as you can." + ] + }, + { + "cell_type": "code", + "execution_count": 92, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 597, + "status": "ok", + "timestamp": 1681913126765, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "nDGUS26IMApD", + "outputId": "a78e10b9-f6d3-433f-f06c-d78ee65f65e4" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "apricot, banana, cherry\n", + "apricot\n", + "banana\n", + "cherry\n", + "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n", + "['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']\n" + ] + } + ], + "source": [ + "# The following code is just for illustration.\n", + "# You do not need it in your solution.\n", + "fruits = ['apricot', 'banana', 'cherry']\n", + "print(', '.join(fruits))\n", + "print('\\n'.join(fruits))\n", + "numbers = list(range(10))\n", + "print(numbers)\n", + "strings = map(str, numbers)\n", + "print(list(strings))" + ] + }, + { + "cell_type": "code", + "execution_count": 97, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 12, + "status": "ok", + "timestamp": 1681913265047, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "G87eCYM61P3a", + "outputId": "23e8acb6-b66b-4871-e44a-8d37413bd1dc" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "FizzBuzz, 1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz\n", + "Buzz, 11, Fizz, 13, 14, FizzBuzz, 16, 17, Fizz, 19\n", + "Buzz, Fizz, 22, 23, Fizz, Buzz, 26, Fizz, 28, 29\n", + "FizzBuzz, 31, 32, Fizz, 34, Buzz, Fizz, 37, 38, Fizz\n", + "Buzz, 41, Fizz, 43, 44, FizzBuzz, 46, 47, Fizz, 49\n", + "Buzz, Fizz, 52, 53, Fizz, Buzz, 56, Fizz, 58, 59\n", + "FizzBuzz, 61, 62, Fizz, 64, Buzz, Fizz, 67, 68, Fizz\n", + "Buzz, 71, Fizz, 73, 74, FizzBuzz, 76, 77, Fizz, 79\n", + "Buzz, Fizz, 82, 83, Fizz, Buzz, 86, Fizz, 88, 89\n", + "FizzBuzz, 91, 92, Fizz, 94, Buzz, Fizz, 97, 98, Fizz\n" + ] + } + ], + "source": [ + "chunks = chunk10(range(100))\n", + "\n", + "for chunk in chunks:\n", + " fizzbuzzed = map(fizzbuzz, chunk)\n", + " print(', '.join(fizzbuzzed))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Dntbbioh29xm" + }, + "source": [ + "## Next module\n", + "\n", + "[8. Debugging](https://colab.research.google.com/drive/1yQskT6SyKvXtXewx5kCla2NOmasgP8Vi)" + ] + } + ], + "metadata": { + "colab": { + "provenance": [ + { + "file_id": "1APhegD_KpLRFn5bC6Ater2wFpT8_Opfr", + "timestamp": 1681904575537 + } + ], + "toc_visible": true + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/solutions/07 Functions solutions.py b/solutions/07 Functions solutions.py new file mode 100644 index 0000000..57d2374 --- /dev/null +++ b/solutions/07 Functions solutions.py @@ -0,0 +1,759 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# name: python3 +# --- + +# %% [markdown] id="fqMJHzNk5yXQ" +# # Module 7: Functions +# +# ### CDH course "Programming in Python" +# +# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# +# Previous module: [6. Loops](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV) +# +# ### This module +# +# - Storing code in a variable so you can reuse it. +# - Being explicit about the purpose of your code. + +# %% [markdown] id="aPFGhEVz40JP" +# ## Exercise 7.1: functions + +# %% [markdown] id="hfcz-cSEKZWW" +# 1. We have seen several standard functions during the course, such as `ord` and `len`. List as many as you can from memory. Try to list the types of the parameters and return values of each function as well. Do any of these functions have side effects? + +# %% [markdown] id="ZG0lv6MhVP8L" +# Some examples: +# - `ord`: Has 1 parameter: a string of 1 character. The return value is an integer. No side effects. +# - `len`: Has 1 parameter: a list, tuple or other iterable. The return value is an integer. No side effects. +# - `input`: Has 1 optional parameter `prompt`, which should be a string. The function has a side effect, which is to wait for the user to type something. Its return value is a string (namely the text that you have entered). + +# %% [markdown] id="BUnMsiUzKbws" +# 2. For exercise purposes, code in the following blocks may call `print` inside a function and omit a docstring. Predict the output, then run the code to see whether your prediction was right. Try to explain any discrepancies. + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 366, "status": "ok", "timestamp": 1681904959646, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="_3va9jT5O0H7" outputId="0187c43c-58c4-45c6-935f-a22d2bf5c864" +def greet(name): + return 'Hello, ' + name + '!' + +print(greet('Berit')) + +# %% executionInfo={"elapsed": 5, "status": "ok", "timestamp": 1681905000500, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="vdTIwoGxM_JV" +name = 'Luka' + +def exclaim(name): + print(name + '!') + +# nothing happens: we have created a function but we haven't called it! + + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 5, "status": "ok", "timestamp": 1681905065618, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="30fv8SAMOblV" outputId="99fd2054-9609-4646-c321-2f4513b6cc5f" +def false(): + return True + +print(False) + +# we are printing the literal value `False`, which is not the same as our function `false` (all lower case) +# Capitalisation matters in python! + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 13, "status": "ok", "timestamp": 1681905150776, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="ongrNaZFNNmV" outputId="45d7e513-7157-4c54-ab92-c4380fb59f02" +length = 5 +width = 2 + +def calculate_area(): + area = length * width + +print(calculate_area()) + +# calculate_area calculates something, but it does not make a return statement. +# So the returned value of `calculate_area()` is None + +# %% colab={"base_uri": "https://localhost:8080/", "height": 35} executionInfo={"elapsed": 378, "status": "ok", "timestamp": 1681905166543, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="MSkOCMMyNoUO" outputId="a9fca128-9415-4ef7-97cb-1546e36f67db" +def question(name): + return 'Who is ' + name + '?' + +question('Jelte') + + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 6, "status": "ok", "timestamp": 1681905320174, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="72DDRhD5OQ0g" outputId="3cd844dd-d4ca-4418-db9e-8c3065a9d3b7" +def add(left, right): + return left + right + +print(add('sweet', 'addition') * add(1, 1)) + +# add('sweet', 'addition') will return 'sweet' + addition', which concatenates +# the strings to 'sweetaddition' + +# add(1, 1) becomes 1 + 1, so 2 + +# then 'sweetaddition' * 2 means "repeat 'sweetaddition' twice" +# hence 'sweetadditionsweetaddition' + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 360, "status": "ok", "timestamp": 1681906368916, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="L0GlJecf8ntf" outputId="893d9d77-378d-4d5c-8a17-fa14fdbd0322" +# I've added some comments to each print statement, to explain what's happening and in what order. + +name = 'Julian' +age = 36 + +print(name) # 1st print + +# make a function. the code in the body will not be executed until we call the function below. +def example(name): + print(name) # 5th print: we are printing the *parameter* `name` that we + # passed on to the function when we called it, which is 'Jelte'. There is a + # global parameter which is also called `name` (with value 'Julian'), but + # the parameter takes precedence. + + name = 'Berit' # update the value of the *parameter* + + print(name) # 6th print: print the new value of the parameter + + print(age) # 7th print: print age. There is no `age` variable specific + # to the function, so we refer to the global variable (36) + +print(name) # 2nd print: name is unchanged. `name = 'Berit'` was part of the +# function body, it's not yet executed + +print(example) # 3rd print: we are printing the function itself, not the +# returned value! + +print(name) # 4th print: name is still unchanged. + +example('Jelte') # we are running the function here, and now we will execute the +# print statements within its body + +print(name) # 8th print: the function `example` did some stuff with its +# parameter called name, but did not do anything with the global variable name. +# So `name` is still 'Julian' + +print(age) # 9th print + + + +# %% [markdown] id="Rwvwlpp0-Hrt" +# 3. In each of the following code blocks, write a docstring for the function, then add assertions and run the block to check that your documentation is correct. + +# %% executionInfo={"elapsed": 366, "status": "ok", "timestamp": 1681906464925, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="ajcRnvzQQ9c5" +def odd(number): + ''' + Checks whether a number is odd + + Input should be an integer. Output is a boolean, True if the number is not + divisible by two. + ''' + return number % 2 == 1 + +assert odd(1) == True +assert odd(6) == False +assert odd(0) == False + + +# %% executionInfo={"elapsed": 393, "status": "ok", "timestamp": 1681906766119, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="giU_bIKrRME4" +def magic(word): + ''' + Checks that the input is a nonempty string and starts with a capital letter. + + Input can be of any type: anything other than a string will return False. + For strings, checks that the string is not empty and the first character + is an uppercase latin character. + ''' + if not word or type(word) != str: + return False + if 'A' <= word[0] <= 'Z': + return True + return False + +assert magic(123) == False +assert magic('Hello python programmers!') == True +assert magic('python python python') == False +assert magic('🤔') == False +assert magic('') == False + + +# %% executionInfo={"elapsed": 8, "status": "ok", "timestamp": 1681907208099, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="pAChbRWn-SKS" +def join_commas(words): + ''' + Joins a iterable of strings into a single string, with ', ' between each item. + + Input should be an iterable of strings with at least 1 item. + ''' + first, *rest = list(words) + text = first + for word in rest: + text = text + ', ' + word + return text + +assert join_commas(['a', 'b', 'c']) == 'a, b, c' +assert join_commas(['a']) == 'a' +assert join_commas(('a', 'b', 'c')) == 'a, b, c' + + +# %% [markdown] id="mc9RtAeATiHw" +# 4. Write a function `is_number` that takes one argument. If the argument is a number, it should return `True`; otherwise, it should return `False`. Make sure to include a docstring! + +# %% executionInfo={"elapsed": 399, "status": "ok", "timestamp": 1681907269669, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="Ap33-rF-UbsB" +# Your definition of is_number here + +def is_number(value): + ''' + Checks if the input is a number (int or float) + ''' + + number_types = [int, float] + return type(value) in number_types + +# The following lines will check your solution (no output is good) +assert is_number(0) +assert is_number(10) +assert is_number(0.5) +assert is_number(8 / 5) +assert not is_number(None) +assert not is_number('dear') +assert not is_number('123') + + +# %% [markdown] id="apA7o120TYRl" +# ## Exercise 7.2: bonus + +# %% [markdown] id="nM43w3VlB3-O" +# 1. Write a function `last_a_index` that, given a string, returns the **last** index of the letter `'a'` in the string. If there is no `'a'` in the string, the function should return `None`. + +# %% executionInfo={"elapsed": 10, "status": "ok", "timestamp": 1681907469796, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="I5s4_a53ENJC" +# Define last_a_index here + +def last_a_index(text): + ''' + Gives the index of the last 'a' in the string. + + If there is no 'a' in the string, returns None + ''' + + a_index = None + + for (index, character) in enumerate(text): + if character == 'a': + a_index = index + + return a_index + +assert last_a_index('banana') == 5 +assert last_a_index('cherry') == None +assert last_a_index('Once upon a time, there was a dragon') == 32 + + +# %% [markdown] id="z4z3-dOaVROx" +# 2. Write a function `replace` with three string arguments: `text`, `query` and `replacement`. It should return a copy of `text` in which all occurrences of `query` are replaced by `replacement`. For example, `replace('small', 'a', 'e')` should return `'smell'`. You may assume that `query` is always a single character. For an additional challenge, you can try to also handle multi-character query strings. For ultimate challenge, try to think of a way to handle the empty string! + +# %% executionInfo={"elapsed": 2, "status": "ok", "timestamp": 1681907653952, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="kyQwfz-mYvfW" +# Define replace here + +# solution one: does not work with multi-character strings +def replace(text, query, replacement): + ''' + Replace each occurence of a character in a string + + Input: + - text: a string in which you want to replace something + - query: the character you wish to replace + - replacement: the string that should replace `query` + ''' + + replaced = '' + + for character in text: + if character == query: + replaced = replaced + replacement + else: + replaced = replaced + character + + return replaced + +assert replace('small', 'a', 'e') == 'smell' +assert replace('banana', 'a', 'o') == 'bonono' +assert replace('cherry', 'a', 'x') == 'cherry' + + +# %% executionInfo={"elapsed": 350, "status": "ok", "timestamp": 1681908669621, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="2ajcWTx1gi2r" +# solution 2: handle multi-character strings and empty strings + +# There no clear answer for what should happen in the case of empty strings! +# It's a question that does not really make sense: replace every insstance of +# # nothing? + +# Here we just return `None` when query is empty. You could also return the +# original string. + +def replace(text, query, replacement): + ''' + Replace each occurence of a substring in a string + + Input: + - text: a string in which you want to replace something + - query: the string you wish to replace + - replacement: the string that should replace `query` + + If the query is empty, returns None. + ''' + + if not query: + return None + + size = len(query) + new_text = '' # where we build our new text + position = 0 # our position as we move through the text + + while position < len(text): + possible_match = text[position:position+size] # slice of text from here, the same size as the query + if possible_match == query: #if this matches... + new_text += replacement # put the replacement in our new string + position += size # and skip ahead until after this slice + else: + # if it doesn't match, move ahead by one character + new_text += text[position] + position += 1 + + return new_text + + +assert replace('small', 'a', 'e') == 'smell' +assert replace('banana', 'a', 'o') == 'bonono' +assert replace('cherry', 'a', 'x') == 'cherry' +assert replace('small', '', '?') == None +assert replace('banana', 'an', 'ab') == 'bababa' +assert replace('banana', 'banana', 'apple') == 'apple' +assert replace('banana', 'e', 'o') == 'banana' + + +# %% [markdown] id="1xUD2ZNjcqEZ" +# ## Exercise 7.3: function tricks + +# %% [markdown] id="hxRC5Nx-cyM4" +# 1. In each of the code blocks below, predict the output, check your prediction by running the code, and try to explain any surprises. + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 392, "status": "ok", "timestamp": 1681908707519, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="HPxio0q6c-M9" outputId="8542c5de-15ae-4841-b438-1211d999241f" +def welcome(name=None): + if name is None: + return 'Welcome to my office. How can I help you?' + return 'Please make yourself at home, ' + name + '!' + +print(welcome()) # default option +print(welcome('Sheean')) + + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 6, "status": "ok", "timestamp": 1681908774383, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="NxXqK4ROdxva" outputId="08744e41-cbe2-4c45-9566-9174466aaf88" +def table(number): + return number, number * 2, number * 3 #return three numbers + +first, second, third = table(5) # table(5) is (5, 10, 15) ; unpack these into three variables + +print(third) #print the third + + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 14, "status": "ok", "timestamp": 1681908875617, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="EVCf3w9qfBCU" outputId="8883c815-c80f-4f0a-f185-4d6d935e54cb" +def contact(name, job): + return 'For ' + job + ', contact ' + name + '.' + +contact_line = contact(job='naming', name='Job') # we use named arguments, so we can supply `name` and `job` in any order +print(contact_line) + + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 4, "status": "ok", "timestamp": 1681909054548, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="OUSpe-hPl6-G" outputId="fa03386e-6570-43ce-a65b-424d5cbfb193" +def exclaim(name): + return name + '!' + +def request(name): + return name + '?' + +def tell(how): + return 'As my mother would say: ' + how('Julian') + +print(tell(request)) + +# tell(request) will return `'As my mother would say: ' + how('Julian')` +# `how` is whatever we pass on as the argument to `tell`, in this case the function `request` +# so we can fill in `how('Julian')` as `request('Julian')` +# now we can look at the body of `request` to see what it does (it puts a +# question mark behind the name) + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 9, "status": "ok", "timestamp": 1681909174217, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="uSN17qnvmoWv" outputId="f36d7049-e723-4036-e0b3-4b48a9b2103f" +def make_enumerate(separator): + def enumerate(items): + if not len(items): + return '' + result = items[0] + for item in items[1:]: + result = result + separator + item + return result + return enumerate + +with_semicolon = make_enumerate('; ') +fruits = with_semicolon(['apricot', 'banana', 'cherry']) +print(fruits) + +# with_semicolon is the output of make_enumerate: another function. +# This function takes a list of strings as input and joins them with a semicolon +# between them. we can then call `with_semicolon` on the list of fruits. + +# %% [markdown] id="CKma4p6Egkwb" +# 2. In each of the following code blocks, something is missing in the function definition. Add the missing element. + +# %% executionInfo={"elapsed": 6, "status": "ok", "timestamp": 1681909191045, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="SaygnjMygwNF" +def combine(left, right): + """ Compute the sum and the product of the arguments. """ + sum = left + right + product = left * right + return sum, product #return both the sum and the product + +assert combine(2, 3) == (5, 6) + + +# %% executionInfo={"elapsed": 6, "status": "ok", "timestamp": 1681909211878, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="GqIgTcG6hMuG" +def announce_time(hour, minute=0): # minute should be optional, so we add a default value. + """ Announce the time in a speaking clock manner. """ + if minute < 10: + minute = '0' + str(minute) + time = str(hour) + ':' + str(minute) + return 'At the next beep, the time will be ' + time + '. BEEP!' + +assert announce_time(12) == 'At the next beep, the time will be 12:00. BEEP!' + + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 395, "status": "ok", "timestamp": 1681909264360, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="nH77gJdOkS5b" outputId="f70a5f41-1916-4deb-da38-248ea056a749" +def echo(value): + print(value) + return value + +assert echo('holiday') == 'holiday' + +# %% [markdown] id="YG5XrO1PoIJx" +# 3. In the following code block, write a function `month`, which returns a string name for given numeric month. Month `1` is `'January'` and month `12` is `December`. When no argument is passed, it should default to the current month. **Tip:** you can avoid writing a big `if`/`elif`/`else` tree by using a list of month names and using the month number as a list index. + +# %% executionInfo={"elapsed": 662, "status": "ok", "timestamp": 1681911393494, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="TnuU_I0Tq9wQ" +# You may pretend that it is forever April +current_month = 4 + +months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] + +def month(number=current_month): + ''' + Return the name of a month based on an number. + + Input should be an integer between 1 and 12. Returns None if this is not the case. + If no month number is given, defaults to April. + + Returns a string with the name of the Month. + ''' + + if type(number) is not int or number < 1 or number > 12: + return None + + index = number - 1 + return months[index] + +# Your definition of month here + +assert month(3) == 'March' +assert month(4) == 'April' +assert month(11) == 'November' +assert month() == 'April' + +# %% [markdown] id="WuRrElhUsD40" +# 4. In the following code block, write a function `weekday`, which is analogous to `month` in the previous exercise. Day `1` is `'Monday'` while day `0` and day `7` are both `'Sunday'`. Can you avoid writing the string `'Sunday'` twice in your code? + +# %% executionInfo={"elapsed": 8, "status": "ok", "timestamp": 1681911394923, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="WUGQqmJysrqS" +# You may pretend it is forever Tuesday +current_weekday = 2 + +weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] + +def weekday(number=current_weekday): + ''' + Return the name of a weekday based on an number. + + Input should be an integer between 0 and 7. Returns None if this is not the case. + If no weekday number is given, defaults to Tuesday. + + Returns a string with the name of the weekday. Day 1 is 'Monday' while day 0 + and day 7 are both 'Sunday'. + ''' + if type(number) is not int or number < 0 or number > 7: + return None + + index = (number - 1) % 7 + return weekdays[index] + +# Your definition of weekday here + +assert weekday() == 'Tuesday' +assert weekday(0) == 'Sunday' +assert weekday(7) == 'Sunday' +assert weekday(4) == 'Thursday' + + +# %% [markdown] id="ZvfEq3NctoOo" +# ## Exercise 7.4: bonus + +# %% [markdown] id="8au2fNRutw8i" +# 1. In exercises 7.3.4 and 7.3.5, where you just implemented `month` and `weekday`, your code was likely very similar. How could you implement both functions without repeating yourself? Check your solution with assertions. + +# %% executionInfo={"elapsed": 444, "status": "ok", "timestamp": 1681911460720, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="JFhOX_Z5uVfC" +def make_namer(names, default): + ''' + Make a function to give names (months, weekdays, etc) based on a number. + + Input: + - A list of names (strings) + - A default input value for the function + + Returns a function that takes a number and returns the corresponding name. + 1 is the first list in the name, etc. + ''' + size = len(names) + + def name(number=default): + ''' + Gives the name corresponding to a number + ''' + if type(number) is not int or number < 0 or number > size: + return None + + index = (number - 1) % size + return names[index] + + return name + +# now we can make our month and weekday functions based on make_namer + +current_month = 4 +months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] + +current_weekday = 2 +weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] + +month = make_namer(months, current_month) +weekday = make_namer(weekdays, current_weekday) + +# repeat the assert statements above to show everything still works + +assert month(3) == 'March' +assert month(4) == 'April' +assert month(11) == 'November' +assert month() == 'April' + +assert weekday() == 'Tuesday' +assert weekday(0) == 'Sunday' +assert weekday(7) == 'Sunday' +assert weekday(4) == 'Thursday' + + +# %% [markdown] id="Gx54DrgJuWKg" +# 2. Write a function `create_date_format`, which takes two arguments that determine how a date should be displayed. The first argument is function that transforms a weekday number to a string suitable for display, the second argument is a function that transforms a month number. Both arguments default to just displaying the number as-is. `create_date_format` returns a new function. The latter function takes any date defined by a year, month, day and weekday, and returns a string suitable for displaying that date as a whole. + +# %% executionInfo={"elapsed": 9, "status": "ok", "timestamp": 1681912144802, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="0KyXFHEWwZ45" +# For this code block, you need to have `month` and `weekday` +# in your runtime from previous exercises. + +def create_date_format(weekday_formatter=str, month_formatter=str): + ''' + Creates a function that formats dates. + + Input: + - weekday_formatter: a function that converts the day of the week (number) to a string + - month_formatter: a function that converts the month (number) to a string + + If these functions are not supplied, the month and day are shown as-is. + + Output: A function that takes integers for the year, month, day of the + month, and day of the week, and returns a string with the formatted date. + ''' + + def format_date(year, month, day_of_month, day_of_week): + ''' + Format a date in a nice string. + ''' + + day_string = weekday_formatter(day_of_week) + month_string = month_formatter(month) + + nice_string = day_string + ', ' + month_string + ' ' + str(day_of_month) + ', ' + str(year) + return nice_string + + return format_date + +# Your definition of create_date_format here + +wordy_format = create_date_format(weekday, month) +cryptic_format = create_date_format() + +assert wordy_format(2023, 4, 11, 2) == 'Tuesday, April 11, 2023' +assert cryptic_format(2023, 4, 11, 2) == '2, 4 11, 2023' + + +# %% [markdown] id="b-p6Ct5_0I-x" +# ## Exercise 7.5: ultimate FizzBuzz (more bonus) +# +# 1. Write two functions `fizz(number)` and `buzz(number)`. If `number` is divisible by 3, `fizz` should return the string `'Fizz'`; otherwise, it should return the empty string `''`. `buzz` should work like `fizz`, but for divisibility by 5 and the string `'Buzz'`. + +# %% executionInfo={"elapsed": 11, "status": "ok", "timestamp": 1681912282529, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="407dPPK966R9" +# Your solution here + +def fizz(number): + ''' + Returns 'Fizz' if a number is divisible by 3, otherwise the empty string + ''' + + if number % 3 == 0: + return 'Fizz' + else: + return '' + +def buzz(number): + ''' + Returns 'Buzz' if a number is divisible by 5, otherwise the empty string + ''' + + if number % 5 == 0: + return 'Buzz' + else: + return '' + +for number in [1, 2, 4, 5, 7, 8, 10, 11, 13, 14]: + assert fizz(number) == '', str(number) + ' is not divisible by 3' +for number in [3, 6, 9, 12, 15, 18, 21, 24, 27]: + assert fizz(number) == 'Fizz', str(number) + ' is divisible by 3' +for number in [1, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14]: + assert buzz(number) == '', str(number) + ' is not divisible by 5' +for number in [5, 10, 15, 20, 25, 30, 35, 40]: + assert buzz(number) == 'Buzz', str(number) + ' is divisible by 5' + + +# %% [markdown] id="QCKc52-r9DrX" +# 2. Using your functions `fizz` and `buzz` from step 1, write the function `fizzbuzz(number)`, which implements the well-known rules from before, but with a slight **twist**: +# - If the number is divisible by 3 and 5, return `'FizzBuzz'`. +# - If the number is divisible by 3 but not by 5, return `'Fizz'`. +# - If the number is divisible by 5 but not by 3, return `'Buzz'`. +# - In all other cases, return the number itself **as a string**. + +# %% executionInfo={"elapsed": 3, "status": "ok", "timestamp": 1681912437888, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="NFADyIW3-7qt" +# Your solution here + +def fizzbuzz(number): + ''' + Turns a number into a fizzbuzz string. + + - If the number is divisible by 3 and 5, returns 'FizzBuzz'. + - If the number is divisible by 3 but not by 5, returns 'Fizz'. + - If the number is divisible by 5 but not by 3, returns 'Buzz'. + - In all other cases, returns the number itself as a string. + ''' + + fizzbuzzed = fizz(number) + buzz(number) + + if fizzbuzzed: + return fizzbuzzed + else: + return str(number) + +for number in [1, 2, 4, 7, 8, 11, 13, 14]: + assert fizzbuzz(number) == str(number), str(number) +for number in [3, 6, 9, 12, 18, 21, 24, 27]: + assert fizzbuzz(number) == 'Fizz', str(number) +for number in [5, 10, 20, 25, 35, 40]: + assert fizzbuzz(number) == 'Buzz', str(number) +for number in [15, 30, 45, 60, 75]: + assert fizzbuzz(number) == 'FizzBuzz', str(number) + + +# %% [markdown] id="o_3wq4agCCZH" +# 3. Write a function `chunk10(sequence)` that takes an iterable `sequence` and returns a list of lists: the first sublist contains the first ten elements of `sequence`, the second sublist contains the next ten elements of `sequence`, and so on. You may assume that the number of values in `sequence` is an exact multiple of 10. + +# %% executionInfo={"elapsed": 8, "status": "ok", "timestamp": 1681912825413, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="4c0A4kMfDdvt" +# Your solution here + +def chunk10(sequence): + ''' + Splits a sequence into chunks of 10 elements + ''' + + items = list(sequence) + chunks = [] + + for position in range(0, len(sequence), 10): + chunk = items[position: position + 10] + chunks.append(chunk) + + return chunks + +assert chunk10('Jelte!!!!!') == [list('Jelte!!!!!')] +assert chunk10('Hey Julian, let us have lunch!') == [ + list('Hey Julian'), + list(', let us h'), + list('ave lunch!'), +] +assert chunk10(range(20)) == [list(range(10)), list(range(10, 20))] + + +# %% [markdown] id="HBA4z4yVIhsn" +# 4. In step 1, you may have written definitions for `fizz` and `buzz` that look very similar to each other. Can you think of a way to avoid repeating the common elements? + +# %% executionInfo={"elapsed": 9, "status": "ok", "timestamp": 1681913046725, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="5g4BRdpLJLIc" +# Your solution here + +def fizz_or_buzz(number, message='Fizz', denominator=3): + ''' + If number is divisible by denominator, return the message. Otherwise, return the empty string. + ''' + if number % denominator == 0: + return message + else: + return '' + +def fizz(number): + return fizz_or_buzz(number) + +def buzz(number): + return fizz_or_buzz(number, 'Buzz', 5) + +for number in [1, 2, 4, 5, 7, 8, 10, 11, 13, 14]: + assert fizz(number) == '', str(number) + ' is not divisible by 3' +for number in [3, 6, 9, 12, 15, 18, 21, 24, 27]: + assert fizz(number) == 'Fizz', str(number) + ' is divisible by 3' +for number in [1, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14]: + assert buzz(number) == '', str(number) + ' is not divisible by 5' +for number in [5, 10, 15, 20, 25, 30, 35, 40]: + assert buzz(number) == 'Buzz', str(number) + ' is divisible by 5' + +# %% [markdown] id="BXzFYNGmPeO6" +# 5. In exercise 7.1.3, you documented the function `join_commas`. We did not actually need to write that function, because it is built into Python, although it goes by the name `', '.join`. That notation also works with other strings, as we demonstrate below. We also remind you of `map`, which appeared in the lecture.

+# Using these functions, as well as your own `fizzbuzz` from step 2 and your own `chunk10` from step 3, try to recreate (roughly) the same `FizzBuzz` output for the numbers 1 through 100 as in [exercise 6.2.4](https://colab.research.google.com/drive/1AZY4ESmsKKMvbalBDLlMrezAM5NzXXxV#scrollTo=uyqbuhKsUlhG), in as little code as you can. + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 597, "status": "ok", "timestamp": 1681913126765, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="nDGUS26IMApD" outputId="a78e10b9-f6d3-433f-f06c-d78ee65f65e4" +# The following code is just for illustration. +# You do not need it in your solution. +fruits = ['apricot', 'banana', 'cherry'] +print(', '.join(fruits)) +print('\n'.join(fruits)) +numbers = list(range(10)) +print(numbers) +strings = map(str, numbers) +print(list(strings)) + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 12, "status": "ok", "timestamp": 1681913265047, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}, "user_tz": -120} id="G87eCYM61P3a" outputId="23e8acb6-b66b-4871-e44a-8d37413bd1dc" +chunks = chunk10(range(100)) + +for chunk in chunks: + fizzbuzzed = map(fizzbuzz, chunk) + print(', '.join(fizzbuzzed)) + +# %% [markdown] id="Dntbbioh29xm" +# ## Next module +# +# [8. Debugging](https://colab.research.google.com/drive/1yQskT6SyKvXtXewx5kCla2NOmasgP8Vi) diff --git a/solutions/09 string manipulation solutions.ipynb b/solutions/09 string manipulation solutions.ipynb index 0fcd94f..308af07 100644 --- a/solutions/09 string manipulation solutions.ipynb +++ b/solutions/09 string manipulation solutions.ipynb @@ -1 +1,511 @@ -{"cells":[{"cell_type":"markdown","metadata":{"id":"5hIj-tbVleEq"},"source":["## Exercise 9.1: String Utilities\n","In each of the code blocks below, try to predict what will be printed, then run the code. If your guess was incorrect, try to figure out why the result is different. If your guess was correct, celebrate!"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"02q-FgvVlxEj"},"outputs":[],"source":["print(len('two'))"]},{"cell_type":"markdown","metadata":{"id":"dZ8S2UialXke"},"source":["A string is an iterable, the `len` is the amount of elements (characters)"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"cvCrnnn9l-oH"},"outputs":[],"source":["print(len(''))"]},{"cell_type":"markdown","metadata":{"id":"6vDjjHzIlcEY"},"source":["An empty string (just like an empty list) has length of 0."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"UlWWF0k7mA62"},"outputs":[],"source":["assert 'A' in 'Matilda'"]},{"cell_type":"markdown","metadata":{"id":"K_daSu19liik"},"source":["Python is case sensitive. The capitalized `A` is not in the string."]},{"cell_type":"code","execution_count":10,"metadata":{"executionInfo":{"elapsed":356,"status":"ok","timestamp":1681825113419,"user":{"displayName":"Jelte van Boheemen","userId":"01262930844892157638"},"user_tz":-120},"id":"Ui3gmvCNmHfB"},"outputs":[],"source":["assert 'A' in 'Matilda'.upper()"]},{"cell_type":"markdown","metadata":{"id":"i2bI-L6glqzO"},"source":["After capitalization of the whole string, `A` is in there."]},{"cell_type":"code","execution_count":9,"metadata":{"executionInfo":{"elapsed":244,"status":"ok","timestamp":1681825107244,"user":{"displayName":"Jelte van Boheemen","userId":"01262930844892157638"},"user_tz":-120},"id":"1tDEnzrumNdO"},"outputs":[],"source":["name = 'Matilda'\n","assert name.upper() == 'MATILDA'"]},{"cell_type":"markdown","metadata":{"id":"1pbJz3qElujM"},"source":["This is the definition of `string.upper()`"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"BUpf6LglmZ4n"},"outputs":[],"source":["name = 'Matilda'\n","assert name.upper().lower() == 'Matilda'"]},{"cell_type":"markdown","metadata":{"id":"Rq3LXgkMlywN"},"source":["This does not work. `Matilda.upper()` equals `'MATILDA'`. \n","\n","`'MATILDA'.lower()` equals `'matilda'`. The first captilal is lost in translation."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"sgfEH2jImlwz"},"outputs":[],"source":["print('Matilda'.replace('ilda', 'ild4'))"]},{"cell_type":"markdown","metadata":{"id":"GhUlBMfPmGb3"},"source":["`string.replace()` is not limited to single characters"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"BEE94VVBmf7T"},"outputs":[],"source":["print('Matilda'.replace('a', 4))"]},{"cell_type":"markdown","metadata":{"id":"qsPQT5VGlNZ2"},"source":["We can only replace strings with other strings. `'Matilda'.replace('a', '4')` would work"]},{"cell_type":"code","execution_count":4,"metadata":{"executionInfo":{"elapsed":5,"status":"ok","timestamp":1681824924342,"user":{"displayName":"Jelte van Boheemen","userId":"01262930844892157638"},"user_tz":-120},"id":"BVKc0bQAnGYq"},"outputs":[],"source":["list_of_words = ['I', 'ate', 'a', 'banana']\n","sentence = 'I ate a banana'\n","\n","assert sentence.split() == list_of_words"]},{"cell_type":"markdown","metadata":{"id":"xdueWI-yk_ME"},"source":["`string.split()` splits on all whitespace characters by default"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"R4U_8kVtmpHE"},"outputs":[],"source":["list_of_words = ['I', 'ate', 'a', 'banana']\n","sentence = 'I ate a banana'\n","\n","assert ''.join(list_of_words) == sentence"]},{"cell_type":"markdown","metadata":{"id":"ZpepgX5Lk0Mk"},"source":["We join the words on the empty string, and not on a space. Thus, the resulting sentence contains all the words concatenated. "]},{"cell_type":"markdown","metadata":{"id":"Xbi4ATS0mQzp"},"source":["## Exercise 9.2: Additional utilities\n","For each of the string functions below, find out what they do. If they take any parameters, describe what these do:\n","- `string.startswith()`\n","- `string.endswith()`\n","- `string.strip()`\n","- `string.lstrip()`\n","- `string.rstrip()`\n","- `string.title()`\n","- `string.find()`\n","- `string.split(',', x)` (describe what 'x' does. It should be a number)"]},{"cell_type":"code","execution_count":22,"metadata":{"executionInfo":{"elapsed":231,"status":"ok","timestamp":1681825855742,"user":{"displayName":"Jelte van Boheemen","userId":"01262930844892157638"},"user_tz":-120},"id":"ZYHALHE7mU4-"},"outputs":[],"source":["# string.startswith(substring) checks if a string starts with a substring\n","assert 'matilda'.startswith('mat')\n","\n","# string.endswith(substring) checks if a string ends with a substring\n","assert 'matilda'.endswith('lda')\n","\n","# string.strip() removes whitespace from the start and end of a string\n","# as an optional parameter, takes a string and removes only characters in that string\n","assert ' matilda '.strip() == 'matilda'\n","assert '..matilda?'.strip('.,') == 'matilda?'\n","\n","# string.lstrip() and string.rstrip() work like string.split()\n","# but only for l(eft) and r(ight) part of the string\n","assert ' matilda '.rstrip() == ' matilda'\n","assert ' matilda '.lstrip() == 'matilda '\n","\n","# string.title() converts a string to title case: each word starts with a capital\n","assert 'MATILDA by roald DaHl'.title() == 'Matilda By Roald Dahl'\n","\n","# string.find(substring) returns the starting character of the substring\n","# if the substring is not found, return -1\n","assert 'matilda'.find('il') == 3\n","\n","# string.split(separator, x) splits a string x times\n","# after x splits have been made, the rest of the string is left intact\n","assert 'Matilda by Roald Dahl'.split(' ', 2) == ['Matilda', 'by', 'Roald Dahl']"]},{"cell_type":"markdown","metadata":{"id":"avcqgDAAoq-w"},"source":["## Exercise 9.3: String formatting\n","In each of the code blocks below, try to predict what will be printed, then run the code. If your guess was incorrect, try to figure out why the result is different. If your guess was correct, celebrate!"]},{"cell_type":"code","execution_count":23,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":7,"status":"ok","timestamp":1681825929769,"user":{"displayName":"Jelte van Boheemen","userId":"01262930844892157638"},"user_tz":-120},"id":"BIa0VOX_owF4","outputId":"ddc080fa-bf11-4064-c5f4-efcbef9b9a64"},"outputs":[{"name":"stdout","output_type":"stream","text":["hey {}\n"]}],"source":["print('hey {}')"]},{"cell_type":"markdown","metadata":{"id":"SDnzz_IYoqRP"},"source":["Using `{}` in a string with `.format()` leaves the curly brackets untouched."]},{"cell_type":"code","execution_count":24,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":3,"status":"ok","timestamp":1681825932026,"user":{"displayName":"Jelte van Boheemen","userId":"01262930844892157638"},"user_tz":-120},"id":"81GUitOZo0l2","outputId":"6be221d0-52b7-47f9-8269-991811402ff7"},"outputs":[{"name":"stdout","output_type":"stream","text":["hey {Julian}\n"]}],"source":["print('hey {Julian}')"]},{"cell_type":"markdown","metadata":{"id":"-HFk3OAAoyyy"},"source":["Again, without `.format()`, the string is untouched"]},{"cell_type":"code","execution_count":25,"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"elapsed":267,"status":"ok","timestamp":1681825967240,"user":{"displayName":"Jelte van Boheemen","userId":"01262930844892157638"},"user_tz":-120},"id":"96Dq4eSCpAji","outputId":"7e3086d7-4e02-4b98-d96e-d91257f65fb5"},"outputs":[{"name":"stdout","output_type":"stream","text":["hey Julian\n"]}],"source":["print('hey {}'.format('Julian'))"]},{"cell_type":"markdown","metadata":{"id":"OFmrf_lto6_k"},"source":["Calling `.format()` fills the placeholders"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"h43fjzPco4V_"},"outputs":[],"source":["print('hey {Julian}'.format('Julian'))"]},{"cell_type":"markdown","metadata":{"id":"XP3dKwEXpAir"},"source":["This doesn't work, the names placeholder expects a named parameter.\n","\n","`'hey {Julian}'.format(Julian='julian')` would work"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"wA18AIPKpFAH"},"outputs":[],"source":["print('hey {name}'.format('Julian'))"]},{"cell_type":"markdown","metadata":{"id":"LI43JmY2pcIg"},"source":["Same as above"]}],"metadata":{"colab":{"authorship_tag":"ABX9TyMi0JBuu4ELq/AQbr5Ws5LZ","provenance":[]},"kernelspec":{"display_name":"Python 3","name":"python3"},"language_info":{"name":"python"}},"nbformat":4,"nbformat_minor":0} +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "5hIj-tbVleEq" + }, + "source": [ + "## Exercise 9.1: String Utilities\n", + "In each of the code blocks below, try to predict what will be printed, then run the code. If your guess was incorrect, try to figure out why the result is different. If your guess was correct, celebrate!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "02q-FgvVlxEj" + }, + "outputs": [], + "source": [ + "print(len('two'))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "dZ8S2UialXke" + }, + "source": [ + "A string is an iterable, the `len` is the amount of elements (characters)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "cvCrnnn9l-oH" + }, + "outputs": [], + "source": [ + "print(len(''))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6vDjjHzIlcEY" + }, + "source": [ + "An empty string (just like an empty list) has length of 0." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "UlWWF0k7mA62" + }, + "outputs": [], + "source": [ + "assert 'A' in 'Matilda'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "K_daSu19liik" + }, + "source": [ + "Python is case sensitive. The capitalized `A` is not in the string." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "executionInfo": { + "elapsed": 356, + "status": "ok", + "timestamp": 1681825113419, + "user": { + "displayName": "Jelte van Boheemen", + "userId": "01262930844892157638" + }, + "user_tz": -120 + }, + "id": "Ui3gmvCNmHfB" + }, + "outputs": [], + "source": [ + "assert 'A' in 'Matilda'.upper()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "i2bI-L6glqzO" + }, + "source": [ + "After capitalization of the whole string, `A` is in there." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "executionInfo": { + "elapsed": 244, + "status": "ok", + "timestamp": 1681825107244, + "user": { + "displayName": "Jelte van Boheemen", + "userId": "01262930844892157638" + }, + "user_tz": -120 + }, + "id": "1tDEnzrumNdO" + }, + "outputs": [], + "source": [ + "name = 'Matilda'\n", + "assert name.upper() == 'MATILDA'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1pbJz3qElujM" + }, + "source": [ + "This is the definition of `string.upper()`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "BUpf6LglmZ4n" + }, + "outputs": [], + "source": [ + "name = 'Matilda'\n", + "assert name.upper().lower() == 'Matilda'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Rq3LXgkMlywN" + }, + "source": [ + "This does not work. `Matilda.upper()` equals `'MATILDA'`. \n", + "\n", + "`'MATILDA'.lower()` equals `'matilda'`. The first captilal is lost in translation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "sgfEH2jImlwz" + }, + "outputs": [], + "source": [ + "print('Matilda'.replace('ilda', 'ild4'))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GhUlBMfPmGb3" + }, + "source": [ + "`string.replace()` is not limited to single characters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "BEE94VVBmf7T" + }, + "outputs": [], + "source": [ + "print('Matilda'.replace('a', 4))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qsPQT5VGlNZ2" + }, + "source": [ + "We can only replace strings with other strings. `'Matilda'.replace('a', '4')` would work" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "executionInfo": { + "elapsed": 5, + "status": "ok", + "timestamp": 1681824924342, + "user": { + "displayName": "Jelte van Boheemen", + "userId": "01262930844892157638" + }, + "user_tz": -120 + }, + "id": "BVKc0bQAnGYq" + }, + "outputs": [], + "source": [ + "list_of_words = ['I', 'ate', 'a', 'banana']\n", + "sentence = 'I ate a banana'\n", + "\n", + "assert sentence.split() == list_of_words" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xdueWI-yk_ME" + }, + "source": [ + "`string.split()` splits on all whitespace characters by default" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "R4U_8kVtmpHE" + }, + "outputs": [], + "source": [ + "list_of_words = ['I', 'ate', 'a', 'banana']\n", + "sentence = 'I ate a banana'\n", + "\n", + "assert ''.join(list_of_words) == sentence" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ZpepgX5Lk0Mk" + }, + "source": [ + "We join the words on the empty string, and not on a space. Thus, the resulting sentence contains all the words concatenated. " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Xbi4ATS0mQzp" + }, + "source": [ + "## Exercise 9.2: Additional utilities\n", + "For each of the string functions below, find out what they do. If they take any parameters, describe what these do:\n", + "- `string.startswith()`\n", + "- `string.endswith()`\n", + "- `string.strip()`\n", + "- `string.lstrip()`\n", + "- `string.rstrip()`\n", + "- `string.title()`\n", + "- `string.find()`\n", + "- `string.split(',', x)` (describe what 'x' does. It should be a number)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "executionInfo": { + "elapsed": 231, + "status": "ok", + "timestamp": 1681825855742, + "user": { + "displayName": "Jelte van Boheemen", + "userId": "01262930844892157638" + }, + "user_tz": -120 + }, + "id": "ZYHALHE7mU4-" + }, + "outputs": [], + "source": [ + "# string.startswith(substring) checks if a string starts with a substring\n", + "assert 'matilda'.startswith('mat')\n", + "\n", + "# string.endswith(substring) checks if a string ends with a substring\n", + "assert 'matilda'.endswith('lda')\n", + "\n", + "# string.strip() removes whitespace from the start and end of a string\n", + "# as an optional parameter, takes a string and removes only characters in that string\n", + "assert ' matilda '.strip() == 'matilda'\n", + "assert '..matilda?'.strip('.,') == 'matilda?'\n", + "\n", + "# string.lstrip() and string.rstrip() work like string.split()\n", + "# but only for l(eft) and r(ight) part of the string\n", + "assert ' matilda '.rstrip() == ' matilda'\n", + "assert ' matilda '.lstrip() == 'matilda '\n", + "\n", + "# string.title() converts a string to title case: each word starts with a capital\n", + "assert 'MATILDA by roald DaHl'.title() == 'Matilda By Roald Dahl'\n", + "\n", + "# string.find(substring) returns the starting character of the substring\n", + "# if the substring is not found, return -1\n", + "assert 'matilda'.find('il') == 3\n", + "\n", + "# string.split(separator, x) splits a string x times\n", + "# after x splits have been made, the rest of the string is left intact\n", + "assert 'Matilda by Roald Dahl'.split(' ', 2) == ['Matilda', 'by', 'Roald Dahl']" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "avcqgDAAoq-w" + }, + "source": [ + "## Exercise 9.3: String formatting\n", + "In each of the code blocks below, try to predict what will be printed, then run the code. If your guess was incorrect, try to figure out why the result is different. If your guess was correct, celebrate!" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 7, + "status": "ok", + "timestamp": 1681825929769, + "user": { + "displayName": "Jelte van Boheemen", + "userId": "01262930844892157638" + }, + "user_tz": -120 + }, + "id": "BIa0VOX_owF4", + "outputId": "ddc080fa-bf11-4064-c5f4-efcbef9b9a64" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "hey {}\n" + ] + } + ], + "source": [ + "print('hey {}')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SDnzz_IYoqRP" + }, + "source": [ + "Using `{}` in a string with `.format()` leaves the curly brackets untouched." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 3, + "status": "ok", + "timestamp": 1681825932026, + "user": { + "displayName": "Jelte van Boheemen", + "userId": "01262930844892157638" + }, + "user_tz": -120 + }, + "id": "81GUitOZo0l2", + "outputId": "6be221d0-52b7-47f9-8269-991811402ff7" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "hey {Julian}\n" + ] + } + ], + "source": [ + "print('hey {Julian}')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-HFk3OAAoyyy" + }, + "source": [ + "Again, without `.format()`, the string is untouched" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 267, + "status": "ok", + "timestamp": 1681825967240, + "user": { + "displayName": "Jelte van Boheemen", + "userId": "01262930844892157638" + }, + "user_tz": -120 + }, + "id": "96Dq4eSCpAji", + "outputId": "7e3086d7-4e02-4b98-d96e-d91257f65fb5" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "hey Julian\n" + ] + } + ], + "source": [ + "print('hey {}'.format('Julian'))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "OFmrf_lto6_k" + }, + "source": [ + "Calling `.format()` fills the placeholders" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "h43fjzPco4V_" + }, + "outputs": [], + "source": [ + "print('hey {Julian}'.format('Julian'))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XP3dKwEXpAir" + }, + "source": [ + "This doesn't work, the names placeholder expects a named parameter.\n", + "\n", + "`'hey {Julian}'.format(Julian='julian')` would work" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "wA18AIPKpFAH" + }, + "outputs": [], + "source": [ + "print('hey {name}'.format('Julian'))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "LI43JmY2pcIg" + }, + "source": [ + "Same as above" + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyMi0JBuu4ELq/AQbr5Ws5LZ", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/solutions/09 string manipulation solutions.py b/solutions/09 string manipulation solutions.py new file mode 100644 index 0000000..fa81140 --- /dev/null +++ b/solutions/09 string manipulation solutions.py @@ -0,0 +1,162 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# name: python3 +# --- + +# %% [markdown] id="5hIj-tbVleEq" +# ## Exercise 9.1: String Utilities +# In each of the code blocks below, try to predict what will be printed, then run the code. If your guess was incorrect, try to figure out why the result is different. If your guess was correct, celebrate! + +# %% id="02q-FgvVlxEj" +print(len('two')) + +# %% [markdown] id="dZ8S2UialXke" +# A string is an iterable, the `len` is the amount of elements (characters) + +# %% id="cvCrnnn9l-oH" +print(len('')) + +# %% [markdown] id="6vDjjHzIlcEY" +# An empty string (just like an empty list) has length of 0. + +# %% id="UlWWF0k7mA62" +assert 'A' in 'Matilda' + +# %% [markdown] id="K_daSu19liik" +# Python is case sensitive. The capitalized `A` is not in the string. + +# %% executionInfo={"elapsed": 356, "status": "ok", "timestamp": 1681825113419, "user": {"displayName": "Jelte van Boheemen", "userId": "01262930844892157638"}, "user_tz": -120} id="Ui3gmvCNmHfB" +assert 'A' in 'Matilda'.upper() + +# %% [markdown] id="i2bI-L6glqzO" +# After capitalization of the whole string, `A` is in there. + +# %% executionInfo={"elapsed": 244, "status": "ok", "timestamp": 1681825107244, "user": {"displayName": "Jelte van Boheemen", "userId": "01262930844892157638"}, "user_tz": -120} id="1tDEnzrumNdO" +name = 'Matilda' +assert name.upper() == 'MATILDA' + +# %% [markdown] id="1pbJz3qElujM" +# This is the definition of `string.upper()` + +# %% id="BUpf6LglmZ4n" +name = 'Matilda' +assert name.upper().lower() == 'Matilda' + +# %% [markdown] id="Rq3LXgkMlywN" +# This does not work. `Matilda.upper()` equals `'MATILDA'`. +# +# `'MATILDA'.lower()` equals `'matilda'`. The first captilal is lost in translation. + +# %% id="sgfEH2jImlwz" +print('Matilda'.replace('ilda', 'ild4')) + +# %% [markdown] id="GhUlBMfPmGb3" +# `string.replace()` is not limited to single characters + +# %% id="BEE94VVBmf7T" +print('Matilda'.replace('a', 4)) + +# %% [markdown] id="qsPQT5VGlNZ2" +# We can only replace strings with other strings. `'Matilda'.replace('a', '4')` would work + +# %% executionInfo={"elapsed": 5, "status": "ok", "timestamp": 1681824924342, "user": {"displayName": "Jelte van Boheemen", "userId": "01262930844892157638"}, "user_tz": -120} id="BVKc0bQAnGYq" +list_of_words = ['I', 'ate', 'a', 'banana'] +sentence = 'I ate a banana' + +assert sentence.split() == list_of_words + +# %% [markdown] id="xdueWI-yk_ME" +# `string.split()` splits on all whitespace characters by default + +# %% id="R4U_8kVtmpHE" +list_of_words = ['I', 'ate', 'a', 'banana'] +sentence = 'I ate a banana' + +assert ''.join(list_of_words) == sentence + +# %% [markdown] id="ZpepgX5Lk0Mk" +# We join the words on the empty string, and not on a space. Thus, the resulting sentence contains all the words concatenated. + +# %% [markdown] id="Xbi4ATS0mQzp" +# ## Exercise 9.2: Additional utilities +# For each of the string functions below, find out what they do. If they take any parameters, describe what these do: +# - `string.startswith()` +# - `string.endswith()` +# - `string.strip()` +# - `string.lstrip()` +# - `string.rstrip()` +# - `string.title()` +# - `string.find()` +# - `string.split(',', x)` (describe what 'x' does. It should be a number) + +# %% executionInfo={"elapsed": 231, "status": "ok", "timestamp": 1681825855742, "user": {"displayName": "Jelte van Boheemen", "userId": "01262930844892157638"}, "user_tz": -120} id="ZYHALHE7mU4-" +# string.startswith(substring) checks if a string starts with a substring +assert 'matilda'.startswith('mat') + +# string.endswith(substring) checks if a string ends with a substring +assert 'matilda'.endswith('lda') + +# string.strip() removes whitespace from the start and end of a string +# as an optional parameter, takes a string and removes only characters in that string +assert ' matilda '.strip() == 'matilda' +assert '..matilda?'.strip('.,') == 'matilda?' + +# string.lstrip() and string.rstrip() work like string.split() +# but only for l(eft) and r(ight) part of the string +assert ' matilda '.rstrip() == ' matilda' +assert ' matilda '.lstrip() == 'matilda ' + +# string.title() converts a string to title case: each word starts with a capital +assert 'MATILDA by roald DaHl'.title() == 'Matilda By Roald Dahl' + +# string.find(substring) returns the starting character of the substring +# if the substring is not found, return -1 +assert 'matilda'.find('il') == 3 + +# string.split(separator, x) splits a string x times +# after x splits have been made, the rest of the string is left intact +assert 'Matilda by Roald Dahl'.split(' ', 2) == ['Matilda', 'by', 'Roald Dahl'] + +# %% [markdown] id="avcqgDAAoq-w" +# ## Exercise 9.3: String formatting +# In each of the code blocks below, try to predict what will be printed, then run the code. If your guess was incorrect, try to figure out why the result is different. If your guess was correct, celebrate! + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 7, "status": "ok", "timestamp": 1681825929769, "user": {"displayName": "Jelte van Boheemen", "userId": "01262930844892157638"}, "user_tz": -120} id="BIa0VOX_owF4" outputId="ddc080fa-bf11-4064-c5f4-efcbef9b9a64" +print('hey {}') + +# %% [markdown] id="SDnzz_IYoqRP" +# Using `{}` in a string with `.format()` leaves the curly brackets untouched. + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 3, "status": "ok", "timestamp": 1681825932026, "user": {"displayName": "Jelte van Boheemen", "userId": "01262930844892157638"}, "user_tz": -120} id="81GUitOZo0l2" outputId="6be221d0-52b7-47f9-8269-991811402ff7" +print('hey {Julian}') + +# %% [markdown] id="-HFk3OAAoyyy" +# Again, without `.format()`, the string is untouched + +# %% colab={"base_uri": "https://localhost:8080/"} executionInfo={"elapsed": 267, "status": "ok", "timestamp": 1681825967240, "user": {"displayName": "Jelte van Boheemen", "userId": "01262930844892157638"}, "user_tz": -120} id="96Dq4eSCpAji" outputId="7e3086d7-4e02-4b98-d96e-d91257f65fb5" +print('hey {}'.format('Julian')) + +# %% [markdown] id="OFmrf_lto6_k" +# Calling `.format()` fills the placeholders + +# %% id="h43fjzPco4V_" +print('hey {Julian}'.format('Julian')) + +# %% [markdown] id="XP3dKwEXpAir" +# This doesn't work, the names placeholder expects a named parameter. +# +# `'hey {Julian}'.format(Julian='julian')` would work + +# %% id="wA18AIPKpFAH" +print('hey {name}'.format('Julian')) + +# %% [markdown] id="LI43JmY2pcIg" +# Same as above diff --git a/solutions/10 - Dictionaries solutions.ipynb b/solutions/10 - Dictionaries solutions.ipynb index fb8f923..4b15cfb 100644 --- a/solutions/10 - Dictionaries solutions.ipynb +++ b/solutions/10 - Dictionaries solutions.ipynb @@ -1 +1,401 @@ -{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"provenance":[{"file_id":"16mecEPkVGUBIFoHIALO7dE8qv7PynHL9","timestamp":1681824660733}]},"kernelspec":{"name":"python3","display_name":"Python 3"},"language_info":{"name":"python"}},"cells":[{"cell_type":"markdown","source":["# Dictionaries\n","\n","### CDH course \"Programming in Python\"\n","\n","[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n","\n","Previous module: [9. String manipulation](https://colab.research.google.com/drive/1Z7NNMxqHTSMoUadH3pVL6Bg6oii4qUoQ)\n","\n","### This module\n","\n","- Learn about _dictionaries_, a useful way of storing and looking up data"],"metadata":{"id":"kjdcNtg3k8Aa"}},{"cell_type":"markdown","source":["## Exercise 10.1: Dictionaries"],"metadata":{"id":"1ezL-XhlvHBq"}},{"cell_type":"markdown","source":["1 . Below are two dictionaries containing information about different types of fruit. Print a nice message about each fruit stating its colour and price. For example, _An apple is red and costs € 2.50_, etc."],"metadata":{"id":"rxcz60KQximW"}},{"cell_type":"code","source":["fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'}\n","fruit_prices = {'apple': 2.50, 'banana': 2.10, 'orange': 1.50}\n","\n","# The example sentence includes 'a'/'an' ('an apple' / 'a banana')\n","# which was a bit of an oversight: the point of this exercise\n","# was to loop through a dictionary and access values.\n","# You could make a solution like this to avoid it.\n","\n","for fruit in fruit_colors:\n"," color = fruit_colors[fruit]\n"," price = fruit_prices[fruit]\n","\n"," print(fruit + 's', 'are', color, 'and cost €', price, sep=' ')\n","\n","# Bonus points if you DID implement a solution on when to use\n","# 'a' or 'an'! Here is an example of your you can do that.\n","\n","print()\n","\n","def starts_with_vowel(word):\n"," '''\n"," Returns True if the word (a string) starts with a vowel.\n"," '''\n"," vowels = ['a', 'e', 'i', 'o', 'u']\n"," if word[0] in vowels:\n"," return True\n"," return False\n","\n","\n","for fruit in fruit_colors:\n"," color = fruit_colors[fruit]\n"," price = fruit_prices[fruit]\n","\n"," if starts_with_vowel(fruit):\n"," article = 'An'\n"," else:\n"," article = 'A'\n","\n"," print(article, fruit, 'is', color, 'and costs €', price, sep=' ')"],"metadata":{"id":"yn89oAAZu33C","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1681894457462,"user_tz":-120,"elapsed":238,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"}},"outputId":"d6b7d53c-d22c-4499-8b59-de508579836e"},"execution_count":3,"outputs":[{"output_type":"stream","name":"stdout","text":["apples are red and cost € 2.5\n","bananas are yellow and cost € 2.1\n","oranges are orange and cost € 1.5\n","\n","An apple is red and costs € 2.5\n","A banana is yellow and costs € 2.1\n","An orange is orange and costs € 1.5\n"]}]},{"cell_type":"markdown","source":["2 . Here is a longer lists of fruit colours. Write a function `count_fruits` which gets gets a colour as input and returns the number of fruits that have that colour (according to `lots_of_fruit`)."],"metadata":{"id":"Gtp5V9dE0LxK"}},{"cell_type":"code","source":["lots_of_fruit = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange',\n"," 'cucumber': 'green', 'kiwi': 'green', 'strawberry': 'red',\n"," 'pineapple': 'yellow','blackberry': 'black', 'cherry': 'red',\n"," 'gooseberry': 'green', 'raspberry': 'red', 'mandarin': 'orange',\n"," 'lemon': 'yellow', 'lime': 'green'}"],"metadata":{"id":"S7gCNyLCxdrO","executionInfo":{"status":"ok","timestamp":1681894473607,"user_tz":-120,"elapsed":227,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"}}},"execution_count":4,"outputs":[]},{"cell_type":"code","source":["def count_fruits(color):\n"," '''Count the number of fruits in `lots_of_fruit` that match this colour.'''\n"," count = 0\n"," for value in lots_of_fruit.values():\n"," if value == color:\n"," count = count + 1\n"," return count\n","\n","# let's see if it works!\n","assert count_fruits('red') == 4\n","assert count_fruits('lavender') == 0"],"metadata":{"id":"nScDQipK35qN","executionInfo":{"status":"ok","timestamp":1681894484122,"user_tz":-120,"elapsed":259,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"}}},"execution_count":5,"outputs":[]},{"cell_type":"markdown","source":["3 . The list `fruit_basket` contains a bunch of fruits. Can you make a dictionary `fruit_counts` which gives the amount for each fruit in `fruit_basket`? (Do not count the fruits by hand!)"],"metadata":{"id":"-Qp6R3Kp3GId"}},{"cell_type":"code","source":["fruit_basket = ['apple', 'banana', 'banana', 'banana', 'apple', 'orange',\n"," 'orange', 'grape', 'grape', 'grape', 'grape', 'grape', 'grape',\n"," 'grape', 'grape', 'grape', 'pear', 'apple', 'strawberry',\n"," 'strawberry', 'strawberry', 'orange']"],"metadata":{"id":"awf-lLQO3N1U","executionInfo":{"status":"ok","timestamp":1681894489366,"user_tz":-120,"elapsed":217,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"}}},"execution_count":6,"outputs":[]},{"cell_type":"code","source":["def count_items(items):\n"," '''\n"," Count the items in a list.\n","\n"," Input: a list of items, such as strings\n"," Output: a dictionary with the total of occurences for each item.\n"," '''\n","\n"," counts = dict() # we will keep track of our counts in here!\n","\n"," for item in items:\n"," # the current count: either the current value in the dictionary\n"," # or 0 if we haven't seen this fruit yet\n"," current_count = counts.get(item, 0)\n"," new_count = current_count + 1\n"," counts[item] = new_count\n"," \n"," return counts\n","\n","fruit_counts = count_items(fruit_basket)\n","\n","# let's see if it works!\n","assert fruit_counts['apple'] == 3\n"],"metadata":{"id":"MDpIZpbm3-BG","executionInfo":{"status":"ok","timestamp":1681894585893,"user_tz":-120,"elapsed":281,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"}}},"execution_count":7,"outputs":[]},{"cell_type":"markdown","source":["4 . Here is a different list, which contains the words in a sentence. Can you use your code above to make a dictionary `word_counts` telling us how often each word occurs? (Tip: if you need to do very similar tasks, make a function!)\n","\n","Write a function that takes a dictionary like `word_counts` tells us the most commonly occuring item and the count. Note that there can be multiple items that occurred the most."],"metadata":{"id":"h-kJhTO951pc"}},{"cell_type":"code","source":["# the variable sent0 contains the first sentence of The Catcher in the Rye\n","# split into single words\n","sent0 = ['If', 'you', 'really', 'want', 'to', 'hear', 'about', 'it,', 'the', \n"," 'first', 'thing', 'you’ll', 'probably', 'want', 'to', 'know', 'is', \n"," 'where', 'I', 'was', 'born,', 'and', 'what', 'my', 'lousy', 'childhood', \n"," 'was', 'like,', 'and', 'how', 'my', 'parents', 'were', 'occupied', \n"," 'and', 'all', 'before', 'they', 'had', 'me,', 'and', 'all', 'that', \n"," 'David', 'Copperfield', 'kind', 'of', 'crap,', 'but', 'I', 'don’t', \n"," 'feel', 'like', 'going', 'into', 'it,', 'if', 'you', 'want', \n"," 'to', 'know', 'the', 'truth.']"],"metadata":{"id":"mdNug4ct5645","executionInfo":{"status":"ok","timestamp":1681894602379,"user_tz":-120,"elapsed":241,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"}}},"execution_count":8,"outputs":[]},{"cell_type":"code","source":["word_counts = count_items(sent0) # we recycle our function from the last exercise"],"metadata":{"id":"XGY3qSEk6B9j","executionInfo":{"status":"ok","timestamp":1681894603343,"user_tz":-120,"elapsed":3,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"}}},"execution_count":9,"outputs":[]},{"cell_type":"code","source":["def most_frequent(counts):\n"," '''\n"," For a dictionary with totals, the most commonly occuring item(s) and the count.\n","\n"," Input should be a dictionary with the total number of occurences for each key in some collection.\n"," Returns a tuple of two items. First is a list of the most frequent item(s). If the input\n"," is an empty dict, the list if empty. Second is the number of occurences for that item.\n"," '''\n","\n"," if not len(counts):\n"," return [], 0\n","\n"," max_count = max(counts.values())\n","\n"," top_items = []\n"," for item, count in counts.items():\n"," if count == max_count:\n"," top_items.append(item)\n","\n"," return top_items, max_count\n","\n","words, total = most_frequent(word_counts)\n","print(words, total)\n","\n","# here are some assert statements you could use to check your own function\n","# feel free to adapt them if your function gives a different output format\n","assert most_frequent(fruit_counts) == (['grape'], 9)\n","assert most_frequent(word_counts) == (['and'], 4)\n","assert most_frequent({}) == ([], 0)"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"iS8rDbONnVDr","executionInfo":{"status":"ok","timestamp":1681894948452,"user_tz":-120,"elapsed":307,"user":{"displayName":"Luka van der Plas","userId":"16305747382115943293"}},"outputId":"c1313777-a4ab-477d-e428-9ae285fc28da"},"execution_count":14,"outputs":[{"output_type":"stream","name":"stdout","text":["['and'] 4\n"]}]}]} \ No newline at end of file +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "kjdcNtg3k8Aa" + }, + "source": [ + "# Dictionaries\n", + "\n", + "### CDH course \"Programming in Python\"\n", + "\n", + "[index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno)\n", + "\n", + "Previous module: [9. String manipulation](https://colab.research.google.com/drive/1Z7NNMxqHTSMoUadH3pVL6Bg6oii4qUoQ)\n", + "\n", + "### This module\n", + "\n", + "- Learn about _dictionaries_, a useful way of storing and looking up data" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1ezL-XhlvHBq" + }, + "source": [ + "## Exercise 10.1: Dictionaries" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rxcz60KQximW" + }, + "source": [ + "1 . Below are two dictionaries containing information about different types of fruit. Print a nice message about each fruit stating its colour and price. For example, _An apple is red and costs € 2.50_, etc." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 238, + "status": "ok", + "timestamp": 1681894457462, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "yn89oAAZu33C", + "outputId": "d6b7d53c-d22c-4499-8b59-de508579836e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "apples are red and cost € 2.5\n", + "bananas are yellow and cost € 2.1\n", + "oranges are orange and cost € 1.5\n", + "\n", + "An apple is red and costs € 2.5\n", + "A banana is yellow and costs € 2.1\n", + "An orange is orange and costs € 1.5\n" + ] + } + ], + "source": [ + "fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'}\n", + "fruit_prices = {'apple': 2.50, 'banana': 2.10, 'orange': 1.50}\n", + "\n", + "# The example sentence includes 'a'/'an' ('an apple' / 'a banana')\n", + "# which was a bit of an oversight: the point of this exercise\n", + "# was to loop through a dictionary and access values.\n", + "# You could make a solution like this to avoid it.\n", + "\n", + "for fruit in fruit_colors:\n", + " color = fruit_colors[fruit]\n", + " price = fruit_prices[fruit]\n", + "\n", + " print(fruit + 's', 'are', color, 'and cost €', price, sep=' ')\n", + "\n", + "# Bonus points if you DID implement a solution on when to use\n", + "# 'a' or 'an'! Here is an example of your you can do that.\n", + "\n", + "print()\n", + "\n", + "def starts_with_vowel(word):\n", + " '''\n", + " Returns True if the word (a string) starts with a vowel.\n", + " '''\n", + " vowels = ['a', 'e', 'i', 'o', 'u']\n", + " if word[0] in vowels:\n", + " return True\n", + " return False\n", + "\n", + "\n", + "for fruit in fruit_colors:\n", + " color = fruit_colors[fruit]\n", + " price = fruit_prices[fruit]\n", + "\n", + " if starts_with_vowel(fruit):\n", + " article = 'An'\n", + " else:\n", + " article = 'A'\n", + "\n", + " print(article, fruit, 'is', color, 'and costs €', price, sep=' ')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Gtp5V9dE0LxK" + }, + "source": [ + "2 . Here is a longer lists of fruit colours. Write a function `count_fruits` which gets gets a colour as input and returns the number of fruits that have that colour (according to `lots_of_fruit`)." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "executionInfo": { + "elapsed": 227, + "status": "ok", + "timestamp": 1681894473607, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "S7gCNyLCxdrO" + }, + "outputs": [], + "source": [ + "lots_of_fruit = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange',\n", + " 'cucumber': 'green', 'kiwi': 'green', 'strawberry': 'red',\n", + " 'pineapple': 'yellow','blackberry': 'black', 'cherry': 'red',\n", + " 'gooseberry': 'green', 'raspberry': 'red', 'mandarin': 'orange',\n", + " 'lemon': 'yellow', 'lime': 'green'}" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "executionInfo": { + "elapsed": 259, + "status": "ok", + "timestamp": 1681894484122, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "nScDQipK35qN" + }, + "outputs": [], + "source": [ + "def count_fruits(color):\n", + " '''Count the number of fruits in `lots_of_fruit` that match this colour.'''\n", + " count = 0\n", + " for value in lots_of_fruit.values():\n", + " if value == color:\n", + " count = count + 1\n", + " return count\n", + "\n", + "# let's see if it works!\n", + "assert count_fruits('red') == 4\n", + "assert count_fruits('lavender') == 0" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-Qp6R3Kp3GId" + }, + "source": [ + "3 . The list `fruit_basket` contains a bunch of fruits. Can you make a dictionary `fruit_counts` which gives the amount for each fruit in `fruit_basket`? (Do not count the fruits by hand!)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "executionInfo": { + "elapsed": 217, + "status": "ok", + "timestamp": 1681894489366, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "awf-lLQO3N1U" + }, + "outputs": [], + "source": [ + "fruit_basket = ['apple', 'banana', 'banana', 'banana', 'apple', 'orange',\n", + " 'orange', 'grape', 'grape', 'grape', 'grape', 'grape', 'grape',\n", + " 'grape', 'grape', 'grape', 'pear', 'apple', 'strawberry',\n", + " 'strawberry', 'strawberry', 'orange']" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "executionInfo": { + "elapsed": 281, + "status": "ok", + "timestamp": 1681894585893, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "MDpIZpbm3-BG" + }, + "outputs": [], + "source": [ + "def count_items(items):\n", + " '''\n", + " Count the items in a list.\n", + "\n", + " Input: a list of items, such as strings\n", + " Output: a dictionary with the total of occurences for each item.\n", + " '''\n", + "\n", + " counts = dict() # we will keep track of our counts in here!\n", + "\n", + " for item in items:\n", + " # the current count: either the current value in the dictionary\n", + " # or 0 if we haven't seen this fruit yet\n", + " current_count = counts.get(item, 0)\n", + " new_count = current_count + 1\n", + " counts[item] = new_count\n", + " \n", + " return counts\n", + "\n", + "fruit_counts = count_items(fruit_basket)\n", + "\n", + "# let's see if it works!\n", + "assert fruit_counts['apple'] == 3\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "h-kJhTO951pc" + }, + "source": [ + "4 . Here is a different list, which contains the words in a sentence. Can you use your code above to make a dictionary `word_counts` telling us how often each word occurs? (Tip: if you need to do very similar tasks, make a function!)\n", + "\n", + "Write a function that takes a dictionary like `word_counts` tells us the most commonly occuring item and the count. Note that there can be multiple items that occurred the most." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "executionInfo": { + "elapsed": 241, + "status": "ok", + "timestamp": 1681894602379, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "mdNug4ct5645" + }, + "outputs": [], + "source": [ + "# the variable sent0 contains the first sentence of The Catcher in the Rye\n", + "# split into single words\n", + "sent0 = ['If', 'you', 'really', 'want', 'to', 'hear', 'about', 'it,', 'the', \n", + " 'first', 'thing', 'you’ll', 'probably', 'want', 'to', 'know', 'is', \n", + " 'where', 'I', 'was', 'born,', 'and', 'what', 'my', 'lousy', 'childhood', \n", + " 'was', 'like,', 'and', 'how', 'my', 'parents', 'were', 'occupied', \n", + " 'and', 'all', 'before', 'they', 'had', 'me,', 'and', 'all', 'that', \n", + " 'David', 'Copperfield', 'kind', 'of', 'crap,', 'but', 'I', 'don’t', \n", + " 'feel', 'like', 'going', 'into', 'it,', 'if', 'you', 'want', \n", + " 'to', 'know', 'the', 'truth.']" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "executionInfo": { + "elapsed": 3, + "status": "ok", + "timestamp": 1681894603343, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "XGY3qSEk6B9j" + }, + "outputs": [], + "source": [ + "word_counts = count_items(sent0) # we recycle our function from the last exercise" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 307, + "status": "ok", + "timestamp": 1681894948452, + "user": { + "displayName": "Luka van der Plas", + "userId": "16305747382115943293" + }, + "user_tz": -120 + }, + "id": "iS8rDbONnVDr", + "outputId": "c1313777-a4ab-477d-e428-9ae285fc28da" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['and'] 4\n" + ] + } + ], + "source": [ + "def most_frequent(counts):\n", + " '''\n", + " For a dictionary with totals, the most commonly occuring item(s) and the count.\n", + "\n", + " Input should be a dictionary with the total number of occurences for each key in some collection.\n", + " Returns a tuple of two items. First is a list of the most frequent item(s). If the input\n", + " is an empty dict, the list if empty. Second is the number of occurences for that item.\n", + " '''\n", + "\n", + " if not len(counts):\n", + " return [], 0\n", + "\n", + " max_count = max(counts.values())\n", + "\n", + " top_items = []\n", + " for item, count in counts.items():\n", + " if count == max_count:\n", + " top_items.append(item)\n", + "\n", + " return top_items, max_count\n", + "\n", + "words, total = most_frequent(word_counts)\n", + "print(words, total)\n", + "\n", + "# here are some assert statements you could use to check your own function\n", + "# feel free to adapt them if your function gives a different output format\n", + "assert most_frequent(fruit_counts) == (['grape'], 9)\n", + "assert most_frequent(word_counts) == (['and'], 4)\n", + "assert most_frequent({}) == ([], 0)" + ] + } + ], + "metadata": { + "colab": { + "provenance": [ + { + "file_id": "16mecEPkVGUBIFoHIALO7dE8qv7PynHL9", + "timestamp": 1681824660733 + } + ] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/solutions/10 - Dictionaries solutions.py b/solutions/10 - Dictionaries solutions.py new file mode 100644 index 0000000..70896de --- /dev/null +++ b/solutions/10 - Dictionaries solutions.py @@ -0,0 +1,184 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# name: python3 +# --- + +# %% [markdown] id="kjdcNtg3k8Aa" +# # Dictionaries +# +# ### CDH course "Programming in Python" +# +# [index](https://colab.research.google.com/drive/1s05aR4wn2dU1C3se1oXfqKz2EY5ilrno) +# +# Previous module: [9. String manipulation](https://colab.research.google.com/drive/1Z7NNMxqHTSMoUadH3pVL6Bg6oii4qUoQ) +# +# ### This module +# +# - Learn about _dictionaries_, a useful way of storing and looking up data + +# %% [markdown] id="1ezL-XhlvHBq" +# ## Exercise 10.1: Dictionaries + +# %% [markdown] id="rxcz60KQximW" +# 1 . Below are two dictionaries containing information about different types of fruit. Print a nice message about each fruit stating its colour and price. For example, _An apple is red and costs € 2.50_, etc. + +# %% id="yn89oAAZu33C" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1681894457462, "user_tz": -120, "elapsed": 238, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}} outputId="d6b7d53c-d22c-4499-8b59-de508579836e" +fruit_colors = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange'} +fruit_prices = {'apple': 2.50, 'banana': 2.10, 'orange': 1.50} + +# The example sentence includes 'a'/'an' ('an apple' / 'a banana') +# which was a bit of an oversight: the point of this exercise +# was to loop through a dictionary and access values. +# You could make a solution like this to avoid it. + +for fruit in fruit_colors: + color = fruit_colors[fruit] + price = fruit_prices[fruit] + + print(fruit + 's', 'are', color, 'and cost €', price, sep=' ') + +# Bonus points if you DID implement a solution on when to use +# 'a' or 'an'! Here is an example of your you can do that. + +print() + +def starts_with_vowel(word): + ''' + Returns True if the word (a string) starts with a vowel. + ''' + vowels = ['a', 'e', 'i', 'o', 'u'] + if word[0] in vowels: + return True + return False + + +for fruit in fruit_colors: + color = fruit_colors[fruit] + price = fruit_prices[fruit] + + if starts_with_vowel(fruit): + article = 'An' + else: + article = 'A' + + print(article, fruit, 'is', color, 'and costs €', price, sep=' ') + +# %% [markdown] id="Gtp5V9dE0LxK" +# 2 . Here is a longer lists of fruit colours. Write a function `count_fruits` which gets gets a colour as input and returns the number of fruits that have that colour (according to `lots_of_fruit`). + +# %% id="S7gCNyLCxdrO" executionInfo={"status": "ok", "timestamp": 1681894473607, "user_tz": -120, "elapsed": 227, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}} +lots_of_fruit = {'apple': 'red', 'banana': 'yellow', 'orange': 'orange', + 'cucumber': 'green', 'kiwi': 'green', 'strawberry': 'red', + 'pineapple': 'yellow','blackberry': 'black', 'cherry': 'red', + 'gooseberry': 'green', 'raspberry': 'red', 'mandarin': 'orange', + 'lemon': 'yellow', 'lime': 'green'} + + +# %% id="nScDQipK35qN" executionInfo={"status": "ok", "timestamp": 1681894484122, "user_tz": -120, "elapsed": 259, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}} +def count_fruits(color): + '''Count the number of fruits in `lots_of_fruit` that match this colour.''' + count = 0 + for value in lots_of_fruit.values(): + if value == color: + count = count + 1 + return count + +# let's see if it works! +assert count_fruits('red') == 4 +assert count_fruits('lavender') == 0 + +# %% [markdown] id="-Qp6R3Kp3GId" +# 3 . The list `fruit_basket` contains a bunch of fruits. Can you make a dictionary `fruit_counts` which gives the amount for each fruit in `fruit_basket`? (Do not count the fruits by hand!) + +# %% id="awf-lLQO3N1U" executionInfo={"status": "ok", "timestamp": 1681894489366, "user_tz": -120, "elapsed": 217, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}} +fruit_basket = ['apple', 'banana', 'banana', 'banana', 'apple', 'orange', + 'orange', 'grape', 'grape', 'grape', 'grape', 'grape', 'grape', + 'grape', 'grape', 'grape', 'pear', 'apple', 'strawberry', + 'strawberry', 'strawberry', 'orange'] + + +# %% id="MDpIZpbm3-BG" executionInfo={"status": "ok", "timestamp": 1681894585893, "user_tz": -120, "elapsed": 281, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}} +def count_items(items): + ''' + Count the items in a list. + + Input: a list of items, such as strings + Output: a dictionary with the total of occurences for each item. + ''' + + counts = dict() # we will keep track of our counts in here! + + for item in items: + # the current count: either the current value in the dictionary + # or 0 if we haven't seen this fruit yet + current_count = counts.get(item, 0) + new_count = current_count + 1 + counts[item] = new_count + + return counts + +fruit_counts = count_items(fruit_basket) + +# let's see if it works! +assert fruit_counts['apple'] == 3 + + +# %% [markdown] id="h-kJhTO951pc" +# 4 . Here is a different list, which contains the words in a sentence. Can you use your code above to make a dictionary `word_counts` telling us how often each word occurs? (Tip: if you need to do very similar tasks, make a function!) +# +# Write a function that takes a dictionary like `word_counts` tells us the most commonly occuring item and the count. Note that there can be multiple items that occurred the most. + +# %% id="mdNug4ct5645" executionInfo={"status": "ok", "timestamp": 1681894602379, "user_tz": -120, "elapsed": 241, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}} +# the variable sent0 contains the first sentence of The Catcher in the Rye +# split into single words +sent0 = ['If', 'you', 'really', 'want', 'to', 'hear', 'about', 'it,', 'the', + 'first', 'thing', 'you’ll', 'probably', 'want', 'to', 'know', 'is', + 'where', 'I', 'was', 'born,', 'and', 'what', 'my', 'lousy', 'childhood', + 'was', 'like,', 'and', 'how', 'my', 'parents', 'were', 'occupied', + 'and', 'all', 'before', 'they', 'had', 'me,', 'and', 'all', 'that', + 'David', 'Copperfield', 'kind', 'of', 'crap,', 'but', 'I', 'don’t', + 'feel', 'like', 'going', 'into', 'it,', 'if', 'you', 'want', + 'to', 'know', 'the', 'truth.'] + +# %% id="XGY3qSEk6B9j" executionInfo={"status": "ok", "timestamp": 1681894603343, "user_tz": -120, "elapsed": 3, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}} +word_counts = count_items(sent0) # we recycle our function from the last exercise + + +# %% colab={"base_uri": "https://localhost:8080/"} id="iS8rDbONnVDr" executionInfo={"status": "ok", "timestamp": 1681894948452, "user_tz": -120, "elapsed": 307, "user": {"displayName": "Luka van der Plas", "userId": "16305747382115943293"}} outputId="c1313777-a4ab-477d-e428-9ae285fc28da" +def most_frequent(counts): + ''' + For a dictionary with totals, the most commonly occuring item(s) and the count. + + Input should be a dictionary with the total number of occurences for each key in some collection. + Returns a tuple of two items. First is a list of the most frequent item(s). If the input + is an empty dict, the list if empty. Second is the number of occurences for that item. + ''' + + if not len(counts): + return [], 0 + + max_count = max(counts.values()) + + top_items = [] + for item, count in counts.items(): + if count == max_count: + top_items.append(item) + + return top_items, max_count + +words, total = most_frequent(word_counts) +print(words, total) + +# here are some assert statements you could use to check your own function +# feel free to adapt them if your function gives a different output format +assert most_frequent(fruit_counts) == (['grape'], 9) +assert most_frequent(word_counts) == (['and'], 4) +assert most_frequent({}) == ([], 0) diff --git a/solutions/11 working with files solutions.ipynb b/solutions/11 working with files solutions.ipynb index 530a8ec..4a8d7ea 100644 --- a/solutions/11 working with files solutions.ipynb +++ b/solutions/11 working with files solutions.ipynb @@ -1 +1,419 @@ -{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"provenance":[],"authorship_tag":"ABX9TyNBT3v2osSyHD05LL0HU2aj"},"kernelspec":{"name":"python3","display_name":"Python 3"},"language_info":{"name":"python"}},"cells":[{"cell_type":"markdown","source":["## Exercise 11.1 - Files\n","In the code block below, try to predict what will be printed, then run the code."],"metadata":{"id":"YC0q3Z4HiM5Z"}},{"cell_type":"code","source":["PATH = 'sample_data/california_housing_test.csv'\n","file = open(PATH)\n","print(file)\n","print(file.read())\n","print(file.readlines())\n","file.close()\n","print(file)"],"metadata":{"id":"vZtR4360i7kh"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["`print(file)` prints the File Object. There is no way to determine by printing if this is still open, or has been closed."],"metadata":{"id":"F9f99yil6tQQ"}},{"cell_type":"markdown","source":["Read the file `sampledata/california_housing_test.csv'`, and print the first row containing data as a list.\n","\n","Are the data in the correct format? Try to fix it if not."],"metadata":{"id":"BXOkW2J7ldYK"}},{"cell_type":"code","source":["PATH = 'sample_data/california_housing_test.csv'\n","file = open(PATH)\n","lines = file.readlines()\n","\n","# Read the first row\n","first_row = lines[0]\n","print(first_row)"],"metadata":{"id":"eP_TfHcmlsce","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1681914737357,"user_tz":-120,"elapsed":202,"user":{"displayName":"Jelte van Boheemen","userId":"01262930844892157638"}},"outputId":"b58e166d-1764-4e0f-91e2-7ea872519052"},"execution_count":4,"outputs":[{"output_type":"stream","name":"stdout","text":["\"longitude\",\"latitude\",\"housing_median_age\",\"total_rooms\",\"total_bedrooms\",\"population\",\"households\",\"median_income\",\"median_house_value\"\n","\n"]}]},{"cell_type":"markdown","source":["Note that this prints the header row, as one big string. Not really what we are looking for!"],"metadata":{"id":"9Bobwjk07Ssq"}},{"cell_type":"code","source":["# Split the file into header row and data rows\n","header, *data = lines\n","first_row = data[0]\n","print(first_row)\n","print(type(first_row))"],"metadata":{"id":"bwxxok8a7Wyy"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["This is the first actual row of data, but still as a big string."],"metadata":{"id":"JZeGlr5p7nlV"}},{"cell_type":"code","source":["columns = first_row.split(',')\n","print(columns)\n","print(type(columns[0]))"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"g4XFr9pG7qor","executionInfo":{"status":"ok","timestamp":1681914801091,"user_tz":-120,"elapsed":2,"user":{"displayName":"Jelte van Boheemen","userId":"01262930844892157638"}},"outputId":"ee3cb701-edf8-44f2-c28b-593f1d33acef"},"execution_count":9,"outputs":[{"output_type":"stream","name":"stdout","text":["['-122.050000', '37.370000', '27.000000', '3885.000000', '661.000000', '1537.000000', '606.000000', '6.608500', '344700.000000\\n']\n","\n"]}]},{"cell_type":"markdown","source":["The row is split into columns. They still contain strings, not numbers. And the last value includes the newline character `\\n`"],"metadata":{"id":"EuCKWl9z7zUf"}},{"cell_type":"code","source":["def clean_value(value):\n"," '''\n"," Clean a single value. Assumes value is a string representation\n"," of a number.\n"," - remove any whitespace characters\n"," - convert the value into a float\n"," - check if it is an integer, if so, convert to an actual int\n"," - return the result\n"," '''\n"," clean_value = value.strip()\n"," float_value = float(clean_value)\n"," if float_value.is_integer():\n"," return int(float_value)\n"," return float_value\n","\n","\n","def clean_values(values):\n"," '''\n"," Cleans an entire row or column using clean_column.\n"," Assumes values is a list of string representations\n"," of numbers.\n"," '''\n"," cleaned_row = []\n"," for value in values:\n"," cleaned_row.append(clean_value(value))\n"," return cleaned_row\n","\n","\n","assert clean_values(columns) == [-122.05, 37.37, 27, 3885, 661, 1537, 606, 6.6085, 344700]"],"metadata":{"id":"BnpSq28I766w","executionInfo":{"status":"ok","timestamp":1681916502255,"user_tz":-120,"elapsed":226,"user":{"displayName":"Jelte van Boheemen","userId":"01262930844892157638"}}},"execution_count":36,"outputs":[]},{"cell_type":"markdown","source":["## Exercise 11.2: Bonus - working with csv datasets\n","> 💡 If you wish to perform an analysis on your own dataset in the upcoming sessions, make sure you complete this exercise. The code you write here can be reused in your own analysis.\n","\n","1. Upload your own dataset, or use `sample_data/california_housing_test.csv`\n"," 1. Read the data from the CSV file\n"," 2. Write functions that take the data as a parameter, and return the following items:\n"," - The last row\n"," - The last column\n"," - The first row. If this is a header row, try if you can come up with a way to separate it from the data rows.\n"," - Calculate the *harmonic mean* of a column containing numbers. Search the [standard library](https://docs.python.org/3.7/library/index.html) for a a relevant module containing the code to do so.\n"," 3. Add an extra column to the data (think of something that makes sense). Write the whole dataset to a new csv file."],"metadata":{"id":"h-mOS3pwoc1O"}},{"cell_type":"markdown","source":["#### 11.2.1"],"metadata":{"id":"nqZZLHi4-HyR"}},{"cell_type":"code","source":["import csv\n","PATH = 'sample_data/california_housing_test.csv'\n","\n","def read_data():\n"," with open(PATH) as csv_file:\n"," reader = csv.reader(csv_file, delimiter=',')\n"," return list(reader)\n"," \n","assert len(read_data()) == 3001"],"metadata":{"id":"3UI9XbgN-Ffb","executionInfo":{"status":"ok","timestamp":1681915598793,"user_tz":-120,"elapsed":194,"user":{"displayName":"Jelte van Boheemen","userId":"01262930844892157638"}}},"execution_count":23,"outputs":[]},{"cell_type":"markdown","source":["#### 11.2.2\n","This may seem like a whole load of code just to calculate some stuff.\n","\n","But notice that by definding general functions, we create a toolkit that we can apply to any future problems! Think ahead!"],"metadata":{"id":"6PUt-Fni-3g0"}},{"cell_type":"code","source":["def get_row(data, row_index):\n"," '''\n"," Returns the row at row_index\n"," '''\n"," return data[row_index]\n","\n","\n","def get_column(data, column_index):\n"," '''\n"," Returns the column at column_index\n"," '''\n"," column = []\n"," for row in data:\n"," column.append(row[column_index])\n"," return column\n","\n","\n","def separate_headers(data):\n"," '''\n"," Separates tabular data into a header row and data rows\n"," '''\n"," header_row, *data_rows = data\n"," return header_row, data_rows\n","\n","\n","def multiply(num1, num2):\n"," return num1 * num2\n","\n","\n","def combine_columns(column_1, column_2, how):\n"," '''\n"," created a new column based on two existing columns\n"," how should be a function that accepts two numbers and returns a number\n"," column_1 and column_2 should contain numerical data\n"," '''\n"," new_column = []\n"," for index, value_1 in enumerate(column_1):\n"," value_2 = column_2[index]\n"," new_column.append(how(value_1, value_2))\n"," return new_column\n","\n","\n","\n","def add_column(data, column_name, column_data):\n"," '''\n"," Adds an extra column to the end of the data\n"," Assumes data containts tabular data WITH headers\n"," colum_data size should match existing table\n"," '''\n"," headers, data_rows = separate_headers(data)\n"," headers = [headers + [column_name]]\n","\n"," new_rows = []\n"," for index, row in enumerate(data_rows):\n"," new_row = row + [column_data[index]]\n"," new_rows.append(new_row)\n"," return headers + new_rows\n","\n","def write_table_to_csv(data, filepath):\n"," with open(filepath, mode='w') as f:\n"," writer = csv.writer(f)\n","\n"," for row in data:\n"," writer.writerow(row)\n","\n","\n","# read the data and get the last row\n","data = read_data()\n","last_row = get_row(data, -1)\n","print(last_row)\n","\n","# determine the number of columns, and use this to get the last column\n","number_of_columns = len(last_row) - 1 # remember that list indices start at 0!\n","last_column = get_column(data, number_of_columns)\n","print(last_column)\n","\n","# get the first data row\n","headers, data_rows = separate_headers(data)\n","first_row = data_rows[0]\n","print(first_row)\n","\n","# get the index of the 'median_income' column\n","income_index = header.index('median_income')\n","# get the column data\n","income = get_column(data_rows, income_index)\n","# convert the data to numbers\n","income_numerical = clean_values(income)\n","# finally, calculate the harmonic mean\n","import statistics\n","harmonic_mean = statistics.harmonic_mean(income_numerical)\n","print(harmonic_mean)\n","\n","# add a new column\n","# get households the same way we did income, this time on one line\n","households_numerical = clean_values(get_column(data_rows, header.index('households')))\n","# combine households and income by multiplying them\n","total_income = combine_columns(income_numerical, households_numerical, multiply)\n","new_data = add_column(data, 'total_income', total_income)\n","# finally, write to a new file\n","write_table_to_csv(new_data, 'sample_data/california_housing_test_expanded.csv')\n"],"metadata":{"id":"wOEhjWRq-6QL"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":[],"metadata":{"id":"UuvUPP01_uUL"}}]} \ No newline at end of file +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "YC0q3Z4HiM5Z" + }, + "source": [ + "## Exercise 11.1 - Files\n", + "In the code block below, try to predict what will be printed, then run the code." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "vZtR4360i7kh" + }, + "outputs": [], + "source": [ + "PATH = 'sample_data/california_housing_test.csv'\n", + "file = open(PATH)\n", + "print(file)\n", + "print(file.read())\n", + "print(file.readlines())\n", + "file.close()\n", + "print(file)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "F9f99yil6tQQ" + }, + "source": [ + "`print(file)` prints the File Object. There is no way to determine by printing if this is still open, or has been closed." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BXOkW2J7ldYK" + }, + "source": [ + "Read the file `sampledata/california_housing_test.csv'`, and print the first row containing data as a list.\n", + "\n", + "Are the data in the correct format? Try to fix it if not." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 202, + "status": "ok", + "timestamp": 1681914737357, + "user": { + "displayName": "Jelte van Boheemen", + "userId": "01262930844892157638" + }, + "user_tz": -120 + }, + "id": "eP_TfHcmlsce", + "outputId": "b58e166d-1764-4e0f-91e2-7ea872519052" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\"longitude\",\"latitude\",\"housing_median_age\",\"total_rooms\",\"total_bedrooms\",\"population\",\"households\",\"median_income\",\"median_house_value\"\n", + "\n" + ] + } + ], + "source": [ + "PATH = 'sample_data/california_housing_test.csv'\n", + "file = open(PATH)\n", + "lines = file.readlines()\n", + "\n", + "# Read the first row\n", + "first_row = lines[0]\n", + "print(first_row)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9Bobwjk07Ssq" + }, + "source": [ + "Note that this prints the header row, as one big string. Not really what we are looking for!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "bwxxok8a7Wyy" + }, + "outputs": [], + "source": [ + "# Split the file into header row and data rows\n", + "header, *data = lines\n", + "first_row = data[0]\n", + "print(first_row)\n", + "print(type(first_row))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "JZeGlr5p7nlV" + }, + "source": [ + "This is the first actual row of data, but still as a big string." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 2, + "status": "ok", + "timestamp": 1681914801091, + "user": { + "displayName": "Jelte van Boheemen", + "userId": "01262930844892157638" + }, + "user_tz": -120 + }, + "id": "g4XFr9pG7qor", + "outputId": "ee3cb701-edf8-44f2-c28b-593f1d33acef" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['-122.050000', '37.370000', '27.000000', '3885.000000', '661.000000', '1537.000000', '606.000000', '6.608500', '344700.000000\\n']\n", + "\n" + ] + } + ], + "source": [ + "columns = first_row.split(',')\n", + "print(columns)\n", + "print(type(columns[0]))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "EuCKWl9z7zUf" + }, + "source": [ + "The row is split into columns. They still contain strings, not numbers. And the last value includes the newline character `\\n`" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "executionInfo": { + "elapsed": 226, + "status": "ok", + "timestamp": 1681916502255, + "user": { + "displayName": "Jelte van Boheemen", + "userId": "01262930844892157638" + }, + "user_tz": -120 + }, + "id": "BnpSq28I766w" + }, + "outputs": [], + "source": [ + "def clean_value(value):\n", + " '''\n", + " Clean a single value. Assumes value is a string representation\n", + " of a number.\n", + " - remove any whitespace characters\n", + " - convert the value into a float\n", + " - check if it is an integer, if so, convert to an actual int\n", + " - return the result\n", + " '''\n", + " clean_value = value.strip()\n", + " float_value = float(clean_value)\n", + " if float_value.is_integer():\n", + " return int(float_value)\n", + " return float_value\n", + "\n", + "\n", + "def clean_values(values):\n", + " '''\n", + " Cleans an entire row or column using clean_column.\n", + " Assumes values is a list of string representations\n", + " of numbers.\n", + " '''\n", + " cleaned_row = []\n", + " for value in values:\n", + " cleaned_row.append(clean_value(value))\n", + " return cleaned_row\n", + "\n", + "\n", + "assert clean_values(columns) == [-122.05, 37.37, 27, 3885, 661, 1537, 606, 6.6085, 344700]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "h-mOS3pwoc1O" + }, + "source": [ + "## Exercise 11.2: Bonus - working with csv datasets\n", + "> 💡 If you wish to perform an analysis on your own dataset in the upcoming sessions, make sure you complete this exercise. The code you write here can be reused in your own analysis.\n", + "\n", + "1. Upload your own dataset, or use `sample_data/california_housing_test.csv`\n", + " 1. Read the data from the CSV file\n", + " 2. Write functions that take the data as a parameter, and return the following items:\n", + " - The last row\n", + " - The last column\n", + " - The first row. If this is a header row, try if you can come up with a way to separate it from the data rows.\n", + " - Calculate the *harmonic mean* of a column containing numbers. Search the [standard library](https://docs.python.org/3.7/library/index.html) for a a relevant module containing the code to do so.\n", + " 3. Add an extra column to the data (think of something that makes sense). Write the whole dataset to a new csv file." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "nqZZLHi4-HyR" + }, + "source": [ + "#### 11.2.1" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "executionInfo": { + "elapsed": 194, + "status": "ok", + "timestamp": 1681915598793, + "user": { + "displayName": "Jelte van Boheemen", + "userId": "01262930844892157638" + }, + "user_tz": -120 + }, + "id": "3UI9XbgN-Ffb" + }, + "outputs": [], + "source": [ + "import csv\n", + "PATH = 'sample_data/california_housing_test.csv'\n", + "\n", + "def read_data():\n", + " with open(PATH) as csv_file:\n", + " reader = csv.reader(csv_file, delimiter=',')\n", + " return list(reader)\n", + " \n", + "assert len(read_data()) == 3001" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6PUt-Fni-3g0" + }, + "source": [ + "#### 11.2.2\n", + "This may seem like a whole load of code just to calculate some stuff.\n", + "\n", + "But notice that by definding general functions, we create a toolkit that we can apply to any future problems! Think ahead!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "wOEhjWRq-6QL" + }, + "outputs": [], + "source": [ + "def get_row(data, row_index):\n", + " '''\n", + " Returns the row at row_index\n", + " '''\n", + " return data[row_index]\n", + "\n", + "\n", + "def get_column(data, column_index):\n", + " '''\n", + " Returns the column at column_index\n", + " '''\n", + " column = []\n", + " for row in data:\n", + " column.append(row[column_index])\n", + " return column\n", + "\n", + "\n", + "def separate_headers(data):\n", + " '''\n", + " Separates tabular data into a header row and data rows\n", + " '''\n", + " header_row, *data_rows = data\n", + " return header_row, data_rows\n", + "\n", + "\n", + "def multiply(num1, num2):\n", + " return num1 * num2\n", + "\n", + "\n", + "def combine_columns(column_1, column_2, how):\n", + " '''\n", + " created a new column based on two existing columns\n", + " how should be a function that accepts two numbers and returns a number\n", + " column_1 and column_2 should contain numerical data\n", + " '''\n", + " new_column = []\n", + " for index, value_1 in enumerate(column_1):\n", + " value_2 = column_2[index]\n", + " new_column.append(how(value_1, value_2))\n", + " return new_column\n", + "\n", + "\n", + "\n", + "def add_column(data, column_name, column_data):\n", + " '''\n", + " Adds an extra column to the end of the data\n", + " Assumes data containts tabular data WITH headers\n", + " colum_data size should match existing table\n", + " '''\n", + " headers, data_rows = separate_headers(data)\n", + " headers = [headers + [column_name]]\n", + "\n", + " new_rows = []\n", + " for index, row in enumerate(data_rows):\n", + " new_row = row + [column_data[index]]\n", + " new_rows.append(new_row)\n", + " return headers + new_rows\n", + "\n", + "def write_table_to_csv(data, filepath):\n", + " with open(filepath, mode='w') as f:\n", + " writer = csv.writer(f)\n", + "\n", + " for row in data:\n", + " writer.writerow(row)\n", + "\n", + "\n", + "# read the data and get the last row\n", + "data = read_data()\n", + "last_row = get_row(data, -1)\n", + "print(last_row)\n", + "\n", + "# determine the number of columns, and use this to get the last column\n", + "number_of_columns = len(last_row) - 1 # remember that list indices start at 0!\n", + "last_column = get_column(data, number_of_columns)\n", + "print(last_column)\n", + "\n", + "# get the first data row\n", + "headers, data_rows = separate_headers(data)\n", + "first_row = data_rows[0]\n", + "print(first_row)\n", + "\n", + "# get the index of the 'median_income' column\n", + "income_index = header.index('median_income')\n", + "# get the column data\n", + "income = get_column(data_rows, income_index)\n", + "# convert the data to numbers\n", + "income_numerical = clean_values(income)\n", + "# finally, calculate the harmonic mean\n", + "import statistics\n", + "harmonic_mean = statistics.harmonic_mean(income_numerical)\n", + "print(harmonic_mean)\n", + "\n", + "# add a new column\n", + "# get households the same way we did income, this time on one line\n", + "households_numerical = clean_values(get_column(data_rows, header.index('households')))\n", + "# combine households and income by multiplying them\n", + "total_income = combine_columns(income_numerical, households_numerical, multiply)\n", + "new_data = add_column(data, 'total_income', total_income)\n", + "# finally, write to a new file\n", + "write_table_to_csv(new_data, 'sample_data/california_housing_test_expanded.csv')\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "UuvUPP01_uUL" + }, + "source": [] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyNBT3v2osSyHD05LL0HU2aj", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/solutions/11 working with files solutions.py b/solutions/11 working with files solutions.py new file mode 100644 index 0000000..d1974cc --- /dev/null +++ b/solutions/11 working with files solutions.py @@ -0,0 +1,235 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# name: python3 +# --- + +# %% [markdown] id="YC0q3Z4HiM5Z" +# ## Exercise 11.1 - Files +# In the code block below, try to predict what will be printed, then run the code. + +# %% id="vZtR4360i7kh" +PATH = 'sample_data/california_housing_test.csv' +file = open(PATH) +print(file) +print(file.read()) +print(file.readlines()) +file.close() +print(file) + +# %% [markdown] id="F9f99yil6tQQ" +# `print(file)` prints the File Object. There is no way to determine by printing if this is still open, or has been closed. + +# %% [markdown] id="BXOkW2J7ldYK" +# Read the file `sampledata/california_housing_test.csv'`, and print the first row containing data as a list. +# +# Are the data in the correct format? Try to fix it if not. + +# %% id="eP_TfHcmlsce" colab={"base_uri": "https://localhost:8080/"} executionInfo={"status": "ok", "timestamp": 1681914737357, "user_tz": -120, "elapsed": 202, "user": {"displayName": "Jelte van Boheemen", "userId": "01262930844892157638"}} outputId="b58e166d-1764-4e0f-91e2-7ea872519052" +PATH = 'sample_data/california_housing_test.csv' +file = open(PATH) +lines = file.readlines() + +# Read the first row +first_row = lines[0] +print(first_row) + +# %% [markdown] id="9Bobwjk07Ssq" +# Note that this prints the header row, as one big string. Not really what we are looking for! + +# %% id="bwxxok8a7Wyy" +# Split the file into header row and data rows +header, *data = lines +first_row = data[0] +print(first_row) +print(type(first_row)) + +# %% [markdown] id="JZeGlr5p7nlV" +# This is the first actual row of data, but still as a big string. + +# %% colab={"base_uri": "https://localhost:8080/"} id="g4XFr9pG7qor" executionInfo={"status": "ok", "timestamp": 1681914801091, "user_tz": -120, "elapsed": 2, "user": {"displayName": "Jelte van Boheemen", "userId": "01262930844892157638"}} outputId="ee3cb701-edf8-44f2-c28b-593f1d33acef" +columns = first_row.split(',') +print(columns) +print(type(columns[0])) + + +# %% [markdown] id="EuCKWl9z7zUf" +# The row is split into columns. They still contain strings, not numbers. And the last value includes the newline character `\n` + +# %% id="BnpSq28I766w" executionInfo={"status": "ok", "timestamp": 1681916502255, "user_tz": -120, "elapsed": 226, "user": {"displayName": "Jelte van Boheemen", "userId": "01262930844892157638"}} +def clean_value(value): + ''' + Clean a single value. Assumes value is a string representation + of a number. + - remove any whitespace characters + - convert the value into a float + - check if it is an integer, if so, convert to an actual int + - return the result + ''' + clean_value = value.strip() + float_value = float(clean_value) + if float_value.is_integer(): + return int(float_value) + return float_value + + +def clean_values(values): + ''' + Cleans an entire row or column using clean_column. + Assumes values is a list of string representations + of numbers. + ''' + cleaned_row = [] + for value in values: + cleaned_row.append(clean_value(value)) + return cleaned_row + + +assert clean_values(columns) == [-122.05, 37.37, 27, 3885, 661, 1537, 606, 6.6085, 344700] + +# %% [markdown] id="h-mOS3pwoc1O" +# ## Exercise 11.2: Bonus - working with csv datasets +# > 💡 If you wish to perform an analysis on your own dataset in the upcoming sessions, make sure you complete this exercise. The code you write here can be reused in your own analysis. +# +# 1. Upload your own dataset, or use `sample_data/california_housing_test.csv` +# 1. Read the data from the CSV file +# 2. Write functions that take the data as a parameter, and return the following items: +# - The last row +# - The last column +# - The first row. If this is a header row, try if you can come up with a way to separate it from the data rows. +# - Calculate the *harmonic mean* of a column containing numbers. Search the [standard library](https://docs.python.org/3.7/library/index.html) for a a relevant module containing the code to do so. +# 3. Add an extra column to the data (think of something that makes sense). Write the whole dataset to a new csv file. + +# %% [markdown] id="nqZZLHi4-HyR" +# #### 11.2.1 + +# %% id="3UI9XbgN-Ffb" executionInfo={"status": "ok", "timestamp": 1681915598793, "user_tz": -120, "elapsed": 194, "user": {"displayName": "Jelte van Boheemen", "userId": "01262930844892157638"}} +import csv +PATH = 'sample_data/california_housing_test.csv' + +def read_data(): + with open(PATH) as csv_file: + reader = csv.reader(csv_file, delimiter=',') + return list(reader) + +assert len(read_data()) == 3001 + + +# %% [markdown] id="6PUt-Fni-3g0" +# #### 11.2.2 +# This may seem like a whole load of code just to calculate some stuff. +# +# But notice that by definding general functions, we create a toolkit that we can apply to any future problems! Think ahead! + +# %% id="wOEhjWRq-6QL" +def get_row(data, row_index): + ''' + Returns the row at row_index + ''' + return data[row_index] + + +def get_column(data, column_index): + ''' + Returns the column at column_index + ''' + column = [] + for row in data: + column.append(row[column_index]) + return column + + +def separate_headers(data): + ''' + Separates tabular data into a header row and data rows + ''' + header_row, *data_rows = data + return header_row, data_rows + + +def multiply(num1, num2): + return num1 * num2 + + +def combine_columns(column_1, column_2, how): + ''' + created a new column based on two existing columns + how should be a function that accepts two numbers and returns a number + column_1 and column_2 should contain numerical data + ''' + new_column = [] + for index, value_1 in enumerate(column_1): + value_2 = column_2[index] + new_column.append(how(value_1, value_2)) + return new_column + + + +def add_column(data, column_name, column_data): + ''' + Adds an extra column to the end of the data + Assumes data containts tabular data WITH headers + colum_data size should match existing table + ''' + headers, data_rows = separate_headers(data) + headers = [headers + [column_name]] + + new_rows = [] + for index, row in enumerate(data_rows): + new_row = row + [column_data[index]] + new_rows.append(new_row) + return headers + new_rows + +def write_table_to_csv(data, filepath): + with open(filepath, mode='w') as f: + writer = csv.writer(f) + + for row in data: + writer.writerow(row) + + +# read the data and get the last row +data = read_data() +last_row = get_row(data, -1) +print(last_row) + +# determine the number of columns, and use this to get the last column +number_of_columns = len(last_row) - 1 # remember that list indices start at 0! +last_column = get_column(data, number_of_columns) +print(last_column) + +# get the first data row +headers, data_rows = separate_headers(data) +first_row = data_rows[0] +print(first_row) + +# get the index of the 'median_income' column +income_index = header.index('median_income') +# get the column data +income = get_column(data_rows, income_index) +# convert the data to numbers +income_numerical = clean_values(income) +# finally, calculate the harmonic mean +import statistics +harmonic_mean = statistics.harmonic_mean(income_numerical) +print(harmonic_mean) + +# add a new column +# get households the same way we did income, this time on one line +households_numerical = clean_values(get_column(data_rows, header.index('households'))) +# combine households and income by multiplying them +total_income = combine_columns(income_numerical, households_numerical, multiply) +new_data = add_column(data, 'total_income', total_income) +# finally, write to a new file +write_table_to_csv(new_data, 'sample_data/california_housing_test_expanded.csv') + + +# %% [markdown] id="UuvUPP01_uUL" +# diff --git a/solutions/Extra exercises day 1 solutions.ipynb b/solutions/Extra exercises day 1 solutions.ipynb index c27aa92..411758b 100644 --- a/solutions/Extra exercises day 1 solutions.ipynb +++ b/solutions/Extra exercises day 1 solutions.ipynb @@ -575,7 +575,6 @@ "pygments_lexer": "ipython3", "version": "3.11.2" }, - "orig_nbformat": 4, "vscode": { "interpreter": { "hash": "2cc67e3a9982c67c9491f847535e93961a1a3bd6725cdcb43113c9880de5dac3" diff --git a/solutions/Extra exercises day 1 solutions.py b/solutions/Extra exercises day 1 solutions.py new file mode 100644 index 0000000..82338e7 --- /dev/null +++ b/solutions/Extra exercises day 1 solutions.py @@ -0,0 +1,199 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# language: python +# name: python3 +# --- + +# %% [markdown] +# Hierbij wat extra opdrachtjes voor de modules van dag 1! + +# %% [markdown] +# # Types +# ## Exercise A +# useful for after 1.4 +# +# 1. Create one variable each for every following type (replace None with an example): +# - Integer (`int`) +# - Floating point (`float`) +# - Boolean (`bool`) +# - String (`str`) + +# %% +example_int = 1 +example_float = 2.0 +example_bool = True +example_string = 'four' + +# %% [markdown] +# Did you know that you can check the type of a variable in Python? If you are ever unsure of the type of a variable, use the `type()` function to check, e.g.: + +# %% +print(type(1)) +print(type(1.0)) +print(type(False)) +print(type('one')) + +# %% [markdown] +# 2. Now try and check the types of the examples you just declared using the method above: + +# %% +## Space for exercise +print(type(example_int)) +print(type(example_float)) +print(type(example_bool)) +print(type(example_string)) + +# %% [markdown] +# Python is a 'dynamically typed' language, which means that the type of a variable can vary based on its context or user coercion. For example, you can coerce an integer to a float by declaring: + +# %% +example_integer = 2 # declare example integer +print(example_integer, type(example_integer)) #print the integer and its type + +coerced_float = float(example_integer) +print(coerced_float, type(coerced_float)) + +# %% [markdown] +# You can coerce these types manually or Python sometimes does it automatically, for example when you try and execute operations on a variable that requires a certain type. For instance, if you try to add the integer and float above, the result of that operation will be a float. Python automatically coerces the integer to a float to complete the addition. +# +# 3. For this exercise, try to perform some operations on these variables below. Which types can be automatically added together? And what type is the result of their operation? What happens when you try a different operation, such as subtraction? + +# %% +ex_int = 1 +ex_float = 2.0 +ex_string = 'three' +ex_bool = True + +ex_result = ex_int + ex_float # example operation +print(ex_result, type(ex_result)) + +result2 = ex_float + ex_float +print(result2, type(result2)) + +result3 = ex_float + ex_bool +print(result3, type(result3)) + +result4 = ex_string + ex_string +print(result4, type(result4)) + +result5 = ex_bool + ex_int +print(result5, type(result5)) + +# %% [markdown] +# # Lists +# ## Exercise B +# Let's make some lists! +# 1. Start by declaring an empty list (`empty_list = []`). + +# %% +my_list = [] + +# %% [markdown] +# You can check the length of the list with the `len()` function: + +# %% +print(len(my_list)) + +# %% [markdown] +# 2. Now, add some elements to your list. Then check the length of the list again! + +# %% +## Space for exercise +my_list.append([1, 2.0, True, 'Four']) +my_list.append(example_float) +my_list.append(example_int) +len(my_list) + +# %% [markdown] +# # Loops +# ## Exercise C + +# %% [markdown] +# 1. Now that you have your list, let's walk through it and print every element in it. Hint: use the `for` loop. + +# %% +## Space for exercise +for item in my_list: + print(item) + +# %% [markdown] +# 2. Instead of simply printing the elements, see if you can print the result of an operation with each element (for example, print the element + 1 if all your list elements are numbers or booleans). Alternatively, see if you can print the type of each element in the list. + +# %% +## Space for exercise +for item in my_list: + print(type(item)) + +# %% [markdown] +# # Logical Operators +# ## Exercise D +# Python allows the use of several logical operators, so to get you in the mindset of a computer, see if you can determine whether an expression is going to return `True` or `False`. After you have given your answer, run the code in the cell to see if you were correct! + +# %% +# Declaration of the variables used in the exercise +n1 = 1 +n2 = 2 +n3 = 3.4 +n4 = 4.5 +s1 = 'Hello' +s2 = 'World' +s3 = 'Hallo' +s4 = 'Wereld' +l1 = [n1, n2, n3, n4] +l2 = [s1, s2, s3, s4] + +# %% +#Example 1 +n1 == n2 + +# %% [markdown] +# True or False? + +# %% +#Example 2 +n1 + n2 == 3 + +# %% +#Example 3 +n1 + n3 != 3.4 + +# %% [markdown] +# True or False? + +# %% +#Example 4 +s1 + s2 == "Hello World" + +# %% [markdown] +# True or False? + +# %% +#Example 5 +s3 == "hallo" + +# %% [markdown] +# True or False? + +# %% +#Example 6 +len(l1) == len(l2) + +# %% [markdown] +# True or False? + +# %% +#Example 7 +len(s3) == len(s1) + +# %% +##Example 8 +for item in l2: + print(len(item) == 5)