diff --git a/.devcontainer.json b/.devcontainer.json new file mode 100644 index 000000000..922a1238d --- /dev/null +++ b/.devcontainer.json @@ -0,0 +1,89 @@ +// (delete the above line to manage this file manually) +////////////////////////////////////////////////////////////// +// +// This file provides configuration options so that a PreTeXt +// project can be edited and built using GitHub's Codespaces. +// It is recommended to keep this in your repository even if you +// do not use this feature, as it will allow other to explore +// your project easily. +// This file will be automatically generated by PreTeXt with the +// latest updates unless you remove the first comment line above. +// +/////////////////////////////////////////////////////////////// + +// commented out because we're not using this but ptx generates it automatically. + +// { +// "name": "PreTeXt-Codespaces", + +// // This Docker image includes some LaTeX support, but is still not to large. Note that if you keep your codespace running, it will use up your GitHub free storage quota. Additional options are listed below. +// "image": "oscarlevin/pretext:small", +// // If you need to generate more complicated assets (such as sageplots) or use additional fonts when building to PDF, comment out the above line and uncomment the following line. +// // "image": "oscarlevin/pretext:full", +// // If you only intend to build for web and don't have any latex-image generated assets, you can use a smaller image: +// // "image": "oscarlevin/pretext:lite", + +// // Add gh cli as a feature (to support codechat) +// "features": { +// "ghcr.io/devcontainers/features/github-cli:1": {} +// }, + +// // Respect the project's designated dependencies +// "postCreateCommand": "pip install -r requirements.txt", + +// // Port forwarding +// // --------------- +// // This is needed by the CodeChat Server. +// "forwardPorts": [ +// // The port used for a Thrift connection between the VSCode CodeChat +// // extension and the CodeChat Server. +// 27376, +// // The port used for an HTTP connection from the CodeChat Client to +// // the CodeChat Server. +// 27377, +// // The port used by a websocket connection between the CodeChat +// // Server and the CodeChat Client. +// 27378 +// ], +// // See the [docs](https://containers.dev/implementors/json_reference/#port-attributes). +// "portsAttributes": { +// "27376": { +// "label": "VSCode extension <-> CodeChat Server", +// "requireLocalPort": true +// }, +// "27377": { +// "label": "CodeChat Client", +// "requireLocalPort": true +// }, +// "27378": { +// "label": "CodeChat Client<->Server websocket", +// "requireLocalPort": true +// // This port needs to be public; however, there's no way to specify port visibility here. See `server.py` in the CodeChat Server for details. +// } +// }, + +// // Configure tool-specific properties. +// "customizations": { +// "codespaces": { +// "openFiles": ["source/main.ptx"] +// }, +// "vscode": { +// "settings": { +// "editor.quickSuggestions": { +// "other": "off" +// }, +// "editor.snippetSuggestions": "top", +// "xml.validation.enabled": false, +// "CodeChat.CodeChatServer.Command": "CodeChat_Server" +// }, +// "extensions": [ +// "ms-vscode.live-server", +// "oscarlevin.pretext-tools", +// "CodeChat.codechat" +// ] +// } +// } + +// // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. +// // "remoteUser": "root" +// } diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..efa5f10a4 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,57 @@ +{ + "name": "PreTeXt-Codespaces", + + // This Docker image includes some LaTeX support, but is still not to large. Note that if you keep your codespace running, it will use up your GitHub free storage quota. Additional options are listed below. + "image": "oscarlevin/pretext:small", + // If you need to generate more complicated assets (such as sageplots) or use additional fonts when building to PDF, comment out the above line and uncomment the following line. + // "image": "oscarlevin/pretext:full", + // If you only intend to build for web and don't have any latex-image generated assets, you can use a smaller image: + // "image": "oscarlevin/pretext:lite", + + + + // The following was the previous version of this file, which used the Codespaces base image. It is still available for reference, but is not recommended. + // "image": "mcr.microsoft.com/devcontainers/python:3", + // "features": { + // "ghcr.io/devcontainers/features/node:1": {}, + // "ghcr.io/rocker-org/devcontainer-features/pandoc:1": {} + // }, + // "forwardPorts": [ + // 27377, + // 27378 + // ], + // "portsAttributes": { + // "27378": { + // "label": "CodeChat", + // "onAutoForward": "openPreview", + // "requireLocalPort": true, + // "elevateIfNeeded": true, + // "protocol": "https" + // } + // }, + // "onCreateCommand": "pip install pretext", + // // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "sudo bash ./.devcontainer/postCreateCommand.sh", + + // Configure tool-specific properties. + "customizations": { + "codespaces": { + "openFiles": [ + "source/main.ptx" + ] + }, + "vscode": { + "settings": { + "editor.quickSuggestions": { + "other": "off" + }, + "editor.snippetSuggestions": "top", + "xml.validation.enabled": false + }, + "extensions": ["ms-vscode.live-server", "oscarlevin.pretext-tools"] + } + } + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} diff --git a/.github/workflows/pretext-cli.yml b/.github/workflows/pretext-cli.yml new file mode 100644 index 000000000..22da9ab94 --- /dev/null +++ b/.github/workflows/pretext-cli.yml @@ -0,0 +1,94 @@ +# (delete the above line to manage this file manually) + +# commented out because we're not using this but ptx generates it automatically. + +# name: PreTeXt-CLI Actions +# on: +# # Runs on pull requests +# pull_request: +# branches: ["*"] +# # Runs on pushes to main +# push: +# branches: ["main"] +# # Runs on demand +# workflow_dispatch: + +# jobs: +# build: +# runs-on: ubuntu-latest +# container: oscarlevin/pretext:full + +# steps: +# - name: Checkout source +# uses: actions/checkout@v4 + +# - name: install deps +# run: pip install -r requirements.txt + +# - name: build deploy targets +# run: pretext build --deploys +# - name: stage deployment +# run: pretext deploy --stage-only + +# - name: Bundle output/stage as artifact +# uses: actions/upload-artifact@v4 +# with: +# name: deploy +# path: output/stage + +# deploy-cloudflare: +# runs-on: ubuntu-latest +# needs: build +# if: vars.CLOUDFLARE_PROJECT_NAME != '' +# permissions: +# contents: read +# deployments: write + +# steps: +# - name: Download artifact +# uses: actions/download-artifact@v4 +# with: +# name: deploy +# path: deploy +# - name: Create 404.html +# run: echo "404 page not found" >> deploy/404.html +# - name: Publish to Cloudflare +# uses: cloudflare/pages-action@v1 +# with: +# apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} +# accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} +# projectName: ${{ vars.CLOUDFLARE_PROJECT_NAME }} +# gitHubToken: ${{ secrets.GITHUB_TOKEN }} +# branch: ${{ github.head_ref || github.ref_name }} +# directory: deploy + +# deploy-ghpages: +# runs-on: ubuntu-latest +# needs: build +# if: vars.PTX_ENABLE_DEPLOY_GHPAGES == 'yes' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) +# permissions: +# contents: read +# pages: write +# id-token: write +# concurrency: +# group: "page" +# cancel-in-progress: false +# environment: +# name: github-pages +# url: ${{ steps.deployment.outputs.page_url }} +# steps: +# - name: Download website artifact +# uses: actions/download-artifact@v4 +# with: +# name: deploy +# path: deploy +# - name: Setup GitHub Pages +# id: check +# uses: actions/configure-pages@v4 +# - name: Upload artifact +# uses: actions/upload-pages-artifact@v3 +# with: +# path: deploy +# - name: Deploy to Github Pages +# id: deployment +# uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 7748bbf5d..c9179e8cd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +# (delete the above line to manage this file manually) __pycache__/ build_info build @@ -19,3 +20,107 @@ beta output published pdf +venv/ +cli.log + +# Boilerplate list of files in a PreTeXt project for git to ignore +# ensure this file is tracked +!.gitignore + +# don't track unpublished builds or stage (note: Runestone uses `published`) +output +published + +# don't track assets generated from source +generated-assets + +# don't track the executables.ptx file +executables.ptx + +# don't track node packages +node_modules + +# don't track error logs +.error_schema.log +logs + +# don't track OS related files (windows/macos/linux) +.DS_Store +.DS_Store? +._* +.AppleDouble +.LSOverride +.Spotlight-V100 +.Trashes +Icon +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db +*.stackdump +*.lnk +*.cab +*.msi +*.msix +*.msm +*.msp +[Dd]esktop.ini +.directory +.fuse_hidden* +.Trash-* +.nfs* + +# Don't include VSCode generated files +.vscode +*.code-workspace + +# Don't inlucde SublimeText files +# Cache files for Sublime Text +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache + +# Workspace files are user-specific +*.sublime-workspace + +# Project files should be checked into the repository, unless a significant +# proportion of contributors will probably not be using Sublime Text +*.sublime-project + +# SFTP configuration file +sftp-config.json +sftp-config-alt*.json + +# Package control specific files +Package Control.last-run +Package Control.ca-list +Package Control.ca-bundle +Package Control.system-ca-bundle +Package Control.cache/ +Package Control.ca-certs/ +Package Control.merged-ca-bundle +Package Control.user-ca-bundle +oscrypto-ca-bundle.crt +bh_unicode_properties.cache + +# Sublime-github package stores a github token in this file +# https://packagecontrol.io/packages/sublime-github +GitHub.sublime-settings + + +# Don't include Dropbox settings and caches +.dropbox +.dropbox.attr +.dropbox.cache + +# Don't track codechat config (will be generated automatically) +codechat_config.yaml + +# Don't track deprecated workflows +.github/workflows/deploy.yml +.github/workflows/test-build.yml diff --git a/README.md b/README.md new file mode 100644 index 000000000..276991c5a --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +# How to Think Like a Computer Scientist: Mines Edition +This is a fork of the original [thinkcspy](https://github.com/runestoneinteractive/thinkcspy) made for Colorado School of Mines CSCI128: + +> This project began with the original How to Think Like a Computer Scientist text by Jeffrey Elkner, Peter Wentworth, Allen B. Downey, Chris Meyers, and Dario Mitchell. Since 2011 Brad Miller, David Ranum, Barbara Ericson, Mark Guzdial, and many others have built on the text making it interactive. + +> Programming is not a "spectator sport". It is something you do, something you participate in. It would make sense, then, that the book you use to learn programming should allow you to be active. That is our goal. + +> This book is meant to provide you with an interactive experience as you learn to program in Python. You can read the text, watch videos, and write and execute Python code. In addition to simply executing code, there is a unique feature called 'codelens' that allows you to control the flow of execution in order to gain a better understanding of how the program works. + +*Note: RST is deprecated, and the new pretext sources are in the pretext folder, but we will keep the _sources (old RST folder) directory until we are 100% sure that the book has been converted correctly and as thoroughly as possible.* + +## Development Environment +Create and activate a virtual environment and install dependencies: + +`python -m venv venv` + +`source venv/bin/activate` + +`pip install -r requirements.txt` + +Then build the textbook: `pretext build web` + +..and finally, run the web server: `pretext view` diff --git a/README.rst b/README.rst deleted file mode 100644 index b1dce3d73..000000000 --- a/README.rst +++ /dev/null @@ -1,59 +0,0 @@ -How to Think Like a Computer Scientist: Interactive Edition -=========================================================== - -This project began with the original How to Think Like a Computer Scientist text by Jeffrey Elkner, Peter Wentworth, Allen B. Downey, Chris Meyers, and Dario Mitchell. Since 2011 Brad Miller, David Ranum, Barbara Ericson, Mark Guzdial, and many others have built on the text making it interactive. - -Programming is not a "spectator sport". It is something you do, -something you participate in. It would make sense, then, -that the book you use to learn programming should allow you to be active. -That is our goal. - -This book is meant to provide you with an interactive experience as you learn -to program in Python. You can read the text, watch videos, -and write and execute Python code. In addition to simply executing code, -there is a unique feature called 'codelens' that allows you to control the -flow of execution in order to gain a better understanding of how the program -works. - -.. image:: https://travis-ci.org/RunestoneInteractive/thinkcspy.svg?branch=master - :target: https://travis-ci.org/RunestoneInteractive/thinkcspy - -Getting Started -=============== - -We have tried to make it as easy as possible for you to build and use this book. - -You can see and read this book online at `runestone.academy `_ - -.. important:: The restructuredText version of this book is deprecated! - -Building with PreTeXt ---------------------- - -1. Create a virtual environment -2. pip install pretextbook -3. To build run: pretext build web -4. pretext view html - -Note: The pretext sources are in the pretext folder, we will keep the _sources folder until we are 100% sure that the book has been converted correctly and as thoroughly as possible. - -Building for Production on a Runestone Server ---------------------------------------------- - -1. clone this repo to `httlacs` instead of `thinkcspy` -2. Run `rsmanage addcourse` and add `httlacs` as course name and base course name -3. Run `rsmanage build --ptx httlacs` - - -Building with runestone ------------------------ -As mentioned above this method is deprecated, but will still work. -Any updates to this book should be made in PreTeXt NOT RST. - -You can build it and host it yourself in just a few simple steps: - - 1. ``pip install -r requirements.txt`` -- Should install everything you need - 2. ``runestone build`` -- will build the html and put it in ``./build/thinkcspy`` - 3. ``runestone serve`` -- will start a webserver and serve the pages locally from ``./build/thinkcspy`` - - diff --git a/_sources/ClassesBasics/Achangeofperspective.rst b/_sources/ClassesBasics/Achangeofperspective.rst index f8e3aa12d..1e1b89d14 100644 --- a/_sources/ClassesBasics/Achangeofperspective.rst +++ b/_sources/ClassesBasics/Achangeofperspective.rst @@ -10,21 +10,12 @@ A change of perspective ----------------------- -Throughout the earlier chapters, we wrote functions and called them using a syntax such as ``drawCircle(tess)``. This suggests that the -function is the active agent. It says something like, *"Hey, drawCircle! -Here's a turtle object for you to use to draw with."* +Throughout the earlier chapters, we wrote functions and called them using a syntax such as ``printRange(lst)``. This suggests that the +function is the active agent. It says something like, *"Hey, printRange! +Here's a list for you to use to print with."* -In object-oriented programming, the objects are considered the active agents. -For example, in our early introduction to turtles, we used -an object-oriented style. We said ``tess.forward(100)``, which -asks the turtle to move itself forward by the given number of steps. -An -invocation like ``tess.circle()`` says *"Hey tess! -Please use your circle method!"* - - - -This change in perspective is sometimes considered to be a more "polite" way to write programming instructions. However, it may not initially +In object-oriented programming, the objects are considered the active agents. This change in perspective is sometimes considered to be a more "polite" way +to write programming instructions. However, it may not initially be obvious that it is useful. It turns out that often times shifting responsibility from the functions onto the objects makes it possible to write more versatile functions and makes it easier to maintain and reuse code. diff --git a/_sources/ClassesBasics/AddingOtherMethodstoourClass.rst b/_sources/ClassesBasics/AddingOtherMethodstoourClass.rst index 312dc3e7d..08973b292 100644 --- a/_sources/ClassesBasics/AddingOtherMethodstoourClass.rst +++ b/_sources/ClassesBasics/AddingOtherMethodstoourClass.rst @@ -20,8 +20,8 @@ We can group together the sensible operations, and the kinds of data they apply to, and each instance of the class can have its own state. A **method** behaves like a function but it is invoked on a specific -instance. For example, with a turtle named ``tess``, ``tess.right(90)`` asks the ``tess`` object to perform its -``right`` method and turn 90 degrees. Methods are accessed using dot notation. +instance. For example, with a list named ``lst``, ``lst.append(90)`` asks the ``lst`` object to perform its +``append`` method and add the integer 90 to the end of the list. Methods are accessed using dot notation. Let's add two simple methods to allow a point to give us information about its state. The ``getX`` method, when invoked, will return the value of the x coordinate. The implementation of this method is straight forward since we already know how to write functions that return values. One thing to notice is that even though the ``getX`` method does not need any other parameter information to do its work, there is still one formal parameter, ``self``. As we stated earlier, all methods defined in a class that operate on objects of that class will have ``self`` as their first parameter. Again, this serves as reference to the object itself which in turn gives access to the state data inside the object. diff --git a/_sources/ClassesBasics/ObjectsRevisited.rst b/_sources/ClassesBasics/ObjectsRevisited.rst index e1236bc4f..e0a5f95fb 100644 --- a/_sources/ClassesBasics/ObjectsRevisited.rst +++ b/_sources/ClassesBasics/ObjectsRevisited.rst @@ -12,13 +12,11 @@ Objects Revisited ----------------- -In Python, every value is actually an object. Whether it be a turtle, a list, or even an integer, they are all objects. Programs manipulate those objects either by performing +In Python, every value is actually an object. Whether it be a string, a list, or even an integer, they are all objects. Programs manipulate those objects either by performing computation with them or by asking them to perform methods. To be more specific, we say that an object has a **state** and a collection of **methods** that it can perform. The state of an object represents those things -that the object knows about itself. For example, as we have seen with turtle objects, each turtle has a state consisting -of the turtle's position, its color, its heading and so on. Each turtle also has the ability to go forward, backward, or turn right or left. Individual turtles are different in that even though they are -all turtles, they differ in the specific values of the individual state attributes (maybe they are in a different location or have a different heading). +that the object knows about itself. For example, each list has a state consisting +of the items it contains, a length and so on. Each list also has the ability to add, delete, and sort the items inside of it. Individual lists are different in +that even though they are all lists, they differ in the specific values of the individual state attributes (maybe they have different items inside them). -.. image:: Figures/objectpic1.png - :alt: Simple object has state and methods diff --git a/_sources/ClassesBasics/ObjectsasArgumentsandParameters.rst b/_sources/ClassesBasics/ObjectsasArgumentsandParameters.rst index 6cd2819a4..925c3a070 100644 --- a/_sources/ClassesBasics/ObjectsasArgumentsandParameters.rst +++ b/_sources/ClassesBasics/ObjectsasArgumentsandParameters.rst @@ -10,13 +10,8 @@ Objects as Arguments and Parameters ------------------------------------- -You can pass an object as an argument in the usual way. We've already seen -this in some of the turtle examples where we passed the turtle to -some function like ``drawRectangle`` so that the function could -control and use whatever turtle instance we passed to it. - -Here is a simple function called ``distance`` involving our new ``Point`` objects. The job of this function is to figure out the -distance between two points. +You can pass an object as an argument in the usual way. Here is a simple function called ``distance`` involving our new ``Point`` objects. +The job of this function is to figure out the distance between two points. .. activecode:: chp13_classes6 diff --git a/_sources/ClassesBasics/UserDefinedClasses.rst b/_sources/ClassesBasics/UserDefinedClasses.rst index 6ea76a86b..0dfb99f2c 100644 --- a/_sources/ClassesBasics/UserDefinedClasses.rst +++ b/_sources/ClassesBasics/UserDefinedClasses.rst @@ -14,7 +14,7 @@ User Defined Classes -------------------- -We've already seen classes like ``str``, ``int``, ``float`` and ``Turtle``. These were defined by Python and +We've already seen classes like ``str``, ``int``, and ``float``. These were defined by Python and made available for us to use. However, in many cases when we are solving problems we need to create data objects that are related to the problem we are trying to solve. We need to create our own classes. diff --git a/_sources/Files/FindingaFileonyourDisk.rst b/_sources/Files/FindingaFileonyourDisk.rst index 56d915f14..b043969d0 100755 --- a/_sources/Files/FindingaFileonyourDisk.rst +++ b/_sources/Files/FindingaFileonyourDisk.rst @@ -45,9 +45,9 @@ but the same principles are in use. For example on windows the path might be You can access files in sub-folders, also called directories, under your home directory by adding a slash and the name of the folder. For example, if you had a file -called ``hello.py`` in a folder called ``CS150`` that is inside a folder called -``PyCharmProjects`` under your home directory, then the full name for the file -``hello.py`` is ``/Users/yourname/PyCharmProjects/CS150/hello.py``. +called ``hello.py`` in a folder called ``CS128`` that is inside a folder called +``VSCodeProjects`` under your home directory, then the full name for the file +``hello.py`` is ``/Users/yourname/VSCodeProjects/CS128/hello.py``. This is called an *absolute file path*. An *absolute file path* typically only works on a specific computer. Think about it for a second. What other computer in the world is going to have an *absolute file path* that starts with diff --git a/_sources/Files/WithStatements.rst b/_sources/Files/WithStatements.rst index 4b18ca404..718bc0768 100644 --- a/_sources/Files/WithStatements.rst +++ b/_sources/Files/WithStatements.rst @@ -1,9 +1,6 @@ With Statements --------------- -.. note:: - This section is a bit of an advanced topic and can be easily skipped. But with statements are becoming very common and it doesn't hurt to know about them in case you run into one in the wild. - Now that you have seen and practiced a bit with opening and closing files, there is another mechanism that Python provides for us that cleans up the often forgotten close. Forgetting to close a file does not necessarily cause a runtime error in the kinds of programs you typically write in an introductory CS course. However if you are writing a program that may run for days or weeks at a time that does a lot of file reading and writing you may run into trouble. In version 2.5 Python introduced the concept of a context manager. The context manager automates the process of doing common operations at the start of some task, as well as automating certain operations at the end of some task. In the context of reading and writing a file, the normal operation is to open the file and assign it to a variable. At the end of working with a file the common operation is to make sure that file is closed. diff --git a/_sources/Files/intro-WorkingwithDataFiles.rst b/_sources/Files/intro-WorkingwithDataFiles.rst index fd5abfb27..8a8b7a8e3 100644 --- a/_sources/Files/intro-WorkingwithDataFiles.rst +++ b/_sources/Files/intro-WorkingwithDataFiles.rst @@ -14,7 +14,9 @@ Working with Data Files ======================= -So far, the data we have used in this book have all been either coded right into the program, or have been entered by the user. In real life data reside in files. For example the images we worked with in the image processing unit ultimately live in files on your hard drive. Web pages, and word processing documents, and music are other examples of data that live in files. In this short chapter we will introduce the Python concepts necessary to use data from files in our programs. +So far, the data we have used in this book have all been either coded right into the program, or have been entered by the user. In real life data reside in files. +For example, web pages, word processing documents, and all data that live in files. In this short chapter we will introduce the Python concepts necessary to use +data from files in our programs. For our purposes, we will assume that our data files are text files--that is, files filled with characters. The Python programs that you write are stored as text files. We can create these files in any of a number of ways. For example, we could use a text editor to type in and save the data. We could also download the data from a website and then save it in a file. Regardless of how the file is created, Python will allow us to manipulate the contents. diff --git a/_sources/Functions/Functionscancallotherfunctions.rst b/_sources/Functions/Functionscancallotherfunctions.rst index 726f70981..28062ce48 100644 --- a/_sources/Functions/Functionscancallotherfunctions.rst +++ b/_sources/Functions/Functionscancallotherfunctions.rst @@ -62,94 +62,6 @@ for ``sum_of_squares``. As you step through you will notice that ``x``, and ``y different values. This illustrates that even though they are named the same, they are in fact, very different. -Now we will look at another example that uses two functions. This example illustrates an -important computer science problem solving technique called -**generalization**. Assume we want to write a -function to draw a square. The generalization step is to realize that a -square is just a special kind of rectangle. - -To draw a rectangle we need to be able to call a function with different -arguments for width and height. Unlike the case of the square, -we cannot repeat the same thing 4 times, because the four sides are not equal. -However, it is the case that drawing the bottom and right sides are the -same sequence as drawing the top and left sides. So we eventually come up with -this rather nice code that can draw a rectangle. - -.. code-block:: python - - def drawRectangle(t, w, h): - """Get turtle t to draw a rectangle of width w and height h.""" - for i in range(2): - t.forward(w) - t.left(90) - t.forward(h) - t.left(90) - -The parameter names are chosen as single letters for conciseness. -In real programs, we will insist on better variable names than this. -The point is that the program doesn't "understand" that you're drawing a rectangle or that the -parameters represent the width and the height. Concepts like rectangle, width, and height are meaningful -for humans. They are not concepts that the program or the computer understands. - -*Thinking like a computer scientist* involves looking for patterns and -relationships. In the code above, we've done that to some extent. We did -not just draw four sides. Instead, we spotted that we could draw the -rectangle as two halves and used a loop to repeat that pattern twice. - -But now we might spot that a square is a special kind of rectangle. A square -simply uses the same value for both the height and the width. -We already have a function that draws a rectangle, so we can use that to draw -our square. - -.. code-block:: python - - def drawSquare(tx, sz): # a new version of drawSquare - drawRectangle(tx, sz, sz) - -Here is the entire example with the necessary set up code. - -.. activecode:: ch04_3 - :nocodelens: - - import turtle - - def drawRectangle(t, w, h): - """Get turtle t to draw a rectangle of width w and height h.""" - for i in range(2): - t.forward(w) - t.left(90) - t.forward(h) - t.left(90) - - def drawSquare(tx, sz): # a new version of drawSquare - drawRectangle(tx, sz, sz) - - wn = turtle.Screen() # Set up the window - wn.bgcolor("lightgreen") - - tess = turtle.Turtle() # create tess - - drawSquare(tess, 50) - - wn.exitonclick() - - - -There are some points worth noting here: - -* Functions can call other functions. -* Rewriting ``drawSquare`` like this captures the relationship - that we've spotted. -* A caller of this function might say ``drawSquare(tess, 50)``. The parameters - of this function, ``tx`` and ``sz``, are assigned the values of the tess object, and - the integer 50 respectively. -* In the body of the function, ``tz`` and ``sz`` are just like any other variable. -* When the call is made to ``drawRectangle``, the values in variables ``tx`` and ``sz`` - are fetched first, then the call happens. So as we enter the top of - function ``drawRectangle``, its variable ``t`` is assigned the tess object, and ``w`` and - ``h`` in that function are both given the value 50. - - So far, it may not be clear why it is worth the trouble to create all of these new functions. Actually, there are a lot of reasons, but this example demonstrates two: @@ -163,10 +75,3 @@ demonstrates two: #. Sometimes you can write functions that allow you to solve a specific problem using a more general solution. - -.. admonition:: Lab - - * `Drawing a Circle <../Labs/lab04_01.html>`_ In this guided lab exercise we will work - through a simple problem solving exercise related to drawing a circle with the turtle. - - diff --git a/_sources/Functions/Functionsthatreturnvalues.rst b/_sources/Functions/Functionsthatreturnvalues.rst index 51a0fa55c..cae049591 100644 --- a/_sources/Functions/Functionsthatreturnvalues.rst +++ b/_sources/Functions/Functionsthatreturnvalues.rst @@ -32,9 +32,7 @@ the absolute value: In this example, the arguments to the ``abs`` function are 5 and -5. -Some functions take more than one argument. For example the math module contains a function -called -``pow`` which takes two arguments, the base and the exponent. +Some functions take more than one argument. For example the range function that we saw with ``for`` loops. .. Inside the function, .. the values that are passed get assigned to variables called **parameters**. @@ -42,14 +40,9 @@ called .. activecode:: ch04_5 :nocanvas: - import math - print(math.pow(2, 3)) + print(list(range(4, 8))) - print(math.pow(7, 4)) - -.. note:: - - Of course, we have already seen that raising a base to an exponent can be done with the ** operator. + print(list(range(1, 10))) Another built-in function that takes more than one argument is ``max``. @@ -65,17 +58,17 @@ return the maximum value sent. The arguments can be either simple values or expressions. In the last example, 503 is returned, since it is larger than 33, 125, and 1. Note that ``max`` also works on lists of values. -Furthermore, functions like ``range``, ``int``, ``abs`` all return values that +Furthermore, functions like ``int``, ``abs`` all return values that can be used to build more complex expressions. .. index:: fruitful function variable; global global variable -So an important difference between these functions and one like ``drawSquare`` is that -``drawSquare`` was not executed because we wanted it to compute a value --- on the contrary, -we wrote ``drawSquare`` because we wanted it to execute a sequence of steps that caused -the turtle to draw a specific shape. +So an important difference between these functions and one like ``printRange`` is that +``printRange`` was not executed because we wanted it to compute a value --- on the contrary, +we wrote ``printRange`` because we wanted it to execute a sequence of steps that caused +the list's range to print. Functions that return values are sometimes called **fruitful functions**. In many other languages, a chunk that doesn't return a value is called a **procedure**, diff --git a/_sources/Functions/functions.rst b/_sources/Functions/functions.rst index 1d1380e0b..879251e0c 100644 --- a/_sources/Functions/functions.rst +++ b/_sources/Functions/functions.rst @@ -55,7 +55,7 @@ pattern: indented the same amount -- *4 spaces is the Python standard* -- from the header line. -We've already seen the ``for`` loop which follows this pattern. +We've already seen the ``while`` and ``for`` loops which follow this pattern. In a function definition, the keyword in the header is ``def``, which is followed by the name of the function and some *parameters* enclosed in @@ -72,37 +72,27 @@ The figure below shows this relationship. A function needs certain information This type of diagram is often called a **black-box diagram** because it only states the requirements from the perspective of the user. The user must know the name of the function and what arguments need to be passed. The details of how the function works are hidden inside the "black-box". -Suppose we're working with turtles and a common operation we need is to draw -squares. It would make sense if we did not have to duplicate all the steps each time we want to make a square. "Draw a square" can be thought of as an *abstraction* of a number of smaller steps. We will need to provide two pieces of information for the function to do its work: a turtle to do the drawing and a size for the side of the square. We could represent this using the following black-box diagram. - -.. image:: Figures/turtleproc.png +Suppose we're working with lists of numeric data and a common operation we need is to find the size of the range of numbers in a list. +It would make sense if we did not have to duplicate all the steps each time we want to find the size of the range. "printRange" can be thought of as an +*abstraction* of a number of smaller steps. We will need to provide one piece of information for the function to do its work: the list we need the range of. Here is a program containing a function to capture this idea. Give it a try. .. activecode:: ch04_1 :nocodelens: - import turtle - - def drawSquare(t, sz): - """Make turtle t draw a square of with side sz.""" - - for i in range(4): - t.forward(sz) - t.left(90) - - - wn = turtle.Screen() # Set up the window and its attributes - wn.bgcolor("lightgreen") + def printRange(lst): + """Prints the size of the range of lst.""" + smallest = min(lst) + largest = max(lst) + print(largest - smallest) - alex = turtle.Turtle() # create alex - drawSquare(alex, 50) # Call the function to draw the square passing the actual turtle and the actual side size - wn.exitonclick() + my_list1 = [1, 2, 3, 4, 5, 6] + printRange(my_list1) -This function is named ``drawSquare``. It has two parameters --- one to tell -the function which turtle to move around and the other to tell it the size -of the square we want drawn. In the function definition they are called ``t`` and ``sz`` respectively. Make sure you know where the body of the function +This function is named ``printRange``. It has one parameter --- a variable storing a list of numbers. In the function definition this parameter is called +``lst``. Make sure you know where the body of the function ends --- it depends on the indentation and the blank lines don't count for this purpose! @@ -129,9 +119,8 @@ Defining a new function does not make the function run. To do that we need a ``print``, ``range`` and ``int``. Function calls contain the name of the function to be executed followed by a list of values in parentheses, called *arguments*, which are assigned to the parameters in the function definition. -So in the second to the last line of -the program, we call the function, and pass ``alex`` as the turtle to be manipulated, -and 50 as the size of the square we want. +So in the last line of +the program, we call the function, and pass ``my_list1`` as the list to be analyzed. .. The parameters being sent to the function, sometimes referred to as the **actual parameters** or **arguments**, .. represent the specific data items that the function will use when it is executing. @@ -142,67 +131,24 @@ and 50 as the size of the square we want. Once we've defined a function, we can call it as often as we like and its statements will be executed each time we call it. In this case, we could use it to get -one of our turtles to draw a square and then we can move the turtle and have it draw a different square in a -different location. Note that we lift the tail so that when ``alex`` moves there is no trace. We put the tail -back down before drawing the next square. Make sure you can identify both invocations of the ``drawSquare`` function. - -.. activecode:: ch04_1a - :nocodelens: - - import turtle - - def drawSquare(t, sz): - """Make turtle t draw a square of with side sz.""" - - for i in range(4): - t.forward(sz) - t.left(90) +the size of the range of multiple lists. Make sure you can identify all three invocations of the ``printRange`` function. - - wn = turtle.Screen() # Set up the window and its attributes - wn.bgcolor("lightgreen") - - alex = turtle.Turtle() # create alex - drawSquare(alex, 50) # Call the function to draw the square - - alex.penup() - alex.goto(100,100) - alex.pendown() - - drawSquare(alex,75) # Draw another square - - wn.exitonclick() - -In the next example, we've changed the ``drawSquare`` -function a little and we get ``tess`` to draw 15 squares with some variations. Once the function has -been defined, we can call it as many times as we like with whatever actual parameters we like. - -.. activecode:: ch04_2 +.. activecode:: ch04_1 :nocodelens: - import turtle - - def drawMulticolorSquare(t, sz): - """Make turtle t draw a multi-colour square of sz.""" - for i in ['red','purple','hotpink','blue']: - t.color(i) - t.forward(sz) - t.left(90) - - wn = turtle.Screen() # Set up the window and its attributes - wn.bgcolor("lightgreen") + def printRange(lst): + """Prints the size of the range of lst.""" + smallest = min(lst) + largest = max(lst) + print(largest - smallest) - tess = turtle.Turtle() # create tess and set some attributes - tess.pensize(3) - size = 20 # size of the smallest square - for i in range(15): - drawMulticolorSquare(tess, size) - size = size + 10 # increase the size for next time - tess.forward(10) # move tess along a little - tess.right(18) # and give her some extra turn + my_list1 = [1, 2, 3, 4, 5, 6] + my_list2 = [2, 6, 9, 16, 42, 100, 2, 5] + printRange(my_list1) + printRange(my_list2) + printRange([5, 10, 1000, 2]) - wn.exitonclick() .. warning:: @@ -210,8 +156,8 @@ been defined, we can call it as many times as we like with whatever actual param the parentheses ``( )`` after the function name are *required*. This can lead to a difficult bug: A function name without the parenthesis is a legal expression *referring* to the function; for example, - ``print`` and ``alex.penup``, but they do - not *call* the associated functions. + ``print``, but it does + not *call* the associated function. Try it below if you want to see. .. note:: @@ -267,10 +213,10 @@ been defined, we can call it as many times as we like with whatever actual param .. mchoice:: test_question5_1_4 :practice: T - :answer_a: def drawSquare(t, sz) - :answer_b: drawSquare - :answer_c: drawSquare(t, sz) - :answer_d: Make turtle t draw a square with side sz. + :answer_a: def printSquare(size) + :answer_b: printSquare + :answer_c: printSquare(size) + :answer_d: Print a square of asterices with side size. :correct: b :feedback_a: This line is the complete function header (except for the semi-colon) which includes the name as well as several other components. :feedback_b: Yes, the name of the function is given after the keyword def and before the list of parameters. @@ -281,61 +227,58 @@ been defined, we can call it as many times as we like with whatever actual param .. code-block:: python - def drawSquare(t, sz): - """Make turtle t draw a square of with side sz.""" - for i in range(4): - t.forward(sz) - t.left(90) + def printSquare(size): + """Print a square of asterices with side size.""" + for i in range(size): + print("*"*size) .. mchoice:: test_question5_1_5 :practice: T :answer_a: i - :answer_b: t - :answer_c: t, sz - :answer_d: t, sz, i + :answer_b: size, i + :answer_c: size + :answer_d: "*"*size :correct: c :feedback_a: i is a variable used inside of the function, but not a parameter, which is passed in to the function. - :feedback_b: t is only one of the parameters to this function. - :feedback_c: Yes, the function specifies two parameters: t and sz. - :feedback_d: the parameters include only those variables whose values that the function expects to receive as input. They are specified in the header of the function. + :feedback_b: i is a variable used inside of the function, but not a parameter, which is passed in to the function. + :feedback_c: Yes, the function specifies one parameter: size. + :feedback_d: This is an argument provided to the call to print(). What are the parameters of the following function? .. code-block:: python - def drawSquare(t, sz): - """Make turtle t draw a square of with side sz.""" - for i in range(4): - t.forward(sz) - t.left(90) + def printSquare(size): + """Print a square of asterices with side size.""" + for i in range(size): + print("*"*size) .. mchoice:: test_question5_1_6 :practice: T - :answer_a: def drawSquare(t, sz) - :answer_b: drawSquare - :answer_c: drawSquare(10) - :answer_d: drawSquare(alex, 10): - :answer_e: drawSquare(alex, 10) + :answer_a: def printSquare(size) + :answer_b: printSquare + :answer_c: printSquare(10) + :answer_d: printSquare(my_size): + :answer_e: printSquare(size): :correct: e - :feedback_a: No, t and sz are the names of the formal parameters to this function. When the function is called, it requires actual values to be passed in. + :feedback_a: No, size is the name of the formal parameter to this function. When the function is called, it requires an actual value to be passed in. :feedback_b: A function call always requires parentheses after the name of the function. - :feedback_c: This function takes two parameters (arguments) - :feedback_d: A colon is only required in a function definition. It will cause an error with a function call. - :feedback_e: Since alex was already previously defined and 10 is a value, we have passed in two correct values for this function. + :feedback_c: Yes, this would work + :feedback_d: Yes, this would work since my_size is already defined. + :feedback_e: A colon is only required in a function definition. It will cause an error with a function call. - Considering the function below, which of the following statements correctly invokes, or calls, this function (i.e., causes it to run)? Assume we already have a turtle named alex. + Considering the function below, which of the following statements correctly invokes, or calls, this function (i.e., causes it to run)? Assume we already have a variable named my_size. .. code-block:: python - def drawSquare(t, sz): - """Make turtle t draw a square of with side sz.""" - for i in range(4): - t.forward(sz) - t.left(90) + def printSquare(size): + """Print a square of asterices with side size.""" + for i in range(size): + print("*"*size) diff --git a/_sources/Functions/mainfunction.rst b/_sources/Functions/mainfunction.rst index addbdf8f0..9128bfabd 100644 --- a/_sources/Functions/mainfunction.rst +++ b/_sources/Functions/mainfunction.rst @@ -19,65 +19,54 @@ Using a Main Function --------------------- Using functions is a good idea. It helps us to modularize our code by breaking a program -into logical parts where each part is responsible for a specific task. For example, in one of our first programs there -was a function called ``drawSquare`` that was responsible for having some turtle draw a square of some size. -The actual turtle and the actual size of the square were defined to be provided as parameters. Here is that original program. +into logical parts where each part is responsible for a specific task. For example, in one of our recent programs there +was a function called ``square`` that was responsible for calculating the square of a number. +After the function definition we defined a variable, called the function, and printed its results. Here is that original program. .. code-block:: python - import turtle + def square(x): + y = x * x + return y - def drawSquare(t, sz): - """Make turtle t draw a square of with side sz.""" + toSquare = 10 + squareResult = square(toSquare) + print("The result of", toSquare, "squared is", squareResult) - for i in range(4): - t.forward(sz) - t.left(90) +If you look closely at the structure of this program, we first define the function ``square``. At this point, we could have defined as many functions as were needed. Finally, there are five statements that set up the window, create the turtle, perform the function invocation, and wait for a user click to terminate the program. - wn = turtle.Screen() # Set up the window and its attributes - wn.bgcolor("lightgreen") +The final three statements perform the main processing that the program will do. Notice that much of the detail has been pushed inside the ``square`` function. +However, there are still these three lines of code that are needed to get things done. - alex = turtle.Turtle() # create alex - drawSquare(alex, 50) # Call the function to draw the square - - wn.exitonclick() - - -If you look closely at the structure of this program, you will notice that we first perform all of our necessary ``import`` statements, in this case to be able to use the ``turtle`` module. Next, we define the function ``drawSquare``. At this point, we could have defined as many functions as were needed. Finally, there are five statements that set up the window, create the turtle, perform the function invocation, and wait for a user click to terminate the program. - -These final five statements perform the main processing that the program will do. Notice that much of the detail has been pushed inside the ``drawSquare`` function. However, there are still these five lines of code that are needed to get things done. - -In many programming languages (e.g. Java and C++), it is not possible to simply have statements sitting alone like this at the bottom of the program. They are required to be part of a special function that is automatically invoked by the operating system when the program is executed. This special function is called **main**. Although this is not required by the Python programming language, it is actually a good idea that we can incorporate into the logical structure of our program. In other words, these five lines are logically related to one another in that they provide the main tasks that the program will perform. Since functions are designed to allow us to break up a program into logical pieces, it makes sense to call this piece ``main``. +In many programming languages (e.g. Java and C++), it is not possible to simply have statements sitting alone like this at the bottom of the program. +They are required to be part of a special function that is automatically invoked by the operating system when the program is executed. +This special function is called **main**. Although this is not required by the Python programming language, it is actually a good idea that we +can incorporate into the logical structure of our program. In other words, these three lines are logically related to one another in that they provide the +main tasks that the program will perform. Since functions are designed to allow us to break up a program into logical pieces, it makes sense to call this +piece ``main``. The following activecode shows this idea. In line 11 we have defined a new function called ``main`` that doesn't need any parameters. The five lines of main processing are now placed inside this function. Finally, in order to execute that main processing code, we need to invoke the ``main`` function (line 20). When you push run, you will see that the program works the same as it did before. .. activecode:: ch04_1main :nocodelens: - import turtle - - def drawSquare(t, sz): - """Make turtle t draw a square of with side sz.""" - - for i in range(4): - t.forward(sz) - t.left(90) + def square(x): + y = x * x + return y def main(): # Define the main function - wn = turtle.Screen() # Set up the window and its attributes - wn.bgcolor("lightgreen") - - alex = turtle.Turtle() # create alex - drawSquare(alex, 50) # Call the function to draw the square - - wn.exitonclick() + toSquare = 10 + squareResult = square(toSquare) + print("The result of", toSquare, "squared is", squareResult) main() # Invoke the main function -Now our program structure is as follows. First, import any modules that will be required. Second, define any functions that will be needed. Third, define a ``main`` function that will get the process started. And finally, invoke the main function (which will in turn call the other functions as needed). +Now our program structure is as follows. First, import any modules that will be required (you'll read about those in the next chapter). Second, define any functions +that will be needed. Third, define a ``main`` function that will get the process started. And finally, invoke the main function +(which will in turn call the other functions as needed). .. note:: @@ -110,6 +99,3 @@ The activecode below defines two simple functions and a main. Line 12 uses an ``if`` statement to ask about the value of the ``__name__`` variable. If the value is ``"__main__"``, then the ``main`` function will be called. Otherwise, it can be assumed that the program is being imported into another program and we do not want to call ``main`` because that program will invoke the functions as needed. This ability to conditionally execute our main function can be extremely useful when we are writing code that will potentially be used by others. It allows us to include functionality that the user of the code will not need, most often as part of a testing process to be sure that the functions are working correctly. -.. note:: - - In order to conditionally execute the ``main`` function, we used a structure called an ``if`` statement to create what is known as selection. This topic will be studied in much more detail later. diff --git a/_sources/Functions/toctree.rst b/_sources/Functions/toctree.rst index 938d5bffc..d85f7f987 100644 --- a/_sources/Functions/toctree.rst +++ b/_sources/Functions/toctree.rst @@ -7,7 +7,6 @@ Functions functions.rst Functionsthatreturnvalues.rst - UnitTesting.rst Variablesandparametersarelocal.rst TheAccumulatorPattern.rst Functionscancallotherfunctions.rst @@ -15,6 +14,6 @@ Functions mainfunction.rst ProgramDevelopment.rst Composition.rst - ATurtleBarChart.rst + UnitTesting.rst Glossary.rst Exercises.rst diff --git a/_sources/GeneralIntro/ThePythonProgrammingLanguage.rst b/_sources/GeneralIntro/ThePythonProgrammingLanguage.rst index c4e689c9d..512b934a4 100644 --- a/_sources/GeneralIntro/ThePythonProgrammingLanguage.rst +++ b/_sources/GeneralIntro/ThePythonProgrammingLanguage.rst @@ -58,11 +58,6 @@ without further translation. .. image:: Figures/compile.png :alt: Compile illustration -Many modern languages use both processes. They are first compiled into a lower -level language, called **byte code**, and then interpreted by a program called -a **virtual machine**. Python uses both processes, but because of the way -programmers interact with it, it is usually considered an interpreted language. - There are two ways to use the Python interpreter: *shell mode* and *program mode*. In shell mode, you type Python expressions into the **Python shell**, and the interpreter immediately shows the result. The example below shows the Python shell at work. @@ -112,16 +107,6 @@ These examples show Python being run from a Unix command line. In other development environments, the details of executing programs may differ. Also, most programs are more interesting than this one. -.. admonition:: Want to learn more about Python? - - If you would like to learn more about installing and using Python, here are some video links. - `Installing Python for Windows `__ shows you how to install the Python environment under - Windows Vista, - `Installing Python for Mac `__ shows you how to install under Mac OS/X, and - `Installing Python for Linux `__ shows you how to install from the Linux - command line. - `Using Python `__ shows you some details about the Python shell and source code. - **Check your understanding** .. mchoice:: question1_2_1 diff --git a/_sources/GeneralIntro/toctree.rst b/_sources/GeneralIntro/toctree.rst index 4a8ddbfbf..0f01d3ef9 100644 --- a/_sources/GeneralIntro/toctree.rst +++ b/_sources/GeneralIntro/toctree.rst @@ -15,9 +15,9 @@ General Introduction RuntimeErrors.rst SemanticErrors.rst ExperimentalDebugging.rst - FormalandNaturalLanguages.rst ATypicalFirstProgram.rst Comments.rst + FormalandNaturalLanguages.rst Glossary.rst Exercises.rst \ No newline at end of file diff --git a/_sources/IntroRecursion/toctree.rst b/_sources/IntroRecursion/toctree.rst index 0f33af2b5..8da4ecbba 100644 --- a/_sources/IntroRecursion/toctree.rst +++ b/_sources/IntroRecursion/toctree.rst @@ -9,8 +9,6 @@ Recursion CalculatingtheSumofaListofNumbers.rst TheThreeLawsofRecursion.rst ConvertinganIntegertoaStringinAnyBase.rst - intro-VisualizingRecursion.rst - SierpinskiTriangle.rst Glossary.rst ProgrammingExercises.rst Exercises.rst diff --git a/_sources/Lists/Aliasing.rst b/_sources/Lists/Aliasing.rst index 8525b989d..560b4d9c5 100644 --- a/_sources/Lists/Aliasing.rst +++ b/_sources/Lists/Aliasing.rst @@ -8,7 +8,7 @@ License". .. qnum:: - :prefix: list-11- + :prefix: list-14- :start: 1 .. index:: alias diff --git a/_sources/Lists/AppendversusConcatenate.rst b/_sources/Lists/AppendversusConcatenate.rst index 83d67bb3f..a169ff6c8 100644 --- a/_sources/Lists/AppendversusConcatenate.rst +++ b/_sources/Lists/AppendversusConcatenate.rst @@ -8,7 +8,7 @@ License". .. qnum:: - :prefix: list-16- + :prefix: list-11- :start: 1 Append versus Concatenate diff --git a/_sources/Lists/CloningLists.rst b/_sources/Lists/CloningLists.rst index 3376aa8b1..9c7677363 100644 --- a/_sources/Lists/CloningLists.rst +++ b/_sources/Lists/CloningLists.rst @@ -8,7 +8,7 @@ License". .. qnum:: - :prefix: list-12- + :prefix: list-15- :start: 1 .. index:: clone diff --git a/_sources/Lists/ConcatenationandRepetition.rst b/_sources/Lists/ConcatenationandRepetition.rst index 6114d799d..74aa97796 100644 --- a/_sources/Lists/ConcatenationandRepetition.rst +++ b/_sources/Lists/ConcatenationandRepetition.rst @@ -8,7 +8,7 @@ License". .. qnum:: - :prefix: list-6- + :prefix: list-5- :start: 1 .. index:: diff --git a/_sources/Lists/ListDeletion.rst b/_sources/Lists/ListDeletion.rst index e5dd5a6e5..0d6d3638a 100644 --- a/_sources/Lists/ListDeletion.rst +++ b/_sources/Lists/ListDeletion.rst @@ -8,7 +8,7 @@ License". .. qnum:: - :prefix: list-9- + :prefix: list-8- :start: 1 .. index:: del; a list portion diff --git a/_sources/Lists/ListMembership.rst b/_sources/Lists/ListMembership.rst index b77dfe2b7..a47245a1c 100644 --- a/_sources/Lists/ListMembership.rst +++ b/_sources/Lists/ListMembership.rst @@ -8,7 +8,7 @@ License". .. qnum:: - :prefix: list-5- + :prefix: list-12- :start: 1 .. index:: in; membership operator diff --git a/_sources/Lists/ListMethods.rst b/_sources/Lists/ListMethods.rst index 21e44e220..509025559 100644 --- a/_sources/Lists/ListMethods.rst +++ b/_sources/Lists/ListMethods.rst @@ -8,7 +8,7 @@ License". .. qnum:: - :prefix: list-14- + :prefix: list-9- :start: 1 List Methods diff --git a/_sources/Lists/ListSlices.rst b/_sources/Lists/ListSlices.rst index edfe3147b..ec90110e5 100644 --- a/_sources/Lists/ListSlices.rst +++ b/_sources/Lists/ListSlices.rst @@ -8,7 +8,7 @@ License". .. qnum:: - :prefix: list-7- + :prefix: list-6- :start: 1 .. index:: diff --git a/_sources/Lists/Listsandforloops.rst b/_sources/Lists/Listsandforloops.rst index 9d7a87e58..36bd171fb 100644 --- a/_sources/Lists/Listsandforloops.rst +++ b/_sources/Lists/Listsandforloops.rst @@ -8,7 +8,7 @@ License". .. qnum:: - :prefix: list-17- + :prefix: list-18- :start: 1 Lists and ``for`` loops diff --git a/_sources/Lists/ListsareMutable.rst b/_sources/Lists/ListsareMutable.rst index ef901545e..cfc3c6c30 100644 --- a/_sources/Lists/ListsareMutable.rst +++ b/_sources/Lists/ListsareMutable.rst @@ -8,7 +8,7 @@ License". .. qnum:: - :prefix: list-8- + :prefix: list-7- :start: 1 Lists are Mutable diff --git a/_sources/Lists/ObjectsandReferences.rst b/_sources/Lists/ObjectsandReferences.rst index c39a2db5a..2efdfbf84 100644 --- a/_sources/Lists/ObjectsandReferences.rst +++ b/_sources/Lists/ObjectsandReferences.rst @@ -8,7 +8,7 @@ License". .. qnum:: - :prefix: list-10- + :prefix: list-13- :start: 1 diff --git a/_sources/Lists/RepetitionandReferences.rst b/_sources/Lists/RepetitionandReferences.rst index b353e0e18..ee8b4f302 100644 --- a/_sources/Lists/RepetitionandReferences.rst +++ b/_sources/Lists/RepetitionandReferences.rst @@ -8,7 +8,7 @@ License". .. qnum:: - :prefix: list-13- + :prefix: list-16- :start: 1 Repetition and References diff --git a/_sources/Lists/StringsandLists.rst b/_sources/Lists/StringsandLists.rst index f2de1e085..b1c4fd3f2 100644 --- a/_sources/Lists/StringsandLists.rst +++ b/_sources/Lists/StringsandLists.rst @@ -8,7 +8,7 @@ License". .. qnum:: - :prefix: list-24- + :prefix: list-10- :start: 1 Strings and Lists diff --git a/_sources/Lists/TheAccumulatorPatternwithLists.rst b/_sources/Lists/TheAccumulatorPatternwithLists.rst index 5e77a5b1a..a1f3cddf5 100644 --- a/_sources/Lists/TheAccumulatorPatternwithLists.rst +++ b/_sources/Lists/TheAccumulatorPatternwithLists.rst @@ -7,7 +7,7 @@ License". .. qnum:: - :prefix: list-29- + :prefix: list-19- :start: 1 .. _accumulator_lists: diff --git a/_sources/Lists/TheReturnofLSystems.rst b/_sources/Lists/TheReturnofLSystems.rst index 3bfb82339..c91622fc1 100644 --- a/_sources/Lists/TheReturnofLSystems.rst +++ b/_sources/Lists/TheReturnofLSystems.rst @@ -8,7 +8,7 @@ License". .. qnum:: - :prefix: list-15- + :prefix: list-17- :start: 1 The Return of L-Systems diff --git a/_sources/Lists/UsingListsasParameters.rst b/_sources/Lists/UsingListsasParameters.rst index 8f6cbc43a..09398e42e 100644 --- a/_sources/Lists/UsingListsasParameters.rst +++ b/_sources/Lists/UsingListsasParameters.rst @@ -8,7 +8,7 @@ License". .. qnum:: - :prefix: list-18- + :prefix: list-20- :start: 1 Using Lists as Parameters diff --git a/_sources/Lists/toctree.rst b/_sources/Lists/toctree.rst index 3b79795c2..f0bf30178 100644 --- a/_sources/Lists/toctree.rst +++ b/_sources/Lists/toctree.rst @@ -9,18 +9,19 @@ Lists ListValues.rst ListLength.rst AccessingElements.rst - ListMembership.rst ConcatenationandRepetition.rst ListSlices.rst ListsareMutable.rst ListDeletion.rst + ListMethods.rst + StringsandLists.rst + AppendversusConcatenate.rst + ListMembership.rst + NestedLists.rst ObjectsandReferences.rst Aliasing.rst CloningLists.rst RepetitionandReferences.rst - ListMethods.rst - TheReturnofLSystems.rst - AppendversusConcatenate.rst Listsandforloops.rst TheAccumulatorPatternwithLists.rst UsingListsasParameters.rst @@ -28,8 +29,6 @@ Lists WhichisBetter.rst FunctionsthatProduceLists.rst ListComprehensions.rst - NestedLists.rst - StringsandLists.rst listTypeConversionFunction.rst TuplesandMutability.rst TupleAssignment.rst diff --git a/_sources/MoreAboutIteration/FlowofExecutionofthewhileLoop.rst b/_sources/MoreAboutIteration/FlowofExecutionofthewhileLoop.rst new file mode 100644 index 000000000..1a6f94ae5 --- /dev/null +++ b/_sources/MoreAboutIteration/FlowofExecutionofthewhileLoop.rst @@ -0,0 +1,43 @@ +.. Copyright (C) Brad Miller, David Ranum, Jeffrey Elkner, Peter Wentworth, Allen B. Downey, Chris + Meyers, and Dario Mitchell. Permission is granted to copy, distribute + and/or modify this document under the terms of the GNU Free Documentation + License, Version 1.3 or any later version published by the Free Software + Foundation; with Invariant Sections being Forward, Prefaces, and + Contributor List, no Front-Cover Texts, and no Back-Cover Texts. A copy of + the license is included in the section entitled "GNU Free Documentation + License". + +.. qnum:: + :prefix: turtle-5- + :start: 1 + +.. index:: control flow, flow of execution + + + +Flow of Execution of the while Loop +----------------------------------- + +As before with ``if``, loops allow us as programmers to manipulate the control flow of a Python program. +We can now possibly skip a portion of code, or choose to repeat it an indefinite number of times. + +The flowchart below provides the general sequence of steps that govern execution of a while loop. + +.. image:: Figures/while_flow.png + :width: 300px + :align: center + + +A codelens demonstration is a good way to help you visualize exactly how the flow of control +works with the while loop. Try stepping forward and backward through the program by pressing +the buttons. You can see the value of ``count`` change as the loop iterates through the values from 10 to 0. + +.. codelens:: vtest + + count = 10 + while count > 0: + print(count) + count = count - 1 + print("Blastoff!") + + diff --git a/_sources/MoreAboutIteration/NewtonsMethod.rst b/_sources/MoreAboutIteration/NewtonsMethod.rst index ef7c8b4e4..fb99ac735 100644 --- a/_sources/MoreAboutIteration/NewtonsMethod.rst +++ b/_sources/MoreAboutIteration/NewtonsMethod.rst @@ -38,22 +38,22 @@ value whose square root will be approximated. The second is the number of times calculation yielding a better result. .. activecode:: chp07_newtonsdef - - def newtonSqrt(n, howmany): - approx = 0.5 * n - for i in range(howmany): - betterapprox = 0.5 * (approx + n/approx) - approx = betterapprox - return betterapprox - - print(newtonSqrt(100, 10)) - print(newtonSqrt(4, 10)) - print(newtonSqrt(1, 10)) + + n = 100 + howmany = 10 + + approx = 0.5 * n + for i in range(howmany): + betterapprox = 0.5 * (approx + n/approx) + approx = betterapprox + prin(betterapprox) .. admonition:: Modify the program ... - All three of the calls to ``newtonSqrt`` in the previous example produce the correct square root for the first parameter. However, were 10 iterations required to get the correct answer? Experiment with different values for the number of repetitions (the 10 on lines 8, 9, and 10). For each of these calls, find the **smallest** value for the number of repetitions that will produce the **correct** result. + The values used in the previous example produce the correct square root for 100. However, were 10 iterations required to get the correct answer? + Experiment with different values for the number of repetitions (howmany on line 2). Find the **smallest** value for the number of + repetitions that will produce the **correct** result. Repeating more than the required number of times is a waste of computing resources. So definite iteration is not a good solution to this problem. @@ -66,15 +66,13 @@ uses a ``while`` condition to execute until the approximation is no longer chang .. codelens:: chp07_newtonswhile - def newtonSqrt(n): - approx = 0.5 * n - better = 0.5 * (approx + n/approx) - while better != approx: - approx = better - better = 0.5 * (approx + n/approx) - return approx - - print(newtonSqrt(10)) + n = 10 + approx = 0.5 * n + better = 0.5 * (approx + n/approx) + while better != approx: + approx = better + better = 0.5 * (approx + n/approx) + print(approx) .. note:: diff --git a/_sources/MoreAboutIteration/SentinelValuesAndValidation.rst b/_sources/MoreAboutIteration/SentinelValuesAndValidation.rst index a5f14f344..8e22d4713 100644 --- a/_sources/MoreAboutIteration/SentinelValuesAndValidation.rst +++ b/_sources/MoreAboutIteration/SentinelValuesAndValidation.rst @@ -21,7 +21,7 @@ Other uses of ``while`` Sentinel Values ~~~~~~~~~~~~~~~~~~~ -Indefinite loops are much more common in the real world than definite loops. +The indefinite loops provided by the ``while`` statement are common in the real world. * If you are selling tickets to an event, you don't know in advance how many tickets you will sell. You keep selling tickets as long as people come @@ -60,25 +60,22 @@ zero is a **sentinel value**, a value used to signal the end of the loop. Here's .. activecode:: ch07_sentinel :timelimit: 60000 - - def checkout(): - total = 0 - count = 0 - moreItems = True - while moreItems: - price = float(input('Enter price of item (0 when done): ')) - if price != 0: - count = count + 1 - total = total + price - print('Subtotal: $', total) - else: - moreItems = False - average = total / count - print('Total items:', count) - print('Total $', total) - print('Average price per item: $', average) - - checkout() + + total = 0 + count = 0 + moreItems = True + while moreItems: + price = float(input('Enter price of item (0 when done): ')) + if price != 0: + count = count + 1 + total = total + price + print('Subtotal: $', total) + else: + moreItems = False + average = total / count + print('Total items:', count) + print('Total $', total) + print('Average price per item: $', average) There are still a few problems with this program. @@ -122,20 +119,17 @@ When you run the following code, try typing something other than Y or N to see h .. activecode:: ch07_validation :timelimit: 60000 - - def get_yes_or_no(message): - valid_input = False - answer = input(message) - while not valid_input: - answer = answer.upper() # convert to upper case - if answer == 'Y' or answer == 'N': - valid_input = True - else: - answer = input('Please enter Y for yes or N for no. \n' + message) - return answer - - response = get_yes_or_no('Do you like lima beans? Y)es or N)o: ') - if response == 'Y': - print('Great! They are very healthy.') - else: - print('Too bad. If cooked right, they are quite tasty.') + + valid_input = False + response = input('Do you like lima beans? Y)es or N)o: ') + while not valid_input: + response = response.upper() # convert to upper case + if response == 'Y' or response == 'N': + valid_input = True + else: + response = input('Please enter Y for yes or N for no. \n' + message) + + if response == 'Y': + print('Great! They are very healthy.') + else: + print('Too bad. If cooked right, they are quite tasty.') diff --git a/_sources/MoreAboutIteration/The3n1Sequence.rst b/_sources/MoreAboutIteration/The3n1Sequence.rst index 25d0b377d..26ed77599 100644 --- a/_sources/MoreAboutIteration/The3n1Sequence.rst +++ b/_sources/MoreAboutIteration/The3n1Sequence.rst @@ -14,28 +14,27 @@ The 3n + 1 Sequence ------------------- -As another example of indefinite iteration, let's look at a sequence that has fascinated mathematicians for many years. +As another example of iteration with ``while``, let's look at a sequence that has fascinated mathematicians for many years. The rule for creating the sequence is to start from some positive integer, call it ``n``, and to generate the next term of the sequence from ``n``, either by halving ``n``, whenever ``n`` is even, or else by multiplying it by three and adding 1 when it is odd. The sequence terminates when ``n`` reaches 1. -This Python function captures that algorithm. Try running this program several times supplying different values for n. +This Python code captures that algorithm. Try running this program several times supplying different values for n. .. activecode:: ch07_indef1 - def seq3np1(n): - """ Print the 3n+1 sequence from n, terminating when it reaches 1.""" - while n != 1: - print(n) - if n % 2 == 0: # n is even - n = n // 2 - else: # n is odd - n = n * 3 + 1 - print(n) # the last print is 1 - - seq3np1(3) + n = 3 + + """ Print the 3n+1 sequence from n, terminating when it reaches 1.""" + while n != 1: + print(n) + if n % 2 == 0: # n is even + n = n // 2 + else: # n is odd + n = n * 3 + 1 + print(n) # the last print is 1 @@ -57,12 +56,6 @@ time through the loop until it reaches 1. You might like to have some fun and see if you can find a small starting number that needs more than a hundred steps before it terminates. - -.. admonition:: Lab - - * `Experimenting with the 3n+1 Sequence <../Labs/sequencelab.html>`_ In this guided lab exercise we will try to learn more about this sequence. - - Particular values aside, the interesting question is whether we can prove that this sequence terminates for *all* positive values of ``n``. So far, no one has been able to prove it *or* disprove it! @@ -77,24 +70,6 @@ You'll notice that if you don't stop when you reach one, the sequence gets into its own loop: 1, 4, 2, 1, 4, 2, 1, 4, and so on. One possibility is that there might be other cycles that we just haven't found. -.. admonition:: Choosing between ``for`` and ``while`` - - Use a ``for`` loop if you know the maximum number of times that you'll - need to execute the body. For example, if you're traversing a list of elements, - or can formulate a suitable call to ``range``, then choose the ``for`` loop. - - So any problem like "iterate this weather model run for 1000 cycles", or "search this - list of words", "check all integers up to 10000 to see which are prime" suggest that a ``for`` loop is best. - - By contrast, if you are required to repeat some computation until some condition is - met, as we did in this 3n + 1 problem, you'll need a ``while`` loop. - - As we noted before, the first case is called **definite iteration** --- we have some definite bounds for - what is needed. The latter case is called **indefinite iteration** --- we are not sure - how many iterations we'll need --- we cannot even establish an upper bound! - - - .. There are also some great visualization tools becoming available to help you .. trace and understand small fragments of Python code. The one we recommend is at .. http://netserv.ict.ru.ac.za/python3_viz diff --git a/_sources/MoreAboutIteration/TheforLoop.rst b/_sources/MoreAboutIteration/TheforLoop.rst new file mode 100644 index 000000000..45366ce09 --- /dev/null +++ b/_sources/MoreAboutIteration/TheforLoop.rst @@ -0,0 +1,94 @@ +.. Copyright (C) Brad Miller, David Ranum, Jeffrey Elkner, Peter Wentworth, Allen B. Downey, Chris + Meyers, and Dario Mitchell. Permission is granted to copy, distribute + and/or modify this document under the terms of the GNU Free Documentation + License, Version 1.3 or any later version published by the Free Software + Foundation; with Invariant Sections being Forward, Prefaces, and + Contributor List, no Front-Cover Texts, and no Back-Cover Texts. A copy of + the license is included in the section entitled "GNU Free Documentation + License". + +.. qnum:: + :prefix: turtle-3- + :start: 1 + +.. index:: for loop, iteration, body + loop; for + +The ``for`` Loop +---------------- + + +The ``while`` statement is a general-purpose tool for iteration, and is necessary for any instance of iteration where we don't know how many repetitions will be needed. +However, if we do know how many are needed, there is a more efficient method: the ``for`` statement. + +As a simple example, let's say we have some friends, and +we'd like to send them each an email inviting them to our party. We +don't quite know how to send email yet, so for the moment we'll just print a +message for each friend. + +.. activecode:: ch03_4 + :nocanvas: + :tour_1: "Overall Tour"; 1-2: Example04_Tour01_Line01; 2: Example04_Tour01_Line02; 1: Example04_Tour01_Line03; + + for name in ["Joe", "Amy", "Brad", "Angelina", "Zuki", "Thandi", "Paris"]: + print(f"Hi {name}! Please come to my party on Saturday!") + + +Take a look at the output produced when you press the ``run`` button. There is one line printed for each friend. Here's how it works: + + +* **name** in this ``for`` statement is the **loop variable**. +* The list of names in the square brackets is a regular list. Later we'll see that other types besides lists can be put in this spot. +* Line 2 is the **loop body**. Like with while, the loop body is always + indented. The loop body is performed one time for each name in the list. +* On each *iteration* or *pass* of the loop, a check is done to see if + there are still more items to be processed. If there are none left (this is + called the **terminating condition** of the loop), the loop has finished. + Program execution continues at the next statement after the loop body. +* If there are items still to be processed, the loop variable is updated to + refer to the next item in the list. This means, in this case, that the loop + body is executed here 7 times, and each time ``name`` will refer to a different + friend. +* At the end of each execution of the body of the loop, Python returns + to the ``for`` statement, to see if there are more items to be handled. + +.. note:: + + Introduction of the for statement causes us to think about the types of iteration we have seen. The ``for`` statement will always iterate through a sequence of + values like the list of names for the party. + Since we know that it will iterate once for each value in the collection, it is often said that a ``for`` loop creates a + **definite iteration** because we definitely know how many times we are going to iterate. On the other + hand, the ``while`` statement is dependent on a condition that needs to evaluate to ``False`` in order + for the loop to terminate. Since we do not necessarily know when this will happen, it creates what we + call **indefinite iteration**. Indefinite iteration simply means that we don't know how many times we will repeat but eventually the condition + controlling the iteration will fail and the iteration will stop. (Unless we have an infinite loop which is of course a problem.) + +.. admonition:: Choosing between ``for`` and ``while`` + + Use a ``for`` loop if you know the maximum number of times that you'll + need to execute the body. For example, if you're traversing a list of elements, + or can formulate a suitable call to ``range``, then choose the ``for`` loop. + + So any problem like "iterate this weather model run for 1000 cycles", or "search this + list of words", "check all integers up to 10000 to see which are prime" suggest that a ``for`` loop is best. + + By contrast, if you are required to repeat some computation until some condition is + met, as we did in this 3n + 1 problem, you'll need a ``while`` loop. + +What you will notice here is that the ``while`` loop is more work for +you --- the programmer --- than the equivalent ``for`` loop. When using a ``while`` +loop you have to control the loop variable yourself. You give it an initial value, test +for completion, and then make sure you change something in the body so that the loop +terminates. + +**Check your understanding** + +.. mchoice:: test_question7_6_1 + :practice: T + :answer_a: True + :answer_b: False + :correct: a + :feedback_a: The syntax for a for-loop can make it easier and more appealing, but a while loop is just as powerful as a for-loop and often more flexible. + :feedback_b: Often a for-loop is more natural and convenient for a task, but that same task can always be expressed using a while loop. + + True or False: You can rewrite any for-loop as a while-loop. diff --git a/_sources/MoreAboutIteration/TherangeFunction.rst b/_sources/MoreAboutIteration/TherangeFunction.rst new file mode 100644 index 000000000..fcafc68c3 --- /dev/null +++ b/_sources/MoreAboutIteration/TherangeFunction.rst @@ -0,0 +1,193 @@ +.. Copyright (C) Brad Miller, David Ranum, Jeffrey Elkner, Peter Wentworth, Allen B. Downey, Chris + Meyers, and Dario Mitchell. Permission is granted to copy, distribute + and/or modify this document under the terms of the GNU Free Documentation + License, Version 1.3 or any later version published by the Free Software + Foundation; with Invariant Sections being Forward, Prefaces, and + Contributor List, no Front-Cover Texts, and no Back-Cover Texts. A copy of + the license is included in the section entitled "GNU Free Documentation + License". + +.. qnum:: + :prefix: turtle-8- + :start: 1 + +The range Function +------------------ + +.. youtube:: YK8QlIT3__M + :divid: advrange + :height: 315 + :width: 560 + :align: left + +In our first example of a while loop, we counted down from 10 to 0. If we were to consider doing this with a for loop, we would need to construct our own series of numbers +to loop through them. + +It turns out that generating lists with a specific number of integers is a very common thing to do, especially when you +want to write simple ``for loop`` controlled iteration. The conventional thing to do is to use a list of integers starting with 0. +In fact, these lists are so popular that Python gives us special built-in ``range`` objects that can deliver a sequence of values to +the ``for`` loop. When called with one parameter, the sequence provided by ``range`` always starts with 0. If you ask for ``range(4)``, then you will get 4 values starting with 0. In other words, 0, 1, 2, and finally 3. Notice that 4 is not included since we started with 0. Likewise, ``range(10)`` provides 10 values, 0 through 9. + +.. sourcecode:: python + + for i in range(4): + # Executes the body with i = 0, then 1, then 2, then 3 + for x in range(10): + # sets x to each of ... [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + +.. note:: + + Computer scientists like to count from 0! + + +So to count something ten times, a good Python programmer would do this: + +.. sourcecode:: python + + for i in range(10): + #do something + + +The `range `_ function is actually a very powerful function +when it comes to +creating sequences of integers. It can take one, two, or three parameters. We have seen +the simplest case of one parameter such as ``range(4)`` which creates ``[0, 1, 2, 3]``. +But what if we really want to have the sequence ``[1, 2, 3, 4]``? +We can do this by using a two parameter version of ``range`` where the first parameter is the starting point and the second parameter is the ending point. The evaluation of ``range(1,5)`` produces the desired sequence. What happened to the 5? +In this case we interpret the parameters of the range function to mean +range(start,beyondLast), where beyondLast means an index past the last index we want. In the 2-parameter version +of range, that is the last index included + 1. + + +.. note:: + + Why in the world would range not just work like range(start, + stop)? Think about it like this. Because computer scientists like to + start counting at 0 instead of 1, ``range(N)`` produces a sequence of + things that is N long, but the consequence of this is that the final + number of the sequence is N-1. In the case of start, + stop it helps to simply think that the sequence begins with start and + continues as long as the number is less than stop. + +.. note:: + The range function is *lazy*: It produces the next element only when needed. + With a regular Python 3 interpreter, printing a range does *not* calculate all the elements. + To immediately calculate all the elements in a range, + wrap the range in a list, like ``list(range(4))``. + Activecode is not designed to work on very long sequences, and it may allow you to be + sloppy, avoiding the list function, and *see* the elements in the range with ``print(range(4))``. + +Here are two examples for you to run. Try them and then add another line below to create a sequence starting +at 10 and going up to 20 (including 20). + +.. activecode:: ch03_5 + :nocanvas: + + print(list(range(4))) + print(list(range(1, 5))) + + +Codelens will help us to further understand the way range works. In this case, the variable ``i`` will take on values +produced by the ``range`` function. + +.. codelens:: rangeme + + for i in range(10): + print(i) + + + +Finally, suppose we want to actually count down from 10 to 1. +How would we do that? Easy, we add another parameter, a step, +that tells range what to count by. For our purposes, we want to start at 10 +and count down by 1 each time. So if we wanted 10 numbers counting down we would use +``range(10,0,-1)``. The most general form of the range is +``range(start, beyondLast, step)``. You can also create a sequence of numbers that +skips some using a step size larger than 1 or smaller than -1. + +.. activecode:: ch03_6 + :nocanvas: + + print(list(range(0, 19, 2))) + print(list(range(0, 20, 2))) + print(list(range(10, 0, -1))) + +Try it in codelens. Do you see why the first two statements produce the same result? + + +.. codelens:: rangeme2 + + for i in range(0, 20, 2): + print(i) + +**Check your understanding** + +.. mchoice:: test_question3_5_1 + :practice: T + :answer_a: Range should generate a sequence that stops before 10 (including 9). + :answer_b: Range should generate a sequence that starts at 10 (including 10). + :answer_c: Range should generate a sequence starting at 3 that stops at 10 (including 10). + :answer_d: Range should generate a sequence using every 10th number between the start and the stopping number. + :correct: a + :feedback_a: Range will generate the sequence 3, 5, 7, 9. + :feedback_b: The first argument (3) tells range what number to start at. + :feedback_c: Range will always stop at the number in the sequence before (not including) the specified limit for the sequence. + :feedback_d: The third argument (2) tells range how many numbers to skip between each element in the sequence. + + In the command range(3, 10, 2), what does the second argument (10) specify? + +.. mchoice:: test_question3_5_2 + :practice: T + :answer_a: range(2, 5, 8) + :answer_b: range(2, 8, 3) + :answer_c: range(2, 10, 3) + :answer_d: range(8, 1, -3) + :correct: c + :feedback_a: This command generates the sequence with just the number 2 because the first parameter (2) tells range where to start, the second number tells range where to end (before 5) and the third number tells range how many numbers to skip between elements (8). Since 10 >= 5, there is only one number in this sequence. + :feedback_b: This command generates the sequence 2, 5 because 8 is not less than 8 (the specified number past the end). + :feedback_c: The first number is the starting point, the second is past the last allowed, and the third is the amount to increment by. + :feedback_d: This command generates the sequence 8, 5, 2 because it starts at 8, ends before 1, and skips to every third number going down. + + What command correctly generates the sequence 2, 5, 8? + +.. mchoice:: test_question3_5_3 + :practice: T + :answer_a: It will generate a sequence starting at 0, with every number included up to but not including the argument it was passed. + :answer_b: It will generate a sequence starting at 1, with every number up to but not including the argument it was passed. + :answer_c: It will generate a sequence starting at 1, with every number including the argument it was passed. + :answer_d: It will cause an error: range always takes exactly 3 arguments. + :correct: a + :feedback_a: Yes, if you only give one number to range it starts with 0 and ends before the number specified incrementing by 1. + :feedback_b: Range with one parameter starts at 0. + :feedback_c: Range with one parameter starts at 0, and never includes the argument it was passed. + :feedback_d: If range is passed only one argument, it interprets that argument as one past the end of the list. + + What happens if you give range only one argument? For example: range(4) + +.. mchoice:: test_question3_5_4 + :practice: T + :answer_a: range(5, 25, 5) + :answer_b: range(20, 3, -5) + :answer_c: range(20, 5, 4) + :answer_d: range(20, 5, -5) + :correct: b + :feedback_a: The step 5 is positive, while the given sequence is decreasing. This answer creates the reversed, increasing sequence. + :feedback_b: Yes: If we take steps of -5, not worrying about the ending, we get 20, 15, 10, 5, 0, .... The limit 3 is past the 5, so the range sequence stops with the 5. + :feedback_c: The step 5 is positive so the sequence would need to increase from 20 toward 4. That does not make sense and the sequence would be empty. + :feedback_d: the sequence can never include the second parameter (5). The second parameter must always be past the end of the range sequence. + + Which range function call will produce the sequence 20, 15, 10, 5? + + +.. mchoice:: test_question3_5_5 + :practice: T + :answer_a: No other value would give the same sequence. + :answer_b: The only other choice is 14. + :answer_c: 11, 13, or 14 + :correct: c + :feedback_a: The sequence produced has steps of 4: 2, 6, 10. The next would be 14, but it is not before the limit 12. There are other limit choices past 10, but not past 14. + :feedback_b: 14 would work: It is also past 10, and not past 14, but there are other integers with the same properties. + :feedback_c: Yes, any integer past 10, and not past the next step at 14 would work. + + What could the second parameter (12) in range(2, 12, 4) be replaced with and generate exactly the same sequence? diff --git a/_sources/MoreAboutIteration/ThewhileStatement.rst b/_sources/MoreAboutIteration/ThewhileStatement.rst index 201fdfc99..8d08fa036 100644 --- a/_sources/MoreAboutIteration/ThewhileStatement.rst +++ b/_sources/MoreAboutIteration/ThewhileStatement.rst @@ -20,75 +20,39 @@ The ``while`` Statement :width: 560 :align: left -There is another Python statement that can also be used to build an iteration. It is called the ``while`` statement. -The ``while`` statement provides a much more general mechanism for iterating. Similar to the ``if`` statement, it uses -a boolean expression to control the flow of execution. The body of while will be repeated as long as the controlling boolean expression evaluates to ``True``. +A basic building block of all programs is to be able to repeat some code +over and over again. In computer science, we refer to this repetitive idea as **iteration**. In this section, we will explore some mechanisms for basic iteration. -The following figure shows the flow of control. +Let's look at our first Python statement that can be used to build an iteration. It is called the ``while`` statement. When used with other code it can be used to +repeat code in a **while loop**. Similar to the ``if`` statement, it uses +a boolean expression to control the flow of execution. The body of while (code indented one space in) will be repeated as long as the controlling boolean +expression evaluates to ``True``. -.. image:: Figures/while_flow.png - -We can use the ``while`` loop to create any type of iteration we wish, including anything that we have previously done with a ``for`` loop. For example, the program in the previous section could be rewritten using ``while``. -Instead of relying on the ``range`` function to produce the numbers for our summation, we will need to produce them ourselves. To to this, we will create a variable called ``aNumber`` and initialize it to 1, the first number in the summation. Every iteration will add ``aNumber`` to the running total until all the values have been used. -In order to control the iteration, we must create a boolean expression that evaluates to ``True`` as long as we want to keep adding values to our running total. In this case, as long as ``aNumber`` is less than or equal to the bound, we should keep going. - - - -Here is a new version of the summation program that uses a while statement. +Here is a simple example that counts down from 10 to 0. .. activecode:: ch07_while1 - def sumTo(aBound): - """ Return the sum of 1+2+3 ... n """ - - theSum = 0 - aNumber = 1 - while aNumber <= aBound: - theSum = theSum + aNumber - aNumber = aNumber + 1 - return theSum - - print(sumTo(4)) - - print(sumTo(1000)) - - - -You can almost read the ``while`` statement as if it were in natural language. It means, -while ``aNumber`` is less than or equal to ``aBound``, continue executing the body of the loop. Within -the body, each time, update ``theSum`` using the accumulator pattern and increment ``aNumber``. After the body of the loop, we go back up to the condition of the ``while`` and reevaluate it. When ``aNumber`` becomes greater than ``aBound``, the condition fails and flow of control continues to the ``return`` statement. - -The same program in codelens will allow you to observe the flow of execution. - -.. codelens:: ch07_while2 - - def sumTo(aBound): - """ Return the sum of 1+2+3 ... n """ - - theSum = 0 - aNumber = 1 - while aNumber <= aBound: - theSum = theSum + aNumber - aNumber = aNumber + 1 - return theSum - - print(sumTo(4)) - - - -More formally, here is the flow of execution for a ``while`` statement: - -#. Evaluate the condition, yielding ``False`` or ``True``. -#. If the condition is ``False``, exit the ``while`` statement and continue - execution at the next statement. -#. If the condition is ``True``, execute each of the statements in the body and - then go back to step 1. - -The body consists of all of the statements below the header with the same -indentation. - -This type of flow is called a **loop** because the third step loops back around -to the top. Notice that if the condition is ``False`` the first time through the + count = 10 + while count > 0: + print(count) + count = count - 1 + print("Blastoff!") + + +* **count** is a normal variable here, but since it is governing the ``while`` loop it is also called the **loop variable**. +* Line 2 here is the **loop condition**. It must always be a boolean expression that will evaluate to ``False`` or ``True``. +* Lines 3 and 4 are the **loop body**. The loop body is always + indented. The indentation determines exactly what statements are "in the + loop". The loop body is run each time the loop is repeated. +* On each *iteration* or *pass* of the loop, a check is done to see if + the loop condition is True (if ``count`` is greater than zero). If it is not (this is + called the **terminating condition** of the loop), the loop has finished. + Program execution continues at the next statement after the loop body. +* If ``count`` is greater than zero, the loop body is executed again. +* At the end of each execution of the body of the loop, Python returns + to the ``while`` statement, to see if the loop should repeat. + +Notice that if the condition is ``False`` the first time through the loop, the statements inside the loop are never executed. .. warning:: @@ -102,51 +66,61 @@ loop, the statements inside the loop are never executed. The body of the loop should change the value of one or more variables so that eventually the condition becomes ``False`` and the loop terminates. Otherwise the loop will repeat forever. This is called an **infinite loop**. -An endless -source of amusement for computer scientists is the observation that the +An endless source of amusement for computer scientists is the observation that the directions written on the back of the shampoo bottle (lather, rinse, repeat) create an infinite loop. -In the case shown above, we can prove that the loop terminates because we -know that the value of ``aBound`` is finite, and we can see that the value of ``aNumber`` -increments each time through the loop, so eventually it will have to exceed ``aBound``. In -other cases, it is not so easy to tell. +We can use the ``while`` loop to create any type of iteration we wish, making more general-purpose than the ``for`` loop we'll learn next week. +For example, let us consider a program that adds all numbers from ``1`` to ``n``. To do this, we will create a variable called ``aNumber`` and initialize it to +1, the first number in the summation. Every iteration will add ``aNumber`` to the running total until all the values have been used. +In order to control the iteration, we must create a boolean expression that evaluates to ``True`` as long as we want to keep adding values to our +running total. In this case, as long as ``aNumber`` is less than or equal to the bound, we should keep going. -.. note:: +Here is the summation program that uses a while statement. - Introduction of the while statement causes us to think about the types of iteration we have seen. The ``for`` statement will always iterate through a sequence of values like the list of names for the party or the list of numbers created by ``range``. Since we know that it will iterate once for each value in the collection, it is often said that a ``for`` loop creates a - **definite iteration** because we definitely know how many times we are going to iterate. On the other - hand, the ``while`` statement is dependent on a condition that needs to evaluate to ``False`` in order - for the loop to terminate. Since we do not necessarily know when this will happen, it creates what we - call **indefinite iteration**. Indefinite iteration simply means that we don't know how many times we will repeat but eventually the condition controlling the iteration will fail and the iteration will stop. (Unless we have an infinite loop which is of course a problem.) +.. activecode:: ch07_while2 -What you will notice here is that the ``while`` loop is more work for -you --- the programmer --- than the equivalent ``for`` loop. When using a ``while`` -loop you have to control the loop variable yourself. You give it an initial value, test -for completion, and then make sure you change something in the body so that the loop -terminates. + """ Return the sum of 1+2+3 ... n """ + aBound = int(input("Please give a number n: ")) + theSum = 0 + aNumber = 1 + while aNumber <= aBound: + theSum = theSum + aNumber + aNumber = aNumber + 1 + print(theSum) -So why have two kinds of loop if ``for`` looks easier? The next section, :ref:`randomly-walking-turtles`, shows an indefinite iteration where -we need the extra power that we get from the ``while`` loop. -.. note:: +You can almost read the ``while`` statement as if it were in natural language. It means, +while ``aNumber`` is less than or equal to ``aBound``, continue executing the body of the loop. Within +the body, each time, update ``theSum`` and increment ``aNumber``. After the body of the loop, we go +back up to the condition of the ``while`` and reevaluate it. When ``aNumber`` becomes greater +than ``aBound``, the condition fails and flow of control continues to the ``print`` statement. - This workspace is provided for your convenience. You can use this activecode window to try out anything you like. +The same program in codelens will allow you to observe the flow of execution. - .. activecode:: scratch_07_01 +.. codelens:: ch07_while3 + """ Return the sum of 1+2+3 ... n """ + aBound = 10 + theSum = 0 + aNumber = 1 + while aNumber <= aBound: + theSum = theSum + aNumber + aNumber = aNumber + 1 + print(theSum) -**Check your understanding** -.. mchoice:: test_question7_2_1 - :practice: T - :answer_a: True - :answer_b: False - :correct: a - :feedback_a: Although the while loop uses a different syntax, it is just as powerful as a for-loop and often more flexible. - :feedback_b: Often a for-loop is more natural and convenient for a task, but that same task can always be expressed using a while loop. - True or False: You can rewrite any for-loop as a while-loop. +In the case shown above, we can prove that the loop terminates because we +know that the value of ``aBound`` is finite, and we can see that the value of ``aNumber`` +increments each time through the loop, so eventually it will have to exceed ``aBound``. In +other cases, it is not so easy to tell. + +.. note:: + + This workspace is provided for your convenience. You can use this activecode window to try out anything you like. + + .. activecode:: scratch_07_01 .. mchoice:: test_question7_2_2 :practice: T diff --git a/_sources/MoreAboutIteration/intro-IterationRevisited.rst b/_sources/MoreAboutIteration/intro-IterationRevisited.rst index fac46c62c..e1e91716a 100644 --- a/_sources/MoreAboutIteration/intro-IterationRevisited.rst +++ b/_sources/MoreAboutIteration/intro-IterationRevisited.rst @@ -11,7 +11,7 @@ :prefix: iter-1- :start: 1 -Iteration Revisited +Iteration =================== .. index:: iteration, assignment, assignment statement, reassignment @@ -26,10 +26,8 @@ people do poorly. Repeated execution of a sequence of statements is called **iteration**. Because iteration is so common, Python provides several language features to make it -easier. We've already seen the ``for`` statement in a previous chapter. This is a very common -form of iteration in Python. In this chapter -we are going to look at the ``while`` statement --- another way to have your -program do iteration. +easier. In this chapter we are going to look at two common forms of iteration: the ``while`` statement +and the ``for`` statement. .. index:: for loop diff --git a/_sources/MoreAboutIteration/toctree.rst b/_sources/MoreAboutIteration/toctree.rst index 6b944d8ff..1a492e782 100644 --- a/_sources/MoreAboutIteration/toctree.rst +++ b/_sources/MoreAboutIteration/toctree.rst @@ -1,21 +1,19 @@ -More About Iteration +Iteration :::::::::::::::::::: .. toctree:: - :caption: More About Iteration + :caption: Iteration :maxdepth: 2 - intro-IterationRevisited.rst - Theforlooprevisited.rst + intro-Iteration.rst ThewhileStatement.rst - RandomlyWalkingTurtles.rst + FlowofExecutionofthewhileLoop.rst The3n1Sequence.rst + SentinelValuesAndValidation.rst + TheforLoop.rst + TherangeFunction.rst NewtonsMethod.rst accumulatorRevisited.rst - SentinelValuesAndValidation.rst AlgorithmsRevisited.rst - SimpleTables.rst - 2DimensionalIterationImageProcessing.rst - ImageProcessingonYourOwn.rst Glossary.rst Exercises.rst diff --git a/_sources/PythonModules/CreatingModules.rst b/_sources/PythonModules/CreatingModules.rst index c704b5971..d425cf397 100644 --- a/_sources/PythonModules/CreatingModules.rst +++ b/_sources/PythonModules/CreatingModules.rst @@ -7,7 +7,7 @@ Creating Modules ---------------- -You've seen how to *use* modules like ``random``, ``math``, and ``turtle``, but how would you *create* a module? +You've seen how to *use* modules like ``random`` and ``math``, but how would you *create* a module? Every time you've written a Python script you've created a module! @@ -120,10 +120,6 @@ Also - look at all the awesome comments in there! It is important to include header comments in your module that explain what the module does. -.. admonition:: Function Comments - - Functions are the next chapter, but the comments used here demonstrate a common Python documentation style. - Ok - so we've got a function in our module now, let's use it. *coffee_customer.py* diff --git a/_sources/PythonModules/MoreAboutUsingModules.rst b/_sources/PythonModules/MoreAboutUsingModules.rst index 4e3076a14..5ea3269ff 100644 --- a/_sources/PythonModules/MoreAboutUsingModules.rst +++ b/_sources/PythonModules/MoreAboutUsingModules.rst @@ -20,18 +20,8 @@ like any other data in Python. Module objects simply contain other Python eleme The first thing we need to do when we wish to use a module is perform an ``import``. In the example above, the statement -``import turtle`` creates a new name, ``turtle``, and makes it refer to a `module object`. This looks very much like +``import math`` creates a new name, ``math``, and makes it refer to a `module object`. This looks very much like the reference diagrams we saw earlier for simple variables. - -.. image:: Figures/modreference.png - -In order to use something contained in a module, we use the `dot` notation, providing the module name and the specific item joined together with a "dot". For example, to use the ``Turtle`` class, we say ``turtle.Turtle``. You should read -this as: "In the module turtle, access the Python element called Turtle". - -We will now turn our attention to a few other modules that you might find useful. - -.. youtube:: SGVgAV0v-Ww - :height: 315 - :width: 560 - :align: left +In order to use something contained in a module, we use the `dot` notation, providing the module name and the specific item joined together with a "dot". For example, to use the ``sqrt`` function, we say ``math.sqrt``. You should read +this as: "In the module math, access the Python element called sqrt". diff --git a/_sources/PythonModules/modules.rst b/_sources/PythonModules/modules.rst index f17176f66..bbd090ff9 100644 --- a/_sources/PythonModules/modules.rst +++ b/_sources/PythonModules/modules.rst @@ -25,28 +25,26 @@ Modules and Getting Help A **module** is a file containing Python definitions and statements intended for use in other Python programs. There are many Python modules that come with -Python as part of the **standard library**. We have already used one of these quite extensively, -the ``turtle`` module. Recall that once we import the module, we can use things +Python as part of the **standard library**. We have already used one of these briefly, +the ``math`` module. Recall that once we import the module, we can use things that are defined inside. .. activecode:: chmod_01 :nocodelens: - import turtle # allows us to use the turtles library + import math # allows us to use the math library - wn = turtle.Screen() # creates a graphics window - alex = turtle.Turtle() # create a turtle named alex + print(math.factorial(10)) # prints 10! + print(math.exp(4)) # prints e^4 - alex.forward(150) # tell alex to move forward by 150 units - alex.left(90) # turn by 90 degrees - alex.forward(75) # complete the second side of a rectangle - wn.exitonclick() + print(math.log2(1024)) # prints log2(1024) + print(math.sqrt(100)) # prints the square root of 100 -Here we are using ``Screen`` and ``Turtle``, both of which are defined inside the turtle module. +Here we are using ``factorial``, ``exp``, ``log2``, and ``sqrt``, all of which are defined inside the math module. -But what if no one had told us about turtle? How would we know +But what if no one had told us about math? How would we know that it exists. How would we know what it can do for us? The answer is to ask for help and the best place to get help about the Python programming environment is to consult with the Python Documentation. @@ -66,13 +64,13 @@ and to use it often. .. image:: Figures/pythondocmedium.png If you have not done so already, take a look at the Global Module Index. Here you will see an alphabetical listing of all -the modules that are available as part of the standard library. Find the turtle module. +the modules that are available as part of the standard library. Find the math module. .. image:: Figures/moduleindexmedium.png -.. image:: Figures/turtlemodmedium.png -You can see that all the turtle functionality that we have talked about is there. However, there is so much more. Take some time to read through and familiarize yourself with some of the other things that turtles can do. +You can see that all the math functionality that we have talked about is there. However, there is so much more. +Take some time to read through and familiarize yourself with some of the other things that math can do. @@ -84,7 +82,7 @@ You can see that all the turtle functionality that we have talked about is there activecode used here was strictly to help us learn. It is not the way we write production programs. To that end, it is necessary to mention that many of the modules available in standard Python - will **not** work in the activecode environment. In fact, only turtle, math, and random have been + will **not** work in the activecode environment. In fact, only math and random have been completely ported at this point. If you wish to explore any additional modules, you will need to also explore using a more robust development environment. diff --git a/_sources/PythonModules/toctree.rst b/_sources/PythonModules/toctree.rst index 246062f60..fc17267f9 100644 --- a/_sources/PythonModules/toctree.rst +++ b/_sources/PythonModules/toctree.rst @@ -7,7 +7,6 @@ Python Modules modules.rst MoreAboutUsingModules.rst - Themathmodule.rst Therandommodule.rst CreatingModules.rst Glossary.rst diff --git a/_sources/Selection/Chainedconditionals.rst b/_sources/Selection/Chainedconditionals.rst index ec543f191..96b3a669b 100644 --- a/_sources/Selection/Chainedconditionals.rst +++ b/_sources/Selection/Chainedconditionals.rst @@ -135,26 +135,4 @@ Here is the same program using ``elif``. print(x, " is 0") -.. mchoice:: test_question6_7_2 - :practice: T - :answer_a: a - :answer_b: b - :answer_c: c - :correct: c - :feedback_a: While the value in x is less than the value in y (3 is less than 5) it is not less than the value in z (3 is not less than 2). - :feedback_b: The value in y is not less than the value in x (5 is not less than 3). - :feedback_c: Since the first two Boolean expressions are false the else will be executed. - - What will the following code print if x = 3, y = 5, and z = 2? - - .. code-block:: python - - if x < y and x < z: - print("a") - elif y < x and y < z: - print("b") - else: - print("c") - - diff --git a/_sources/Selection/ConditionalExecutionBinarySelection.rst b/_sources/Selection/ConditionalExecutionBinarySelection.rst index 76ab28dcc..20ac98a18 100644 --- a/_sources/Selection/ConditionalExecutionBinarySelection.rst +++ b/_sources/Selection/ConditionalExecutionBinarySelection.rst @@ -56,14 +56,22 @@ indented under the ``else`` clause get executed. .. image:: Figures/flowchart_if_else.png - - -As with the function definition from the last chapter and other compound -statements like ``for``, the ``if`` statement consists of a header line and a body. The header +As a program executes, the interpreter always keeps track of which statement is +about to be executed. We call this the **control flow**, or the **flow of +execution** of the program. When humans execute programs, they often use their +finger to point to each statement in turn. So you could think of control flow +as "Python's moving finger". + +Control flow until now has been strictly top to bottom, one statement at a +time. We call this type of control **sequential**. In Python flow is sequential as long as +successive statements are indented the *same* amount. The ``if`` statement +introduces indented sub-statements after the if heading. + +Each ``if`` statement consists of a header line and a body. The header line begins with the keyword ``if`` followed by a *boolean expression* and ends with a colon (:). -The more indented statements that follow are called a **block**. +The more indented statements that follow are called a **block** or sometimes a **body**. Each of the statements inside the first block of statements is executed in order if the boolean expression evaluates to ``True``. The entire first block of statements diff --git a/_sources/Selection/toctree.rst b/_sources/Selection/toctree.rst index cd2eb1482..03604c8e8 100644 --- a/_sources/Selection/toctree.rst +++ b/_sources/Selection/toctree.rst @@ -6,12 +6,12 @@ Selection :maxdepth: 2 BooleanValuesandBooleanExpressions.rst - Logicaloperators.rst - PrecedenceofOperators.rst ConditionalExecutionBinarySelection.rst OmittingtheelseClauseUnarySelection.rst Nestedconditionals.rst Chainedconditionals.rst + Logicaloperators.rst + PrecedenceofOperators.rst BooleanFunctions.rst Glossary.rst Exercises.rst diff --git a/_sources/Strings/ACollectionDataType.rst b/_sources/Strings/ACollectionDataType.rst index 03c5e92c7..e8eda97bf 100644 --- a/_sources/Strings/ACollectionDataType.rst +++ b/_sources/Strings/ACollectionDataType.rst @@ -14,12 +14,10 @@ A Collection Data Type ---------------------- -So far we have seen built-in types like: ``int``, ``float``, -``bool``, ``str`` and we've seen lists. -``int``, ``float``, and -``bool`` are considered to be simple or primitive data types because their values are not composed +So far we have seen built-in types like: ``int``, ``float``, and ``str``. +``int`` and ``float`` are considered to be simple or primitive data types because their values are not composed of any smaller parts. They cannot be broken down. -On the other hand, strings and lists are different from the others because they +On the other hand, strings (and lists that we will talk about next chapter) are different from the others because they are made up of smaller pieces. In the case of strings, they are made up of smaller strings each containing one **character**. @@ -30,7 +28,8 @@ single entity (the whole), or we may want to access its parts. This ambiguity is Strings can be defined as sequential collections of characters. This means that the individual characters that make up the string are assumed to be in a particular order from left to right. -A string that contains no characters, often referred to as the **empty string**, is still considered to be a string. It is simply a sequence of zero characters and is represented by '' or "" (two single or two double quotes with nothing in between). +A string that contains no characters, often referred to as the **empty string**, is still considered to be a string. +It is simply a sequence of zero characters and is represented by '' or "" (two single or two double quotes with nothing in between). .. index:: string operations, concatenation diff --git a/_sources/Strings/StringComparison.rst b/_sources/Strings/StringComparison.rst index e239e4b69..e566331e4 100644 --- a/_sources/Strings/StringComparison.rst +++ b/_sources/Strings/StringComparison.rst @@ -8,7 +8,7 @@ License". .. qnum:: - :prefix: strings-8- + :prefix: strings-9- :start: 1 diff --git a/_sources/Strings/StringMethods.rst b/_sources/Strings/StringMethods.rst index 307cfda0a..c08c30fe6 100644 --- a/_sources/Strings/StringMethods.rst +++ b/_sources/Strings/StringMethods.rst @@ -16,13 +16,11 @@ String Methods -------------- -We previously saw that each turtle instance has its own attributes and -a number of methods that can be applied to the instance. For example, -we wrote ``tess.right(90)`` when we wanted the turtle object ``tess`` to perform the ``right`` method to turn -to the right 90 degrees. The "dot notation" is the way we connect the name of an object to the name of a method -it can perform. +We previously used a few functions like ``print()`` and ``input()``. A **method** is a function that is attached to a specific Python object. +To access this function, we write the object, then a dot ``.``, and then the name of the method. The "dot notation" is the way we connect the name of an object to the name of a method +it can perform. For example, we can write ``ss.upper()`` when we wanted the string ``ss`` to perform the ``upper()`` method to create an upper-case version of itself. -Strings are also objects. Each string instance has its own attributes and methods. The most important attribute of the string is the collection of characters. There are a wide variety of methods. Try the following program. +Remember that Strings are objects. Each string instance has its own attributes and methods. The most important attribute of the string is the collection of characters. There are a wide variety of methods. Try the following program. .. activecode:: chp08_upper @@ -157,7 +155,7 @@ change the original. You can also consult the `Python documentation for strings .. _Format-Strings: -String Format Method +F-Strings ~~~~~~~~~~~~~~~~~~~~~ In grade school quizzes a common convention is to use fill-in-the blanks. For instance, @@ -168,26 +166,25 @@ In grade school quizzes a common convention is to use fill-in-the blanks. For in and you can fill in the name of the person greeted, and combine given text with a chosen insertion. *We use this as an analogy:* Python has a similar -construction, better called fill-in-the-braces. The string method ``format``, makes +construction, called a formatted string or an **f-string**. An f-string makes substitutions into places in a string enclosed in braces. Run this code: .. activecode:: ch08_methods3 person = input('Your name: ') - greeting = 'Hello {}!'.format(person) + greeting = f'Hello {person}!' print(greeting) There are several new ideas here! -The string for the ``format`` method has a special form, with braces embedded. -Such a string is called a *format string*. Places where -braces are embedded are replaced by the value of an expression -taken from the parameter list for the ``format`` method. There are many +The string has been formatted in a new way. We have included an ``f`` before the starting quotation mark. +Such a string is called an *f-string*. Places where +braces are embedded are replaced by the value of the expression inside the braces. There are many variations on the syntax between the braces. In this case we use the syntax where the first (and only) location in the string with -braces has a substitution made from the first (and only) parameter. +braces has the variable ``person``. When this code is evaluated, the value of the person variable is placed in the string in this location. In the code above, this new string is assigned to the identifier ``greeting``, and then the string is printed. @@ -201,7 +198,7 @@ version: .. activecode:: ch08_methods4 person = input('Enter your name: ') - print('Hello {}!'.format(person)) + print(f'Hello {person}!') There can be multiple substitutions, with data of any type. Next we use floats. Try original price $2.50 with a 7% discount: @@ -211,35 +208,29 @@ Next we use floats. Try original price $2.50 with a 7% discount: origPrice = float(input('Enter the original price: $')) discount = float(input('Enter discount percentage: ')) newPrice = (1 - discount/100)*origPrice - calculation = '${} discounted by {}% is ${}.'.format(origPrice, discount, newPrice) + calculation = f'${origPrice} discounted by {discount}% is ${newPrice}.' print(calculation) -The parameters are inserted into the braces in order. - If you used the data suggested, this result is not satisfying. Prices should appear with exactly two places beyond the decimal point, but that is not the default way to display floats. -Format strings can give further information inside the braces +F-strings can give further information inside the braces showing how to specially format data. In particular floats can be shown with a specific number of decimal places. -For two decimal places, put ``:.2f`` inside the braces for the monetary values: +For two decimal places, put ``:.2f`` inside the braces but after the variable name for the monetary values: .. activecode:: ch08_methods6 origPrice = float(input('Enter the original price: $')) discount = float(input('Enter discount percentage: ')) newPrice = (1 - discount/100)*origPrice - calculation = '${:.2f} discounted by {}% is ${:.2f}.'.format(origPrice, discount, newPrice) + calculation = f'${origPrice:.2f} discounted by {discount}% is ${newPrice:.2f}.' print(calculation) The 2 in the format modifier can be replaced by another integer to round to that specified number of digits. -This kind of format string depends directly on the order of the -parameters to the format method. There are other approaches that we will -skip here, explicitly numbering substitutions and taking substitutions from a dictionary. - A technical point: Since braces have special meaning in a format string, there must be a special rule if you want braces to actually be included in the final *formatted* string. The rule is to double @@ -251,40 +242,22 @@ formatted string:: a = 5 b = 9 - setStr = 'The set is {​{ {},{} }​}.'.format(a, b) + setStr = f'The set is {​{ {a},{b} }​}.' print(setStr) Unfortunately, at the time of this writing, the ActiveCode format implementation has a bug, printing doubled braces, but standard Python prints ``{5, 9}``. -You can have multiple placeholders indexing the same argument, or perhaps even have extra -arguments that are not referenced at all: - -.. activecode:: ch08_formatspecification - - letter = """ - Dear {0} {2}. - {0}, I have an interesting money-making proposition for you! - If you deposit $10 million into my bank account, I can - double your money ... - """ - - print(letter.format("Paris", "Whitney", "Hilton")) - print(letter.format("Bill", "Henry", "Gates")) - - - - .. mchoice:: test_question8_3_3 :practice: T :answer_a: Nothing - it causes an error - :answer_b: sum of {} and {} is {}; product: {}. 2 6 8 12 + :answer_b: sum of {} and {} is {}; product: {}. :answer_c: sum of 2 and 6 is 8; product: 12. :answer_d: sum of {2} and {6} is {8}; product: {12}. :correct: c - :feedback_a: It is legal format syntax: put the data in place of the braces. - :feedback_b: Put the data into the format string; not after it. - :feedback_c: Yes, correct substitutions! + :feedback_a: It is legal format syntax. + :feedback_b: Put the value of each expression in place of the braces. + :feedback_c: Yes, correct! :feedback_d: Close: REPLACE the braces. @@ -294,7 +267,7 @@ arguments that are not referenced at all: x = 2 y = 6 - print('sum of {} and {} is {}; product: {}.'.format( x, y, x+y, x*y)) + print(f'sum of {x} and {y} is {x+y}; product: {x*y}.') .. mchoice:: test_question8_3_4 @@ -313,6 +286,6 @@ arguments that are not referenced at all: .. code-block:: python v = 2.34567 - print('{:.1f} {:.2f} {:.7f}'.format(v, v, v)) + print(f'{v:.1f} {v:.2f} {v:.7f}') diff --git a/_sources/Strings/StringsareImmutable.rst b/_sources/Strings/StringsareImmutable.rst index 3e4bca44e..ad934011e 100644 --- a/_sources/Strings/StringsareImmutable.rst +++ b/_sources/Strings/StringsareImmutable.rst @@ -8,7 +8,7 @@ License". .. qnum:: - :prefix: strings-9- + :prefix: strings-8- :start: 1 .. index:: mutable, immutable diff --git a/_sources/Strings/TheSliceOperator.rst b/_sources/Strings/TheSliceOperator.rst index f8fb8082d..283f00dc3 100644 --- a/_sources/Strings/TheSliceOperator.rst +++ b/_sources/Strings/TheSliceOperator.rst @@ -32,9 +32,6 @@ selecting a character: The `slice` operator ``[n:m]`` returns the part of the string from the n'th character to the m'th character, including the first but excluding the last. In other words, start with the character at index n and go up to but do not include the character at index m. -This -behavior may seem counter-intuitive but if you recall the ``range`` function, it did not include its end -point either. If you omit the first index (before the colon), the slice starts at the beginning of the string. If you omit the second index, the slice goes to the diff --git a/_sources/Strings/toctree.rst b/_sources/Strings/toctree.rst index 86ae0ba68..b926c10ae 100644 --- a/_sources/Strings/toctree.rst +++ b/_sources/Strings/toctree.rst @@ -12,18 +12,17 @@ Strings StringMethods.rst Length.rst TheSliceOperator.rst - StringComparison.rst StringsareImmutable.rst + StringComparison.rst + Theinandnotinoperators.rst TraversalandtheforLoopByItem.rst TraversalandtheforLoopByIndex.rst TraversalandthewhileLoop.rst - Theinandnotinoperators.rst TheAccumulatorPatternwithStrings.rst - TurtlesandStringsandLSystems.rst + Characterclassification.rst Loopingandcounting.rst Afindfunction.rst Optionalparameters.rst - Characterclassification.rst Summary.rst Glossary.rst Exercises.rst diff --git a/_sources/index.rst b/_sources/index.rst index 05921b03d..bfd39a59b 100644 --- a/_sources/index.rst +++ b/_sources/index.rst @@ -54,24 +54,24 @@ Table of Contents GeneralIntro/toctree.rst SimplePythonData/toctree.rst - Debugging/toctree.rst - PythonTurtle/toctree.rst - PythonModules/toctree.rst - Functions/toctree.rst - Selection/toctree.rst - MoreAboutIteration/toctree.rst Strings/toctree.rst Lists/toctree.rst + Selection/toctree.rst + Debugging/toctree.rst + MoreAboutIteration/toctree.rst + Functions/toctree.rst + PythonModules/toctree.rst Files/toctree.rst - Dictionaries/toctree.rst - Exceptions/toctree.rst - WebApps/toctree.rst - GUIandEventDrivenProgramming/toctree.rst IntroRecursion/toctree.rst ClassesBasics/toctree.rst + Exceptions/toctree.rst ClassesDiggingDeeper/toctree.rst Inheritance/toctree.rst UnitTesting/toctree.rst + Dictionaries/toctree.rst + WebApps/toctree.rst + GUIandEventDrivenProgramming/toctree.rst + PythonTurtle/toctree.rst Labs :::: diff --git a/conf.py b/conf.py index 1d700d086..30c670ad9 100644 --- a/conf.py +++ b/conf.py @@ -51,10 +51,10 @@ generate_component_labels = False # General information about the project. -project = u'How to Think Like a Computer Scientist' -copyright = u'2014 Brad Miller, David Ranum, Created using Runestone Interactive' +project = u'Computer Science for STEM' +copyright = u'based on the book by 2014 Brad Miller, David Ranum, Created using Runestone Interactive' course_description = """Based on the original open source book by Allan Downy and Jeff Elkner. Learn Python, this edition is expanded with additional topics and is fully interactive. Try examples, answer questions, interactively, right in the book!""" -key_words = "intro google active learning fun data turtle graphics learn" +key_words = "intro google active learning fun data learn" shelf_section = "Intro to Computer Science" @@ -63,9 +63,9 @@ # built documents. # # The short X.Y version. -version = '3.0' +version = '1.0' # The full version, including alpha/beta/rc tags. -release = '3.0' +release = '1.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -130,7 +130,7 @@ #html_theme_options = {'nosidebar': 'true'} html_theme_options = { # Navigation bar title. (Default: ``project`` value) - 'navbar_title': "How To Think Like a Computer Scientist", + 'navbar_title': "Computer Science for STEM", # Tab name for entire site. (Default: "Site") 'navbar_site_name': "Chapters", @@ -177,10 +177,10 @@ # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -html_title = 'How to Think like a Computer Scientist: Interactive Edition' +html_title = 'Computer Science for STEM' # A shorter title for the navigation bar. Default is the same as html_title. -html_short_title = 'How to Think Like a Computer Scientist' +html_short_title = 'Computer Science for STEM' # The name of an image file (relative to this directory) to place at the top # of the sidebar. diff --git a/pavement.py b/pavement.py index e48cbd584..f7af9f986 100644 --- a/pavement.py +++ b/pavement.py @@ -21,7 +21,7 @@ ######## CHANGE THIS ########## -project_name = "thinkcspy" +project_name = "mines_csstem" ############################### master_url = None @@ -48,7 +48,7 @@ doctrees=doctrees, template_args={ "course_id": project_name, - "course_title": "How\\ to\\ Think\\ like\\ a\\ Computer\\ Scientist", + "course_title": "Computer\\ Science\\ for\\ STEM", "login_required": "false", "appname": master_app, "loglevel": 10, @@ -89,7 +89,7 @@ template_args = { "course_id": project_name, - "course_title": "How\\ to\\ Think\\ like\\ a\\ Computer\\ Scientist", + "course_title": "Computer\\ Science\\ for\\ STEM", "login_required": "false", "appname": master_app, "loglevel": 10, diff --git a/pretext/AdditionalTopics/Aliasingandcopying.ptx b/pretext/AdditionalTopics/Aliasingandcopying.ptx new file mode 100644 index 000000000..97a0c7c0e --- /dev/null +++ b/pretext/AdditionalTopics/Aliasingandcopying.ptx @@ -0,0 +1,78 @@ + +
+ Aliasing and Copying +

Because dictionaries are mutable, you need to be aware of aliasing (as we saw with lists). Whenever + two variables refer to the same dictionary object, changes to one affect the other. + For example, opposites is a dictionary that contains pairs + of opposites.

+ + +opposites = {'up': 'down', 'right': 'wrong', 'true': 'false'} +alias = opposites + +print(alias is opposites) + +alias['right'] = 'left' +print(opposites['right']) + + +

As you can see from the is operator, alias and opposites refer to the same object.

+

If you want to modify a dictionary and keep a copy of the original, use the dictionary + copy method. Since acopy is a copy of the dictionary, changes to it will not effect the original.

+ + +acopy = opposites.copy() +acopy['right'] = 'left' # does not change opposites + + +

+ Check your understanding +

+ + +

What is printed by the following statements?

+ + +mydict = {"cat":12, "dog":6, "elephant":23, "bear":20} +yourdict = mydict +yourdict["elephant"] = 999 +print(mydict["elephant"]) + + +
+ + + +

23

+
+ + mydict and yourdict are both names for the same dictionary. + +
+ + +

None

+
+ + The dictionary is mutable so changes can be made to the keys and values. + +
+ + +

999

+
+ + Yes, since yourdict is an alias for mydict, the value for the key elephant has been changed. + +
+ + +

Error, there are two different keys named elephant.

+
+ + There is only one dictionary with only one key named elephant. The dictionary has two different names, mydict and yourdict. + +
+
+
+
diff --git a/pretext/AdditionalTopics/BigO.ptx b/pretext/AdditionalTopics/BigO.ptx new file mode 100644 index 000000000..01735d235 --- /dev/null +++ b/pretext/AdditionalTopics/BigO.ptx @@ -0,0 +1,202 @@ + +
+ Big O Analysis + +

A common question that comes up when programming is: "How long will my program take to run?". Even if a program provides the correct output, if it takes + too long to finish then it is unacceptable. There is a problem here though, it's impossible to reliably say exactly how long a program will take to run. + It depends on too many things. The capabilities of the computer running the code, what else is running on the computer, and the size of the input are just + some of the things that would need to be considered. +

+ +

To simplify this issue, we'll give up trying to estimate exactly how long a program will run, and instead look at the biggest factor that affects + existing code: the size of the input. If we wrote a program that ran for 60 seconds on 100 megabytes of input data, how should we expect the program to + react to 200 megabytes of input data? Maybe it would run in 120 seconds (twice the data for twice the run time)? Maybe it would still run in 60 seconds, + assuming that extra data isn't used. Or maybe the program would run for far longer. The issue is that we don't know what the relationship is between the size + of the input data and the behavior of the program.

+ +

This is where Big O Analysis comes in. Big O is a notation computer scientists use to describe the relationship between the size + of the input data and the behavior of the program. These terms are written like a mathematical function using the variable n. n as a variable represents the + size of the input data provided to the program. The Big O function tells us how n affects the time the program will take to complete.

+ +

Consider the example we had before. We have a program that takes 60 seconds to run on 100 megabytes of input data, we'd like to know (roughly) + how long the program might take to run on 200 megabytes of input data. If we know the run time of the program is the function f(n) = n^2, with n being + the size of the data, now we have enough information to make a guess. If n is doubled, then the time the program runs for will quadruple! (2*n)^2 = 4 * n^2.

+ +

The formal mathematical notation for Big O is denoted with a capital O (a big o!) followed by parentheses. + Inside of the O() is most commonly some term of n. In our previous example, we would say the program has O(n^2) behavior.

+ +

Different functions of n have different magnitudes, which helps us to quantify how quick or slow an algorithm is relative to the input size n. + From left to right, left being the quickest time and right being the slowest time, we typically see these complexities:

+ +

O(1), O(logn), O(n), O(nlogn), O(n^2), O(n^3), O(2^n), O(n!).

+ +

Big O is like a limit in that only the most significant terms matter as n gets bigger and bigger. We typically expect n to be very, VERY large because + small inputs aren't as strongly affected by time limits. If a program takes 0.001 seconds to run with most normal data, is it really a big deal if it takes 0.004 + seconds on occasion? What if we were dealing with a program that had to run for a month though? Now that factor of four starts to hurt a lot more.

+ +

There is another important aspect that we have ignored up to this point: programs can often have wildly different behavior depending on their input. + Consider a contrived example:

+ + +var = input() +if 'a' in var: + while True: + print("run forever!") +else: + print("done") + + + +

In this program, the size of the input doesn't matter as much as whether the input string contains a letter "a" or not. If it does, the program runs forever. + If it doesn't, the program ends almost immediately. How do we reconcile this with our Big O notation? The answer is to be a pessimist. We adopt the assumption that + everything that can happen to slow down our program will happen. In the code above, we assume that the input ALWAYS will contain an "a". This assumption is broadly + known as the "worst case". Big O notation uses this assumption by default in every instance you will see it (at least in this class). Any other case besides "worst" will be labeled.

+ +

Let's look at some more examples:

+ + +sum = 1 + 1 +print(sum) + + +

This code has a Big O of O(1), also referred to as constant time. This is because the program does nothing with its input. In fact, it doesn't + even take input! Constant time operations are typically things in code which do not loop. A constant time program suggests it will always finish in a + consistent amount of time, no matter what happens.

+ +

Now, let's check out an example with a loop:

+ + +def example_func(n): + for i in range(n): + print(i) + + +

As you can see, this function simply prints 0 to n. Each print takes a little time, so a larger n means a longer program run time. + We denote the complexity of example_func as O(n), + because whether n = 100 or n = 10000000, as the complexity trends to infinity, it remains O(n).

+ +

In the last code example, O(n) was the complexity for all cases, because the loop always goes to n.

+ + Big O Complexity Graph + +

This figure shows complexities as a graph and which ones are considered "desirable" or at least "acceptable". Context mostly determines if these are "good" terms or not, + but do strive to never write something worse than O(n^3)!

+ +

It may be difficult to appreciate the implications of these terms when first seeing them. Let's say we have a set of algorithms with the following complexities, but they + all run with the same time (1 milliseconds) for n = 10. This table shows what will happen if we increase the size of the input:

+ + + + + + n + + + O(log(n)) + + + O(n) + + + O(n^3) + + + O(2^n) + + + + + 10 + + + 1 ms + + + 1 ms + + + 1 ms + + + 1 ms + + + + + 11 + + + 1 ms + + + 1.1 ms + + + ~1.3 ms + + + 2 ms + + + + + 20 + + + 1.3 ms + + + 2 ms + + + 8 ms + + + 1 s + + + + + 100 + + + 2 ms + + + 10 ms + + + 1 s + + + 10^16 years + + + + + 100000 + + + 5 ms + + + 10 s + + + 31 years + + + :) + + + +
+ +

As you can see, what started off as a negligible difference exploded into a totally unacceptable time for larger input sizes applied to larger Big O terms. Examples like these are precisely why + computer scientists are so fixated on Big O. 100000 data points is not a lot of data. Large tech companies are often running code on billions or + trillions of data points, and anything less the most efficient code won't be able to run at-scale.

+ +

We will end this section with a disclaimer. We have only covered the bare basic concepts of Big O here today. If you continue to study computer science, + you'll have more opportunities to explore it in much more detail, including seeing the formal definition of Big O as well as learning how to determine the Big O of your own code. + For this specific class, we only ask you to be familiar with the notation of Big O and have a basic intuition behind what it communicates.

+
diff --git a/pretext/AdditionalTopics/BinaryRepresentations.ptx b/pretext/AdditionalTopics/BinaryRepresentations.ptx new file mode 100644 index 000000000..12c83b6c7 --- /dev/null +++ b/pretext/AdditionalTopics/BinaryRepresentations.ptx @@ -0,0 +1,55 @@ + +
+ Binary Representations + +

Have you ever seen all of the "hacker" 01010110101010s in the movies? As you might + know, this is called binary. While it's not actually how hacking works, binary is still the base of all computing. + Every word that you are reading right now was transmitted to your computer as a series of 1's and 0's. Although you won't + be typing 0's and 1's at a keyboard all day, binary is still useful to know.

+ +

Quick background: binary is a numbering system, just like decimal (the numbering system we normally use). + Decimal uses the digits 0-9, but binary only uses the digits 0 and 1, which are called bits. + In other words, binary is just a different way of counting.

+ +

Believe it or not, this is indirectly how you've been counting your entire life. For instance, in decimal numbering (base 10):

+ +

1023 (base 10) = (1 * 10^3) + (0 * 10^2) + (2 * 10^1) + (3 * 10^0)

+ +

There are even more numbering systems, like hexadecimal and octal, but you only need to understand binary for this course.

+ +

Binary deals with powers of two (hence the name), reading from right to left and starting at 0. + If the bit is 0, it is "off" and the position is multiplied by 0; if the bit is 1, it is "on" and its + position in the number is the exponent with 2 as the base. Binary numbering is also called base 2 + because of that. For instance:

+ +

1000 (base 2) = (1 * 2^3) + (0 * 2^2) + (0 * 2^1) + (0 * 2^0) = 8

+ +

Converting decimal to binary: A quick way to convert decimal to binary is to find the largest + factor of 2 that will go into the number, and concatenate 1 if it goes into the number; concatenate 0 if not. Subtract + the number from the running total and repeat until we hit 0. For instance:

+ +

Example: Convert 78 to binary

+

1. If we think about all of our powers of 2, 2^7 = 128 is too large (128 > 78), so we know 2^6 is where we'll start our number, and we need a 1 in that position. We now have: 1xxxxxx.

+

2. 78 - 64 = 14, which is our remainder from the last digit. 2^5 = 32 > 14, so we know 2^5 is a 0. We now have: 10xxxxx.

+

3. 78 - 64 = 14, which is our remainder from the last digit. 2^4 = 16 > 14, so we know 2^4 is a 0. We now have: 100xxxx.

+

4. 78 - 64 = 14, which is our remainder from the last digit. 2^3 = 8 < 14, so we know 2^3 is a 1 because it fits in! We now have: 1001xxx.

+

5. 78 - 64 - 8 = 6, which is our remainder from the last digit. 2^2 = 4 < 6, so we know 2^2 is a 1 because it fits in! We now have: 10011xx.

+

6. 78 - 64 - 8 - 4 = 2, which is our remainder from the last digit. 2^1 = 2 < 4, so we know 2^1 is a 1 because it fits in! We now have: 100111x.

+

7. 78 - 64 - 8 - 4 - 2 = 0, so we are done and can fill any remainders with a 0 bit.

+

Our final answer is: 1001110 (base 2)

+ + +

+ Typically when we write binary, we'll see our bits in groups of 4, because our binary sequences are normally + some multiple of 4, like 8, 16, or 32. Because of this, we would add a leading zero and + write our previous answer as: 0100 1110 (base 2). +

+
+ +

Converting binary to decimal: As mentioned above, you can simply look at each bit, + and add 2 to the power of its position if the bit is 1.

+ +

Like usual in math, there are a few different ways to arrive at one conclusion. These are not + the only ways to do conversions. If these explanations don't make sense to you, ask your instructor + or Google for their explanation.

+
diff --git a/pretext/AdditionalTopics/DataScience.ptx b/pretext/AdditionalTopics/DataScience.ptx new file mode 100644 index 000000000..79a7e19cb --- /dev/null +++ b/pretext/AdditionalTopics/DataScience.ptx @@ -0,0 +1,53 @@ + +
+ Data Science + +

Data science is a multidisciplinary field which combines computer science, + math, and other domains to answer questions using data.

+ +

As the world moves more and more towards storing and analyzing large amounts of data, + data science is a vital skill for you to be familiar with, whether you're a computer science major or not. It is also + a very common and useful application of programming, which is why we're discussing it in this class.

+ +

Data science is perhaps best defined by describing what data science looks like. The data science process consists of four steps:

+ +
    +
  1. Obtaining data
  2. +
  3. Cleaning the data
  4. +
  5. Exploring the data
  6. +
  7. Predicting unknowns
  8. +
+ +

Obtaining the data: We live in a time where data is more abundant then ever before. Getting a hold of data can involve gathering it yourself, + purchasing it, or taking advantage of the many, many sites online now which have a plethora of data + available for free (and sometimes paid) use. If you are getting your data from some 3rd party, it will likely come in a .csv, .json, or SQL database format.

+ +

Cleaning the data: This can vary, but ultimately you need to prepare your data + in a way that makes it easily usable in the next steps. Often data starts out "noisy" or contains errors. In this step you may + fix things in the data, change missing data, or correct wrong data.

+ +

Cleaning is regularly considered the longest step in this process! Data can come in all sorts of different + formats now, with anomalies, with blanks, and so much more. It often depends on context and you own goals + what "fixing" data even means.

+ +

Exploring the data: Now that the data is prepared, we can do some analysis on it! As the term suggests, exploring the data is about coming to better + understand it. You often don't know what is interesting or useful about data when you first encounter it. You may need to do some sort of statistical + analysis to uncover the interesting aspects, or you may want to graph values and look for relationships and trends visually.

+ +

Predicting unknowns: Having come to understand the data better, you can now use it to create new knowledge. These days, this step typically involves + using machine learning models. These techniques can generally be split into three groups:

+ +
    +
  1. Supervised Learning: With supervised learning, we try to construct a model that describes the relationship between inputs and outputs (regularly + referred to as "labels"). Knowing what labels we want in advance is what makes a method "supervised". For example, we could create a model to guess when an email + is spam or not based on its contents; the label here is "spam" or "not spam". Or we could try to guess what the stock price will be for our favorite company based + on how it has performed in the last few weeks. The label here would be the predicted stock price.
  2. +
  3. Unsupervised Learning: Contrasting with supervised learning, with unsupervised learning we don't know the labels in advance. An example here could be + using social media data to automatically identify friend groups. We don't know in advance how many groups we'll find or what their nature will be. Because of this, it + can be harder to guess what kind of results unsupervised learning will produce.
  4. +
  5. Semi-Supervised Learning: Semi-supervised learning is an attempt to capture the best aspects of both supervised and unsupervised learning. With these + approaches we start with some data that has labels and also some data that doesn't. To use a previous example, we could take a collection of emails, only some of + which have been labeled as spam or not, and still try to construct a reliable method for identifying new emails as spam. If it goes well, then we've saved ourselves + a lot of time that would have otherwise been spent labeling emails.
  6. +
+
diff --git a/pretext/AdditionalTopics/DictionaryComprehensions.ptx b/pretext/AdditionalTopics/DictionaryComprehensions.ptx new file mode 100644 index 000000000..21d295a30 --- /dev/null +++ b/pretext/AdditionalTopics/DictionaryComprehensions.ptx @@ -0,0 +1,91 @@ + +
+ Dictionary Comprehensions +

Like lists, dictionaries also support comprehensions as an alternative to dictionary generation with lengthier loops. The format for a dictionary comprehension is as follows

+
{<key-expression>:<value-expression> for <item> in <sequence> if <condition>}
+

Make careful note of the use of curly brackets instead of square brackets. These brackets are primarily how Python determines what kind of comprehension + you are making. Like list comprehensions, the if clause is optional.

+

Let's view an example,

+ + +first_names = ["Radia", "Grace", "Katherine", "Jeannette"] +last_names = ["Perlman", "Hopper", "Johnson", "Wing"] + +first_to_last = {first_names[i]:last_names[i] for i in range(len(first_names))} + +print(first_to_last) + + +

This code takes two separate lists and associates their values in a new dictionary. More specifically, first names are mapped to last names.

+

Another more complicated example:

+ + +def percentage(n): + """ Return a percentage value from a provided raw score, rounded to two decimal places. """ + + return round((n / 1000) * 100, 2) + +names = ["Sansa", "Jamie", "Cersei", "Jon", "Arya"] +percentage_scores = {name:percentage(int(input(f"Please enter a score for {name}"))) for name in names} +print(percentage_scores) + + + +

This workspace is provided for your convenience. You can use this activecode window to try out anything you like.

+ + + + + + +
+

+ Check your understanding +

+ + +

What is printed by the following statements?

+ + +alist = [4,2,8,6,5] +dic = {num:num**2 for num in alist if num < 6} +print(dic) + + +
+ + + +

[4,2,5]

+
+ + This is the list of keys that will be generated, but it is missing the associated values. + +
+ + +

{4:16,2:4,8:64,6:36,5:25}

+
+ + This is nearly correct, but has too many values. Look at the if clause. + +
+ + +

{4:16,2:4,5:25}

+
+ + Yes, this is correct. + +
+ + +

{4:8,2:4,5:10}

+
+ + These are the correct keys, but pay close attention to the value expression in the code. + +
+
+
+
diff --git a/pretext/AdditionalTopics/Dictionarymethods.ptx b/pretext/AdditionalTopics/Dictionarymethods.ptx new file mode 100644 index 000000000..c1455c0bf --- /dev/null +++ b/pretext/AdditionalTopics/Dictionarymethods.ptx @@ -0,0 +1,365 @@ + +
+ Dictionary Methods +

Dictionaries have a number of useful built-in methods. + The following table provides a summary and more details can be found in the + Python Documentation.

+ + + + + Method + + + Parameters + + + Description + + + + + keys + + + none + + + Returns a view of the keys in the dictionary + + + + + values + + + none + + + Returns a view of the values in the dictionary + + + + + items + + + none + + + Returns a view of the key-value pairs in the dictionary + + + + + get + + + key + + + Returns the value associated with key; None otherwise + + + + + get + + + key,alt + + + Returns the value associated with key; alt otherwise + + + +
+

The keys method returns what Python 3 calls a view of its underlying keys. + We can iterate over the view or turn the view into a + list by using the list conversion function.

+ + +inventory = {'apples': 430, 'bananas': 312, 'oranges': 525, 'pears': 217} + +for akey in inventory.keys(): # the order in which we get the keys is not defined + print("Got key", akey, "which maps to value", inventory[akey]) + +ks = list(inventory.keys()) +print(ks) + + +

It is so common to iterate over the keys in a dictionary that you can + omit the keys method call in the for loop — iterating over + a dictionary implicitly iterates over its keys.

+ + +inventory = {'apples': 430, 'bananas': 312, 'oranges': 525, 'pears': 217} + +for k in inventory: + print("Got key", k) + + +

As we saw earlier with strings and lists, dictionary methods use dot notation, + which specifies the name of the method to the right of the dot and the name of + the object on which to apply the method immediately to the left of the dot. The empty + parentheses in the case of keys indicate that this method takes no parameters.

+

The values and items methods are similar to keys. They return view objects which can be turned + into lists or iterated over directly. Note that the items are shown as tuples containing the key and the associated value.

+ + +inventory = {'apples': 430, 'bananas': 312, 'oranges': 525, 'pears': 217} + +print(list(inventory.values())) +print(list(inventory.items())) + +for (k,v) in inventory.items(): + print("Got", k, "that maps to", v) + +for k in inventory: + print("Got", k, "that maps to", inventory[k]) + + +

Note that tuples are often useful for getting both the key and the value at the same + time while you are looping. The two loops do the same thing.

+

The in and not in operators can test if a key is in the dictionary:

+ + +inventory = {'apples': 430, 'bananas': 312, 'oranges': 525, 'pears': 217} +print('apples' in inventory) +print('cherries' in inventory) + +if 'bananas' in inventory: + print(inventory['bananas']) +else: + print("We have no bananas") + + +

This operator can be very useful since looking up a non-existent key in a + dictionary causes a runtime error.

+

The get method allows us to access the value associated with a key, similar to the [ ] operator. + The important difference is that get will not cause a runtime error if the key is not present. It + will instead return None. There exists a variation of get that allows a second parameter that serves as an alternative return value + in the case where the key is not present. This can be seen in the final example below. In this case, since cherries is not a key, return 0 (instead of None).

+ + +inventory = {'apples': 430, 'bananas': 312, 'oranges': 525, 'pears': 217} + +print(inventory.get("apples")) +print(inventory.get("cherries")) + +print(inventory.get("cherries", 0)) + + + +

This workspace is provided for your convenience. You can use this activecode window to try out anything you like.

+ + + + + + +
+

+ Check your understanding +

+ + +

What is printed by the following statements?

+ + +mydict = {"cat":12, "dog":6, "elephant":23, "bear":20} +keylist = list(mydict.keys()) +keylist.sort() +print(keylist[3]) + + +
+ + + +

cat

+
+ + keylist is a list of all the keys which is then sorted. cat would be at index 1. + +
+ + +

dog

+
+ + keylist is a list of all the keys which is then sorted. dog would be at index 2. + +
+ + +

elephant

+
+ + Yes, the list of keys is sorted and the item at index 3 is printed. + +
+ + +

bear

+
+ + keylist is a list of all the keys which is then sorted. bear would be at index 0. + +
+
+
+ + +

What is printed by the following statements?

+ + +mydict = {"cat":12, "dog":6, "elephant":23, "bear":20} +answer = mydict.get("cat") // mydict.get("dog") +print(answer) + + +
+ + + +

2

+
+ + get returns the value associated with a given key so this divides 12 by 6. + +
+ + +

0.5

+
+ + 12 is divided by 6, not the other way around. + +
+ + +

bear

+
+ + Take another look at the example for get above. get returns the value associated with a given key. + +
+ + +

Error, divide is not a valid operation on dictionaries.

+
+ + The integer division operator is being used on the values returned from the get method, not on the dictionary. + +
+
+
+ + +

What is printed by the following statements?

+ + +mydict = {"cat":12, "dog":6, "elephant":23, "bear":20} +print("dog" in mydict) + + +
+ + + +

True

+
+ + Yes, dog is a key in the dictionary. + +
+ + +

False

+
+ + The in operator returns True if a key is in the dictionary, False otherwise. + +
+
+
+ + +

What is printed by the following statements?

+ + +mydict = {"cat":12, "dog":6, "elephant":23, "bear":20} +print(23 in mydict) + + +
+ + + +

True

+
+ + 23 is a value in the dictionary, not a key. + +
+ + +

False

+
+ + Yes, the in operator returns True if a key is in the dictionary, False otherwise. + +
+
+
+ + +

What is printed by the following statements?

+ + +total = 0 +mydict = {"cat":12, "dog":6, "elephant":23, "bear":20} +for akey in mydict: + if len(akey) > 3: + total = total + mydict[akey] +print(total) + + +
+ + + +

18

+
+ + Add the values that have keys greater than 3, not equal to 3. + +
+ + +

43

+
+ + Yes, the for statement iterates over the keys. It adds the values of the keys that have length greater than 3. + +
+ + +

0

+
+ + This is the accumulator pattern. total starts at 0 but then changes as the iteration proceeds. + +
+ + +

61

+
+ + Not all the values are added together. The if statement only chooses some of them. + +
+
+
+
diff --git a/pretext/AdditionalTopics/Dictionaryoperations.ptx b/pretext/AdditionalTopics/Dictionaryoperations.ptx new file mode 100644 index 000000000..c0c5ca2db --- /dev/null +++ b/pretext/AdditionalTopics/Dictionaryoperations.ptx @@ -0,0 +1,88 @@ + +
+ Dictionary Operations +

The del statement removes a key-value pair from a dictionary. For example, + the following dictionary contains the names of various fruits and the number of + each fruit in stock. If someone buys all of the pears, we can remove the entry from the dictionary.

+ + + inventory = {'apples': 430, 'bananas': 312, 'oranges': 525, 'pears': 217} + + del inventory['pears'] + + + +

Dictionaries are also mutable. As we've seen before with lists, this means that the dictionary can + be modified by referencing an association on the left hand side of the assignment statement. In the previous + example, instead of deleting the entry for pears, we could have set the inventory to 0. +

+ + + inventory = {'apples': 430, 'bananas': 312, 'oranges': 525, 'pears': 217} + + inventory['pears'] = 0 + + +

Similarily, + a new shipment of 200 bananas arriving could be handled like this.

+ + + inventory = {'apples': 430, 'bananas': 312, 'oranges': 525, 'pears': 217} + inventory['bananas'] = inventory['bananas'] + 200 + + + numItems = len(inventory) + + +

Notice that there are now 512 bananas—the dictionary has been modified. Note also that the len function also works on dictionaries. It returns the number + of key-value pairs:

+

+ Check your understanding +

+ + +

What is printed by the following statements?

+ + +mydict = {"cat":12, "dog":6, "elephant":23} +mydict["mouse"] = mydict["cat"] + mydict["dog"] +print(mydict["mouse"]) + + +
+ + + +

12

+
+ + 12 is associated with the key cat. + +
+ + +

0

+
+ + The key mouse will be associated with the sum of the two values. + +
+ + +

18

+
+ + Yes, add the value for cat and the value for dog (12 + 6) and create a new entry for mouse. + +
+ + +

Error, there is no entry with mouse as the key.

+
+ + Since the new key is introduced on the left hand side of the assignment statement, a new key-value pair is added to the dictionary. + +
+
+
+
diff --git a/pretext/AdditionalTopics/Exercises.ptx b/pretext/AdditionalTopics/Exercises.ptx new file mode 100644 index 000000000..4806889e7 --- /dev/null +++ b/pretext/AdditionalTopics/Exercises.ptx @@ -0,0 +1,532 @@ + + + Exercises + + +

Write a program that allows the user to enter a string. It then prints a + table of the letters of the alphabet in alphabetical order which occur in + the string together with the number of times each letter occurs. Case should + be ignored. A sample run of the program might look this this:

+
Please enter a sentence: ThiS is String with Upper and lower case Letters.
+a  2
+c  1
+d  1
+e  5
+g  1
+h  2
+i  4
+l  2
+n  2
+o  1
+p  2
+r  4
+s  5
+t  5
+u  1
+w  2
+$
+
+ + + + + + + + +x = input("Enter a sentence") + +x = x.lower() # convert to all lowercase + +alphabet = 'abcdefghijklmnopqrstuvwxyz' + +letter_count = {} # empty dictionary +for char in x: + if char in alphabet: # ignore any punctuation, numbers, etc + if char in letter_count: + letter_count[char] = letter_count[char] + 1 + else: + letter_count[char] = 1 + +keys = letter_count.keys() +for char in sorted(keys): + print(char, letter_count[char]) + + + +
+
+

Give the Python interpreter's response to each of the following from a + continuous interpreter session:

+

+

    +
  1. + + +>>> d = {'apples': 15, 'bananas': 35, 'grapes': 12} +>>> d['banana'] + + +
  2. +
  3. + + +>>> d['oranges'] = 20 +>>> len(d) + + +
  4. +
  5. + + +>>> 'grapes' in d + + +
  6. +
  7. + + +>>> d['pears'] + + +
  8. +
  9. + + +>>> d.get('pears', 0) + + +
  10. +
  11. + + +>>> fruits = d.keys() +>>> fruits.sort() +>>> print(fruits) + + +
  12. +
  13. + + +>>> del d['apples'] +>>> 'apples' in d + + +
  14. +
+

+
+

Be sure you understand why you get each result. Then apply what you + have learned to fill in the body of the function below, and add code for + the tests indicated:

+ + +def add_fruit(inventory, fruit, quantity=0): + pass + +# make these tests work... +new_inventory = {} +add_fruit(new_inventory, 'strawberries', 10) +# test that 'strawberries' in new_inventory +# test that new_inventory['strawberries'] is 10 +add_fruit(new_inventory, 'strawberries', 25) +# test that new_inventory['strawberries'] is now 35) + + + + +

Write a program called alice_words.py that creates a text file named + alice_words.txt containing an alphabetical listing of all the words, and the + number of times each occurs, in the text version of Alice's Adventures in Wonderland. + (You can obtain a free plain text version of the book, along with many others, from + http://www.gutenberg.org.) The first 10 lines of your output file should look + something like this

+
+ + + + + Word + + + Count + + + + + a + + + 631 + + + + + a-piece + + + 1 + + + + + abide + + + 1 + + + + + able + + + 1 + + + + + about + + + 94 + + + + + above + + + 3 + + + + + absence + + + 1 + + + + + absurd + + + 2 + + + +
+
+

How many times does the word, alice, occur in the book? If you are writing this + in the activecode window simply print out the results rather than write them to a file.

+
+ + + + + + + f = open('alice.txt', 'r') + +count = {} + +for line in f: + for word in line.split(): + + # remove punctuation + word = word.replace('_', '').replace('"', '').replace(',', '').replace('.', '') + word = word.replace('-', '').replace('?', '').replace('!', '').replace("'", "") + word = word.replace('(', '').replace(')', '').replace(':', '').replace('[', '') + word = word.replace(']', '').replace(';', '') + + # ignore case + word = word.lower() + + # ignore numbers + if word.isalpha(): + if word in count: + count[word] = count[word] + 1 + else: + count[word] = 1 + +keys = count.keys() +keys.sort() + +# save the word count analysis to a file +out = open('alice_words.txt', 'w') + +for word in keys: + out.write(word + " " + str(count[word])) + out.write('\n') + +print("The word 'alice' appears " + str(count['alice']) + " times in the book.") + + +f = open('alice.txt', 'r') + +count = {} + +for line in f: + for word in line.split(): + + # remove punctuation + word = word.replace('_', '').replace('"', '').replace(',', '').replace('.', '') + word = word.replace('-', '').replace('?', '').replace('!', '').replace("'", "") + word = word.replace('(', '').replace(')', '').replace(':', '').replace('[', '') + word = word.replace(']', '').replace(';', '') + + # ignore case + word = word.lower() + + # ignore numbers + if word.isalpha(): + if word in count: + count[word] = count[word] + 1 + else: + count[word] = 1 + +keys = count.keys() +keys.sort() + +# save the word count analysis to a file +out = open('alice_words.txt', 'w') + +for word in keys: + out.write(word + " " + str(count[word])) + out.write('\n') + +print("The word 'alice' appears " + str(count['alice']) + " times in the book.") + + +
+ + +

What is the longest word in Alice in Wonderland? How many characters does it have?

+
+ + + + + +
+ + +

Here's a table of English to Pirate translations

+ + + + + English + + + Pirate + + + + + sir + + + matey + + + + + hotel + + + fleabag inn + + + + + student + + + swabbie + + + + + boy + + + matey + + + + + madam + + + proud beauty + + + + + professor + + + foul blaggart + + + + + restaurant + + + galley + + + + + your + + + yer + + + + + excuse + + + arr + + + + + students + + + swabbies + + + + + are + + + be + + + + + lawyer + + + foul blaggart + + + + + the + + + th' + + + + + restroom + + + head + + + + + my + + + me + + + + + hello + + + avast + + + + + is + + + be + + + + + man + + + matey + + + +
+

Write a function named translator that takes a parameter containing a sentence in English + (no punctuation and all words in lowercase) and returns that sentence translated to Pirate.

+

For example, the sentence hello there students should be translated to avast there swabbies.

+
+ + +def translator(english): + + pirate = {} + pirate['sir'] = 'matey' + pirate['hotel'] = 'fleabag inn' + pirate['student'] = 'swabbie' + pirate['boy'] = 'matey' + pirate['restaurant'] = 'galley' + pirate['hello'] = 'avast' + pirate['students'] = 'swabbies' + + # Complete the function + +==== +from unittest.gui import TestCaseGui + +class myTests(TestCaseGui): + + def testOne(self): + self.assertEqual(translator("hello there students"),'avast there swabbies','translator("hello there students") yields "avast there swabbies"') + self.assertEqual(translator("the boy stayed in the hotel"),'the matey stayed in the fleabag inn','translator("the boy stayed in the hotel") yields "the matey stayed in the fleabag inn"') + +myTests().main() + + + + + +def translator(sentence): + + pirate = {} + pirate['sir'] = 'matey' + pirate['hotel'] = 'fleabag inn' + pirate['student'] = 'swabbie' + pirate['boy'] = 'matey' + pirate['restaurant'] = 'galley' + pirate['hello'] = 'avast' + pirate['students'] = 'swabbies' + + psentence = [] + words = sentence.split() + for aword in words: + if aword in pirate: + psentence.append(pirate[aword]) + else: + psentence.append(aword) + + return " ".join(psentence) + + + +
+
diff --git a/pretext/AdditionalTopics/Glossary.ptx b/pretext/AdditionalTopics/Glossary.ptx new file mode 100644 index 000000000..a0e7daa8a --- /dev/null +++ b/pretext/AdditionalTopics/Glossary.ptx @@ -0,0 +1,50 @@ + +
+ Glossary + + + Binary search +

A searching algorithm where you look through a sorted list in halves; an improvement upon linear search.

+
+ + Big O Notation +

A notation computer scientists use to describe the relationship between the size + of the input data and the behavior of the program, denoted with O() and some factor inside of the parenthesis.

+
+ + constant time +

A Big O time complexity of O(1).

+
+ + comprehension +

A specific Python construction that allows collection types to be created and filled with a single line of code.

+
+ + dictionary +

A collection of key-value pairs that maps from keys to values. The keys + can be any immutable type, and the values can be any type.

+
+ + key +

A data item that is mapped to a value in a dictionary. Keys are used + to look up values in a dictionary.

+
+ + key-value pair +

One of the pairs of items in a dictionary. Values are looked up in a + dictionary by key.

+
+ + Linear search +

A searching algorithm where you look through in a linear order (directly from start to end).

+
+ + mapping type +

A mapping type is a data type comprised of a collection of keys and + associated values. Python's only built-in mapping type is the + dictionary. Dictionaries implement the + associative array + abstract data type.

+
+
+
diff --git a/pretext/Lists/ListComprehensions.ptx b/pretext/AdditionalTopics/ListComprehensions.ptx similarity index 66% rename from pretext/Lists/ListComprehensions.ptx rename to pretext/AdditionalTopics/ListComprehensions.ptx index f2ac50d45..b5a4eaf74 100644 --- a/pretext/Lists/ListComprehensions.ptx +++ b/pretext/AdditionalTopics/ListComprehensions.ptx @@ -1,10 +1,16 @@ -
+
List Comprehensions -

The previous example creates a list from a sequence of values based on some selection criteria. An easy way to do this type of processing in Python is to use a list comprehension. List comprehensions are concise ways to create lists. The general syntax is:

-
[<expression> for <item> in <sequence> if  <condition>]
+

Generating or modifying a list of values based on some consistent rule is a very common operation done in Python. Take, for example, + generating a list of values according to a math function, or performing the same string operation on a list of words. This is common + enough that Python has built-in syntax to construct these smaller, collection-producing loops succinctly. These are known as comprehensions, + and can be done with several different collection types.

+ +

A comprehension that generates a list is, naturally, known as a list comprehension. + List comprehensions are concise ways to create lists. The general syntax is:

+
[<expression> for <item> in <sequence> if <condition>]

where the if clause is optional. For example,

- + mylist = [1,2,3,4,5] @@ -14,7 +20,7 @@ print(yourlist)

The expression describes each element of the list that is being built. The for clause iterates through each item in a sequence. The items are filtered by the if clause if there is one. In the example above, the for statement lets item take on all the values in the list mylist. Each item is then squared before it is added to the list that is being built. The result is a list of squares of the values in mylist.

-

To write the primes_upto function we will use the is_prime function to filter the sequence of integers coming from the range function. In other words, for every integer from 2 up to but not including n, if the integer is prime, keep it in the list.

+

To write a primes_upto function we can use the previously-made is_prime function to filter the sequence of integers coming from a range function. In other words, for every integer from 2 up to but not including n, if the integer is prime, keep it in the list.

def primes_upto(n): @@ -26,7 +32,7 @@ def primes_upto(n):

This workspace is provided for your convenience. You can use this activecode window to try out anything you like.

- + @@ -36,7 +42,7 @@ def primes_upto(n):

Check your understanding

- +

What is printed by the following statements?

diff --git a/pretext/AdditionalTopics/More-BigO.ptx b/pretext/AdditionalTopics/More-BigO.ptx new file mode 100644 index 000000000..e8b108fb7 --- /dev/null +++ b/pretext/AdditionalTopics/More-BigO.ptx @@ -0,0 +1,161 @@ + +
+ Big O Simplification and Practice + +

One more important topic in Big O Analysis is simplification. We won't get too deep into the math, but here are a few general rules:

+ +

Constant time operations always simplify to O(1). For instance, O(3) and O(1000000) both simplify to O(1).

+ +

Added constants are ignored when a larger factor is around. For instance, O(n + 1) is just O(n).

+ +

Multiplication by a constant factor simplifies to the factor, such as O(3n) becoming O(n).

+ +

Added polynomials are ignored when a larger factor is around. For instance, O(n! + n) is just O(n!), or O(n^2 + n) is just O(n^2).

+ +

With nested loops, any inner loop factors are multiplied by the outer loop factor.

+ + + +

Example 1: Find the time complexity of this program. Be careful of the nested loop.

+ + + def example_func(n): + for i in range(n): + for j in range(n): + print(i * j) + + +
+ + + +

O(1)

+
+ + Incorrect; we have two nested loops here. + +
+ + +

O(logn)

+
+ + Not quite. You typically see O(logn) more often in search/sort algorithms. + +
+ + +

O(n)

+
+ + Incorrect; both loops (one of which is nested) depend on n. + +
+ + +

O(n^2)

+
+ + Correct! Both loops depend on n, and one loop is nested, which multiplies it by the parent factor. + +
+
+
+ + + +

Example 2: Find the time complexity of this program.

+ + +def example_func(n): + for i in range(n): + for j in range(10000): + print(j) + + +
+ + + +

O(1)

+
+ + Not quite; we one loop going to n and one loop going to a constant time. + +
+ + +

O(logn)

+
+ + Not quite. You typically see O(logn) more often in search/sort algorithms. + +
+ + +

O(n)

+
+ + Correct! O(10000n) simplifies to O(n). + +
+ + +

O(n^2)

+
+ + Incorrect. We only have one loop going to n, the inner loop is going to a constant. + +
+
+
+ + + +

Example 3: Find the time complexity of this program. Apply the same rules that you do for a for loop.

+ + +n = input() +i = 0 +while i < n: + print(i) + i += 1 + + +
+ + + +

O(1)

+
+ + Not quite, since n is an arbitrary user inputted value. + +
+ + +

O(logn)

+
+ + Not quite. You typically see O(logn) more often in search/sort algorithms. + +
+ + +

O(n)

+
+ + Correct! We're going to n in this loop. + +
+ + +

O(n^2)

+
+ + Incorrect. We only have one loop going to n. + +
+
+
+
diff --git a/pretext/AdditionalTopics/SearchSortAlgorithms.ptx b/pretext/AdditionalTopics/SearchSortAlgorithms.ptx new file mode 100644 index 000000000..6b1f8f10b --- /dev/null +++ b/pretext/AdditionalTopics/SearchSortAlgorithms.ptx @@ -0,0 +1,50 @@ + +
+ Search and Sort Algorithms + +

A major part of computer science is studying algorithms. There are two types of algorithms + that are spoken about very often: search and sort. Not only are they great + for learning about Big O and algorithms, but they're very applicable in the real world.

+ +

As the internet grows and the data we store grows, companies have to wrestle with the large amounts of data + they have. Imagine if you're Google and you're looking for one user out of one billion. + Now imagine you need to do that a thousand times a minute, with people doing it at the same time. + The code you write and the algorithms you use become massively important to how quickly + these operations can be done.

+ +

Linear search is the most basic searching algorithm, and the most intuitive. + Quite simply, you look through whatever you're considering in a linear order; from first to last. + This could be a stack of papers, or it could be a list in Python.

+ + + +# a program which finds 10 in my list, linearly +my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +for element in my_list: + if element == 10: + print('found 10!') + break + + + +

Linear search has a best case complexity of O(1), average case of O(n), and worst case of O(n). + In the best case, the element we're looking for is the first element. But in the + average and worst cases, we will have to look through some n elements.

+ +

Binary search is an improvement upon linear search. Binary search splits the searched list in half and looks at the middle value. + If the number we're searching for is less than that value, we know it must be in the left half of the list; + if it's greater, we know it must be in the right half of the list, and we can repeat this process on smaller portions of the list until we find the number.

+ +

Binary search has a best case complexity of O(1), average case of O(logn), and worst case of O(logn).

+ +

However, there's a catch: for binary search to work, the list must be sorted! This is where sorting algorithms can come in. Sorting will not be covered in + this class, but we will describe one sorting algorithm for those that are curious.

+ +

Selection sort is a simple sorting algorithm. The idea is that we take the smallest element and swap it with the leftmost element, + and then the leftmost element becomes the sorted list. We repeat this process, and the sorted portion of the list grows in size each time, so we're only + adding elements from the nonsorted portion of the list to the end of the sorted part of the list.

+ +

The time complexity of selection sort is O(n^2) in every case.

+ +

Some other common sorting algorithms include Bubble Sort and Insertion Sort, but we will leave those for another class.

+
diff --git a/pretext/AdditionalTopics/intro-Dictionaries.ptx b/pretext/AdditionalTopics/intro-Dictionaries.ptx new file mode 100644 index 000000000..03a7c5e68 --- /dev/null +++ b/pretext/AdditionalTopics/intro-Dictionaries.ptx @@ -0,0 +1,139 @@ + +
+ Dictionaries +

All of the compound data types we have studied in detail so far — strings, + lists, and tuples — are sequential collections. This means that the items in the collection are + ordered from left to right and they use integers as indices to access + the values they contain.

+

+ Dictionaries are a different kind of collection. They are Python's + built-in mapping type. A map is an unordered, associative collection. The association, or mapping, + is from a key, which can be any immutable type, + to a value, which can be any Python data object.

+

As an example, we will create a dictionary to translate English words into + Spanish. For this dictionary, the keys are strings and the values will also be strings.

+

One way to create a dictionary is to start with the empty dictionary and add + key-value pairs. The empty dictionary is denoted {} +

+ + +eng2sp = {} +eng2sp['one'] = 'uno' +eng2sp['two'] = 'dos' +eng2sp['three'] = 'tres' + + +

The first assignment creates an empty dictionary named eng2sp. The other + assignments add new key-value pairs to the dictionary. The left hand side gives the dictionary and the key being associated. The right hand side gives the value being associated with that key. + We can print the current + value of the dictionary in the usual way. + The key-value pairs of the dictionary are separated by commas. Each pair + contains a key and a value separated by a colon.

+

The order of the pairs may not be what you expected. Python uses complex + algorithms, designed for very fast access, to determine where the + key-value pairs are stored in a dictionary. + For our purposes we can think of this ordering as unpredictable.

+

Another way to create a dictionary is to provide a list of key-value pairs + using the same syntax as the previous output.

+ + + eng2sp = {'three': 'tres', 'one': 'uno', 'two': 'dos'} + print(eng2sp) + + + +

It doesn't matter what order we write the pairs. The values in a dictionary are + accessed with keys, not with indices, so there is no need to care about + ordering.

+

Here is how we use a key to look up the corresponding value.

+ + +eng2sp = {'three': 'tres', 'one': 'uno', 'two': 'dos'} + +value = eng2sp['two'] +print(value) + + +

The key 'two' yields the value 'dos'. +

+ +

This workspace is provided for your convenience. You can use this activecode window to try out anything you like.

+ + + + + + +
+

+ Check your understanding +

+ + +

A dictionary is an unordered collection of key-value pairs.

+
+ + + +

False

+
+ + Dictionaries associate keys with values but there is no assumed order for the entries. + +
+ + +

True

+
+ + Yes, dictionaries are associative collections meaning that they store key-value pairs. + +
+
+
+ + +

What is printed by the following statements?

+ + +mydict = {"cat":12, "dog":6, "elephant":23} +print(mydict["dog"]) + + +
+ + + +

12

+
+ + 12 is associated with the key cat. + +
+ + +

6

+
+ + Yes, 6 is associated with the key dog. + +
+ + +

23

+
+ + 23 is associated with the key elephant. + +
+ + +

Error, you cannot use the index operator with a dictionary.

+
+ + The [ ] operator, when used with a dictionary, will look up a value based on its key. + +
+
+
+
diff --git a/pretext/AdditionalTopics/pygame/Introduction.ptx b/pretext/AdditionalTopics/pygame/Introduction.ptx new file mode 100644 index 000000000..ebb158449 --- /dev/null +++ b/pretext/AdditionalTopics/pygame/Introduction.ptx @@ -0,0 +1,105 @@ + +
+ Pygame Introduction + +

Another aspect of python that is not specifically covered in this course is Pygame. This section will cover an introduction and key features of pygame so that you could get started programming your own game. + Pygame is a free open-source library for programming games using Python. Pygame is considered a simple library in the world of game development, its capabilities include: graphics and animation, user input handling, + collision detection, event handling, and sprite management. +

+ +

To first use pygame you must install it on your computer. For both Mac and Windows, open vscode and a new terminal (Terminal -> New Terminal). In the terminal type : pip install pygame. If you already have pygame installed + on your computer make sure that it is up to date with the latest version. Once pygame is installed and updated you are ready to get started!

+ +

Key Aspects of Game Development:

+

Game loop: The game loop is a continuous cycle that begins when game play starts and ends based upon certain events happening. The game loop listens for user input such as: a mouse click, key events, the window closing, etc. + based upon these inputs the loop will update the game. This creates the illusion of the user interacting with the game. For example, programming that once a user lost a certain amount of points the game would end, the game + loop would continuously check the amount of points the user had and once it reached a certain threshold the game would quit.

+ +

Event handling: In game development event handling is a way to control responses to an event. When an event occurs such as user input or a collision, the event is directed to an event handler. This is a block of code that runs in + response to the event. For example, if the user collides with an obstacle the game will end.

+ +

Sprites: Sprites are two dimensional animations or objects that often represent characters within the game. In pygame, sprites are typically created as classes and different sprite objects are created from that class. + This could be as the user, as an enemy(ies) etc.

+ +

Collisions: Collisions are an often used concept in game development, many basic games such as car racing, pong, etc. Collision detection is also a type of event handling, where when two objects share the same coordinates + the collision event occurs.

+ +

Game speed: Game speed is an aspect of the game that controls how quickly the game is played. For example, if you are playing an obstacle course, how quickly or not the objects appear is considered the game speed. + Typically this is a variable that can be adjusted by the players depending on the difficulty that they choose to play the game at.

+ + +

When beginning writing any pygame file the first two lines should always be: import pygame and pygame.init(). + These two commands import and initialize the pygame module so that you may use it in your code. You will then want + to create and name the window that your game will appear in outside of your coding environment. To do this:

+ + +window = pygame.display.set_mode((500,500)) #this will create and set the size of your window +pygame.display.set_caption("Name") #this will set the name of your window +exit = False +while !exit: #game loop + for event in pygame.event.get() #listens for events + if event.type == pygame.QUIT: + exit = True + pygame.display.update() + + +

When the above code is run. it should create a blank black window on your computer. All further creative ideas can extend from this central code.

+ +

Drawing in Pygame:

+ + +Color(r,g,b) #Color +pygame.display.set_mode() #Create display window + + +

Pygame can only have a single display active at a time, if set_mode() is called while another window is open, the previous window will be closed. +Using pygame.display.quit() will also shut down the window.

+

pygame.draw.rect- draws a rectangle

+

pygame.draw.polygon- draws a polygon

+

pygame.draw.circle- draws a circle

+

pygame.draw.ellipse- draws an ellipse

+

pygame.draw.arc- draws an elliptical arc

+

pygame.draw.line- draws a straight line

+

pygame.draw.lines- draws multiple contiguous straight line segments

+ +

Events in Pygame:

+

pygame.event.get()-gets events from the queue

+

pygame.event.wait()- waits for single event from the queue

+

pygame.event.clear()- removes all events from the queue

+

To get the state of various input devices, you can forego the event queue and access the input +devices directly with their appropriate modules. For example: pyagme.mouse, pygame.key, pygame.joystick, etc. +If this method is used, pygame requires some from of communication with the system window manager and other parts +of the platform. To keep pygame in sync with the system you will need to call: pygame.event.pump(), to keep everything current. +Usually, this should be called once per game loop.

+ +

The event queue itself contains event objects that can be accessed from the queue by checking for existence or grabbing them +directly off of the stack.

+ +

Mouse Event in Pygame:

+

pygame.mouse.get_pressed()- get the state of the mouse buttons

+

pygame.mouse.get_pos()- get the mouse cursor position

+

pygame.mouse.set_pos()- set the mouse cursor position

+

pygame.mouse.set_visible()- hide or show the mouse cursor

+

pygame.mouse.get_visible()- check if the display is receiving mouse input

+ +

When the display mode is set, the queue will start receiving mouse evets. The mouse buttons themselves will generate: pygameMOUSEBUTTONDOWN/UP events. +Similarly, a pygame.MOUSEMOTION event whenever the mouse is moved. All of these events can be directed to an event handler within the game loop.

+ +

Time in Pygame:

+

pygame.time.get_ticks()- gets the time in milliseconds

+

pygame.time.wait()- pauses the program for a specified amount of time

+

pygame.time.delay()- same^

+

pygame.time.set_timer()- repeatedly create an event on the event queue

+

pygame.time.Clock- creates an object to track the time.

+

Use of time in pygame can add an additional layer of complexity to your game. Whether that is making sure that the user completes a game level within a certain +amount of time, or times the user and records the best time.

+ +

Here is an example of code that moves a box around the screen using the arrow keys.

+
+ + +
+ + + +
diff --git a/pretext/AdditionalTopics/pygame/image.png b/pretext/AdditionalTopics/pygame/image.png new file mode 100644 index 000000000..5b67d93fa Binary files /dev/null and b/pretext/AdditionalTopics/pygame/image.png differ diff --git a/pretext/AdditionalTopics/toctree.ptx b/pretext/AdditionalTopics/toctree.ptx new file mode 100644 index 000000000..5f1a746a0 --- /dev/null +++ b/pretext/AdditionalTopics/toctree.ptx @@ -0,0 +1,18 @@ + + +4Additional Topics +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 + diff --git a/pretext/AdditionalTopics/video-more-python.ptx b/pretext/AdditionalTopics/video-more-python.ptx new file mode 100644 index 000000000..485b0ef9f --- /dev/null +++ b/pretext/AdditionalTopics/video-more-python.ptx @@ -0,0 +1,7 @@ + +
+ Practice Video Lecture +

Week 15 Video Lecture:

+
diff --git a/pretext/ClassesBasics/Achangeofperspective.ptx b/pretext/ClassesBasics/Achangeofperspective.ptx index 71c3bec0d..d928a8e9e 100644 --- a/pretext/ClassesBasics/Achangeofperspective.ptx +++ b/pretext/ClassesBasics/Achangeofperspective.ptx @@ -1,17 +1,11 @@
A change of perspective -

Throughout the earlier chapters, we wrote functions and called them using a syntax such as drawCircle(tess). This suggests that the - function is the active agent. It says something like, Hey, drawCircle! - Here's a turtle object for you to use to draw with.

-

In object-oriented programming, the objects are considered the active agents. - For example, in our early introduction to turtles, we used - an object-oriented style. We said tess.forward(100), which - asks the turtle to move itself forward by the given number of steps. - An - invocation like tess.circle() says Hey tess! - Please use your circle method!

-

This change in perspective is sometimes considered to be a more polite way to write programming instructions. However, it may not initially +

Throughout the earlier chapters, we wrote functions and called them using a syntax such as printRange(lst). This suggests that the + function is the active agent. It says something like, "Hey, printRange! + Here's a list for you to use to print with."

+

In object-oriented programming, the objects are considered the active agents. This change in perspective is sometimes considered to be a more "polite" way + to write programming instructions. However, it may not initially be obvious that it is useful. It turns out that often times shifting responsibility from the functions onto the objects makes it possible to write more versatile functions and makes it easier to maintain and reuse code.

diff --git a/pretext/ClassesBasics/AddingOtherMethodstoourClass.ptx b/pretext/ClassesBasics/AddingOtherMethodstoourClass.ptx index 8365cd680..c4d897295 100644 --- a/pretext/ClassesBasics/AddingOtherMethodstoourClass.ptx +++ b/pretext/ClassesBasics/AddingOtherMethodstoourClass.ptx @@ -10,11 +10,11 @@ We can group together the sensible operations, and the kinds of data they apply to, and each instance of the class can have its own state.

A method behaves like a function but it is invoked on a specific - instance. For example, with a turtle named tess, tess.right(90) asks the tess object to perform its - right method and turn 90 degrees. Methods are accessed using dot notation.

+ instance. For example, with a list named lst, lst.append(90) asks the lst object to perform its + append method and add the integer 90 to the end of the list. Methods are accessed using dot notation.

Let's add two simple methods to allow a point to give us information about its state. The getX method, when invoked, will return the value of the x coordinate. The implementation of this method is straight forward since we already know how to write functions that return values. One thing to notice is that even though the getX method does not need any other parameter information to do its work, there is still one formal parameter, self. As we stated earlier, all methods defined in a class that operate on objects of that class will have self as their first parameter. Again, this serves as reference to the object itself which in turn gives access to the state data inside the object.

- + class Point: """ Point class for representing and manipulating x,y coordinates. """ @@ -40,7 +40,7 @@ print(p.getY())

Let's add another method, distanceFromOrigin, to see better how methods work. This method will again not need any additional information to do its work. It will perform a more complex task.

- + class Point: """ Point class for representing and manipulating x,y coordinates. """ diff --git a/pretext/ClassesBasics/ConvertinganObjecttoaString.ptx b/pretext/ClassesBasics/ConvertinganObjecttoaString.ptx index d46f2cc7a..c213d5d0d 100644 --- a/pretext/ClassesBasics/ConvertinganObjecttoaString.ptx +++ b/pretext/ClassesBasics/ConvertinganObjecttoaString.ptx @@ -3,7 +3,7 @@ Converting an Object to a String

When we're working with classes and objects, it is often necessary to print an object (that is to print the state of an object). Consider the example below.

- + class Point: """ Point class for representing and manipulating x,y coordinates. """ @@ -35,7 +35,7 @@ print(p)

The __str__ method is responsible for returning a string representation as defined by the class creator. In other words, you as the programmer, get to choose what a Point should look like when it gets printed. In this case, we have decided that the string representation will include the values of x and y as well as some identifying text. It is required that the __str__ method create and return a string.

- + class Point: """ Point class for representing and manipulating x,y coordinates. """ diff --git a/pretext/ClassesBasics/ImprovingourConstructor.ptx b/pretext/ClassesBasics/ImprovingourConstructor.ptx index e4fc90bde..a85f45fb7 100644 --- a/pretext/ClassesBasics/ImprovingourConstructor.ptx +++ b/pretext/ClassesBasics/ImprovingourConstructor.ptx @@ -5,7 +5,7 @@ provide some additional capability for the user to pass information to the constructor. Since constructors are simply specially named functions, we can use parameters (as we've seen before) to provide the specific information.

We can make our class constructor more general by putting extra parameters into the __init__ method, as shown in this codelens example.

- + class Point: """ Point class for representing and manipulating x,y coordinates. """ diff --git a/pretext/ClassesBasics/InstancesasReturnValues.ptx b/pretext/ClassesBasics/InstancesasReturnValues.ptx index c246aeadf..71ca70edf 100644 --- a/pretext/ClassesBasics/InstancesasReturnValues.ptx +++ b/pretext/ClassesBasics/InstancesasReturnValues.ptx @@ -8,7 +8,7 @@ and wish to find the midpoint halfway between it and some other target point. We would like to write a method, call it halfway that takes another Point as a parameter and returns the Point that is halfway between the point and the target.

- + class Point: @@ -50,7 +50,7 @@ print(mid.getY()) We can always see whether the coordinates of Point self or target are being referred to.

This workspace is provided for your convenience. You can use this activecode window to try out anything you like.

- + diff --git a/pretext/ClassesBasics/ObjectsRevisited.ptx b/pretext/ClassesBasics/ObjectsRevisited.ptx index ea2f44fcf..bc12ff29b 100644 --- a/pretext/ClassesBasics/ObjectsRevisited.ptx +++ b/pretext/ClassesBasics/ObjectsRevisited.ptx @@ -1,11 +1,11 @@
Objects Revisited -

In Python, every value is actually an object. Whether it be a turtle, a list, or even an integer, they are all objects. Programs manipulate those objects either by performing - computation with them or by asking them to perform methods. To be more specific, we say that an object has - a state and a collection of methods that it can perform. The state of an object represents those things - that the object knows about itself. For example, as we have seen with turtle objects, each turtle has a state consisting - of the turtle's position, its color, its heading and so on. Each turtle also has the ability to go forward, backward, or turn right or left. Individual turtles are different in that even though they are - all turtles, they differ in the specific values of the individual state attributes (maybe they are in a different location or have a different heading).

- Simple object has state and methods +

In Python, every value is actually an object. Whether it be a string, a list, or even an integer, they are all objects. Programs manipulate those objects either by performing + computation with them or by asking them to perform methods. To be more specific, we say that an object has + a state and a collection of methods that it can perform. The state of an object represents those things + that the object knows about itself. For example, each list has a state consisting + of the items it contains, a length and so on. Each list also has the ability to add, delete, and sort the items inside of it. Individual lists are different in + that even though they are all lists, they differ in the specific values of the individual state attributes (maybe they have different items inside them).

+
diff --git a/pretext/ClassesBasics/ObjectsasArgumentsandParameters.ptx b/pretext/ClassesBasics/ObjectsasArgumentsandParameters.ptx index 93a00188f..7be61958f 100644 --- a/pretext/ClassesBasics/ObjectsasArgumentsandParameters.ptx +++ b/pretext/ClassesBasics/ObjectsasArgumentsandParameters.ptx @@ -1,13 +1,9 @@
Objects as Arguments and Parameters -

You can pass an object as an argument in the usual way. We've already seen - this in some of the turtle examples where we passed the turtle to - some function like drawRectangle so that the function could - control and use whatever turtle instance we passed to it.

-

Here is a simple function called distance involving our new Point objects. The job of this function is to figure out the - distance between two points.

- +

You can pass an object as an argument in the usual way. Here is a simple function called distance involving our new Point objects. + The job of this function is to figure out the distance between two points.

+ import math diff --git a/pretext/ClassesBasics/UserDefinedClasses.ptx b/pretext/ClassesBasics/UserDefinedClasses.ptx index 7a7a52a72..cabf4271a 100644 --- a/pretext/ClassesBasics/UserDefinedClasses.ptx +++ b/pretext/ClassesBasics/UserDefinedClasses.ptx @@ -1,7 +1,7 @@
User Defined Classes -

We've already seen classes like str, int, float and Turtle. These were defined by Python and +

We've already seen classes like str, int, and float. These were defined by Python and made available for us to use. However, in many cases when we are solving problems we need to create data objects that are related to the problem we are trying to solve. We need to create our own classes.

As an example, consider the concept of a mathematical point. In two dimensions, a point is two @@ -49,7 +49,7 @@ class Point: other name, but nobody ever does!) is automatically set to reference the newly-created object that needs to be initialized.

So let's use our new Point class now.

- + class Point: """ Point class for representing and manipulating x,y coordinates. """ @@ -68,7 +68,7 @@ print("Nothing seems to have happened with the points")

During the initialization of the objects, we created two attributes called x and y for each, and gave them both the value 0.

-

The asignments are not to x and y, but to self.x and self.y. +

The assignments are not to x and y, but to self.x and self.y. The attributes x and y are always attached to a particular instance. The instance is always explicitly referenced with dot notation.

@@ -77,7 +77,7 @@ print("Nothing seems to have happened with the points") having an x and y coordinate with value 0. However, because we have not asked the point to do anything, we don't see any other result.

Simple object has state and methods

You can see this for yourself, via codelens:

- + class Point: """ Point class for representing and manipulating x,y coordinates. """ @@ -95,7 +95,7 @@ print("Nothing seems to have happened with the points")

The following program adds a few print statements. You can see that the output suggests that each one is a Point object. However, notice that the is operator returns False meaning that they are different objects (we will have more to say about this in a later chapter).

- + class Point: """ Point class for representing and manipulating x,y coordinates. """ @@ -114,7 +114,7 @@ print(q) print(p is q) -

This should look familiar — we've used classes before to create +

The variables p and q are assigned references to two new Point objects. A function like Turtle or Point that creates a new object instance is called a constructor. Every class automatically uses the name of the class as the name of the constructor function. @@ -141,7 +141,7 @@ alex = Turtle()

Check Your Understanding

- +

What is the the output of the following print code?

diff --git a/pretext/ClassesBasics/toctree.ptx b/pretext/ClassesBasics/toctree.ptx index 4d9669ccf..245c14591 100644 --- a/pretext/ClassesBasics/toctree.ptx +++ b/pretext/ClassesBasics/toctree.ptx @@ -11,5 +11,4 @@ 4 4 4 -4 diff --git a/pretext/ClassesDiggingDeeper/ArithmeticMethods.ptx b/pretext/ClassesDiggingDeeper/ArithmeticMethods.ptx index 62e988b93..f98e63ca2 100644 --- a/pretext/ClassesDiggingDeeper/ArithmeticMethods.ptx +++ b/pretext/ClassesDiggingDeeper/ArithmeticMethods.ptx @@ -23,7 +23,7 @@ def add(self,otherfraction):

You can try the addition method and then modify the fractions and retry.

- + def gcd(m, n): while m % n != 0: @@ -94,7 +94,7 @@ print(f3)

This workspace is provided for your convenience. You can use this activecode window to try out anything you like.

- + diff --git a/pretext/ClassesDiggingDeeper/Fractions.ptx b/pretext/ClassesDiggingDeeper/Fractions.ptx index 879380f0f..8c89a58b2 100644 --- a/pretext/ClassesDiggingDeeper/Fractions.ptx +++ b/pretext/ClassesDiggingDeeper/Fractions.ptx @@ -12,7 +12,7 @@

To design our class, we simply need to use the analysis above to realize that the state of a fraction object can be completely described by representing two integers. We can begin by implementing the Fraction class and the __init__ method which will allow the user to provide a numerator and a denominator for the fraction being created.

- + class Fraction: diff --git a/pretext/ClassesDiggingDeeper/ObjectsareMutable.ptx b/pretext/ClassesDiggingDeeper/ObjectsareMutable.ptx index 21fd86124..7b170476b 100644 --- a/pretext/ClassesDiggingDeeper/ObjectsareMutable.ptx +++ b/pretext/ClassesDiggingDeeper/ObjectsareMutable.ptx @@ -12,7 +12,7 @@ representation would be 3/4.

There is a very nice iterative method for computing the greatest common divisor of two integers. Try to run the function on a number of different examples.

- + def gcd(m, n): while m % n != 0: @@ -31,7 +31,7 @@ print(gcd(12, 16)) a fraction method called simplify. We will ask the fraction to put itself in lowest terms.

The simplify method will pass the numerator and the denominator to the gcd function to find the greatest common divisor. It will then modify itself by dividing its num and its den by that value.

- + def gcd(m, n): while m % n != 0: diff --git a/pretext/ClassesDiggingDeeper/Sameness.ptx b/pretext/ClassesDiggingDeeper/Sameness.ptx index a6768f6d8..1dd7ac238 100644 --- a/pretext/ClassesDiggingDeeper/Sameness.ptx +++ b/pretext/ClassesDiggingDeeper/Sameness.ptx @@ -13,7 +13,7 @@

We've already seen the is operator in the chapter on lists, where we talked about aliases. It allows us to find out if two references refer to the same object.

- + class Fraction: @@ -53,7 +53,7 @@ def sameRational(f1, f2):

This type of equality is known as deep equality since it compares the values deep in the object, not just the reference to the object.

- + def sameRational(f1, f2): return f1.getNum()*f2.getDen() == f2.getNum() * f1.getDen() diff --git a/pretext/ClassesDiggingDeeper/toctree.ptx b/pretext/ClassesDiggingDeeper/toctree.ptx index 83d49f7dc..e268cd4dc 100644 --- a/pretext/ClassesDiggingDeeper/toctree.ptx +++ b/pretext/ClassesDiggingDeeper/toctree.ptx @@ -6,5 +6,5 @@ 4 4 4 -4 +4 diff --git a/pretext/ClassesDiggingDeeper/video-classes.ptx b/pretext/ClassesDiggingDeeper/video-classes.ptx new file mode 100644 index 000000000..8693bbc58 --- /dev/null +++ b/pretext/ClassesDiggingDeeper/video-classes.ptx @@ -0,0 +1,7 @@ + +
+ Practice Video Lecture +

Week 13 Video Lecture:

+
diff --git a/pretext/Strings/Afindfunction.ptx b/pretext/ComplexLogic/Afindfunction.ptx similarity index 95% rename from pretext/Strings/Afindfunction.ptx rename to pretext/ComplexLogic/Afindfunction.ptx index c531bb891..723b13c40 100644 --- a/pretext/Strings/Afindfunction.ptx +++ b/pretext/ComplexLogic/Afindfunction.ptx @@ -1,8 +1,8 @@ -
+
A <c>find</c> function

Here is an implementation for a restricted find method, where the target is a single character.

- + def find(astring, achar): """ diff --git a/pretext/Lists/Aliasing.ptx b/pretext/ComplexLogic/Aliasing.ptx similarity index 98% rename from pretext/Lists/Aliasing.ptx rename to pretext/ComplexLogic/Aliasing.ptx index 415797a7e..e450d82b0 100644 --- a/pretext/Lists/Aliasing.ptx +++ b/pretext/ComplexLogic/Aliasing.ptx @@ -16,7 +16,7 @@ print(a is b) is aliased. Changes made with one alias affect the other. In the codelens example below, you can see that a and b refer to the same list after executing the assignment statement b = a.

- + a = [81, 82, 83] b = [81, 82, 83] diff --git a/pretext/Lists/FunctionsthatProduceLists.ptx b/pretext/ComplexLogic/FunctionsthatProduceLists.ptx similarity index 100% rename from pretext/Lists/FunctionsthatProduceLists.ptx rename to pretext/ComplexLogic/FunctionsthatProduceLists.ptx diff --git a/pretext/Strings/Loopingandcounting.ptx b/pretext/ComplexLogic/Loopingandcounting.ptx similarity index 100% rename from pretext/Strings/Loopingandcounting.ptx rename to pretext/ComplexLogic/Loopingandcounting.ptx diff --git a/pretext/ComplexLogic/NestedList_Navigation.ptx b/pretext/ComplexLogic/NestedList_Navigation.ptx new file mode 100644 index 000000000..f0965f52b --- /dev/null +++ b/pretext/ComplexLogic/NestedList_Navigation.ptx @@ -0,0 +1,107 @@ + +
+ Nested List Traversal +

Nested lists can be traversed the say way as other lists. However, the code to do so can easily get complicated and confusing. In this section let us + examine several example programs that perform this task.

+ +

The following program concerns a list of lists of names. In some cases, we may want to take nested list data and "un-nest" it. To do this, we need to + iterate through the outer list, and then construct another loop to iterate through each name in each sublist. In this program's case, we are simply printing each + name as we encounter it.

+ + +names = [["Thomas", "Abraham", "George"], ["Theodore", "Grover", "William"], ["Franklin", "Dwight", "John", "Harry"], ["George", "Bill"]] +for sublist in names: + for name in sublist: + print(name) + + +

Nested lists can also be navigated by index rather than by value. This complicates the code somewhat, but gives us more flexibility for what can be done to each item. + In this example program, we visit each name and turn it lowercase.

+ + +names = [["Thomas", "Abraham", "George"], ["Theodore", "Grover", "William"], ["Franklin", "Dwight", "John", "Harry"], ["George", "Bill"]] +for sublist_index in range(len(names)): + for name_index in range(len(names[sublist_index])): + names[sublist_index][name_index] = names[sublist_index][name_index].lower() +print(names) + + +

Note that in the above program it is very important that we use len(names[sublist_index]) in the second range call. Because the nested lists in this example + do not have the same length, we have to check the length of each sublist when setting up our nested for loop bounds. If we do not do this carefully, then we can easily + miss data or get an out of bounds exception. Consider a different, erroneous example:

+ + +names = [["Thomas", "Abraham", "George"], ["Theodore", "Grover", "William"], ["Franklin", "Dwight", "John", "Harry"], ["George", "Bill"]] +for sublist_index in range(len(names)): + for name_index in range(len(names[0])): + names[sublist_index][name_index] = names[sublist_index][name_index].lower() +print(names) + + +

Nested lists can be used in many different contexts, but one more common use is to model 2D grids of data. Here is one last example that treats nested lists as a 2D coordinate plane + and finds the location of a single value in that plane. Note this example reverses the y axis, so y values increase as you travel downward. This is to match the indexing on the lists, + though we could model this as a more familiar coordinate system with some small changes.

+ + +coord = [['_', '_', '_', '_'], + ['_', '_', 'p', '_'], + ['_', '_', '_', '_'], + ['_', '_', '_', '_']] +for y in range(len(coord)): + for x in range(len(coord[y])): + if coord[y][x] == 'p': + print(f'p is as coordinates (x={x}, y={y})') + + +

+ Check your understanding +

+ + +

What is printed by the following statements?

+ + +alist = [ [4, [True, False], 6, 8], [888, 999] ] +for a in alist: + for b in range(1, len(a)): + if type(a[b]) == type(a[b-1]): + print(a[b], end=" ") + + +
+ + + +

8 999

+
+ + Yes, you are correct. + +
+ + +

4 6 8 888 999

+
+ + You are correct that the code will treat the list with boolean value differently, but you are missing other aspects. Look again at the bounds of the inner loop. + +
+ + +

4 True False 6 8 888 999

+
+ + This would be the output for a full traversal of the data. However, this code cannot fully traverse the data since it has three levels of nesting and the code only reaches two levels. + +
+ + +

4 [True, False] 6 8 888 999

+
+ + This would be the correct output if the code traversed two levels of nesting and printed everything. However, the if statement is also doing something here. + +
+
+
+
diff --git a/pretext/Lists/NestedLists.ptx b/pretext/ComplexLogic/NestedLists.ptx similarity index 91% rename from pretext/Lists/NestedLists.ptx rename to pretext/ComplexLogic/NestedLists.ptx index 04ea9df74..0e5b5cf9e 100644 --- a/pretext/Lists/NestedLists.ptx +++ b/pretext/ComplexLogic/NestedLists.ptx @@ -1,5 +1,5 @@ -
+
Nested Lists

A nested list is a list that appears as an element in another list. In this list, the element with index 3 is a nested list. @@ -7,7 +7,7 @@ nested list, we can proceed in two steps. First, extract the nested list, then extract the item of interest. It is also possible to combine those steps using bracket operators that evaluate from left to right.

- + nested = ["hello", 2.0, 5, [10, 20]] innerlist = nested[3] @@ -18,10 +18,10 @@ print(item) print(nested[3][1]) -

+

Check your understanding

- +

What is printed by the following statements?

diff --git a/pretext/Lists/ObjectsandReferences.ptx b/pretext/ComplexLogic/ObjectsandReferences.ptx similarity index 91% rename from pretext/Lists/ObjectsandReferences.ptx rename to pretext/ComplexLogic/ObjectsandReferences.ptx index bf8bd24d2..8582f42f1 100644 --- a/pretext/Lists/ObjectsandReferences.ptx +++ b/pretext/ComplexLogic/ObjectsandReferences.ptx @@ -1,5 +1,5 @@ -
+
Objects and References

If we execute these assignment statements,

@@ -19,7 +19,7 @@ b = "banana" refer to.

We already know that objects can be identified using their unique identifier. We can also test whether two names refer to the same object using the is operator. The is operator will return true if the two references are to the same object. In other words, the references are the same. Try our example from above.

- + a = "banana" b = "banana" @@ -32,7 +32,7 @@ print(a is b) Since strings are immutable, Python can optimize resources by making two names that refer to the same string literal value refer to the same object.

This is not the case with lists. Consider the following example. Here, a and b refer to two different lists, each of which happens to have the same element values.

- + a = [81, 82, 83] b = [81, 82, 83] @@ -48,7 +48,7 @@ print(a == b) a and b have the same value but do not refer to the same object.

There is one other important thing to notice about this reference diagram. The variable a is a reference to a collection of references. Those references actually refer to the integer values in the list. In other words, a list is a collection of references to objects. Interestingly, even though a and b are two different lists (two different collections of references), the integer object 81 is shared by both. Like strings, integers are also immutable so Python optimizes and lets everyone share the same object for some commonly used small integers.

Here is the example in codelens. Pay particular attention to the id values.

- + a = [81, 82, 83] b = [81, 82, 83] diff --git a/pretext/Strings/Optionalparameters.ptx b/pretext/ComplexLogic/Optionalparameters.ptx similarity index 100% rename from pretext/Strings/Optionalparameters.ptx rename to pretext/ComplexLogic/Optionalparameters.ptx diff --git a/pretext/Lists/PureFunctions.ptx b/pretext/ComplexLogic/PureFunctions.ptx similarity index 86% rename from pretext/Lists/PureFunctions.ptx rename to pretext/ComplexLogic/PureFunctions.ptx index 453bf9383..05c7c04c0 100644 --- a/pretext/Lists/PureFunctions.ptx +++ b/pretext/ComplexLogic/PureFunctions.ptx @@ -1,5 +1,5 @@ -
+
Pure Functions

A pure function does not produce side effects. It communicates with the calling program only through parameters (which it does not modify) and a return @@ -7,7 +7,7 @@ To use the pure function version of double_stuff to modify things, you would assign the return value back to things.

- + def doubleStuff(a_list): """ Return a new list in which contains doubles of the elements in a_list. """ @@ -24,7 +24,7 @@ print(things)

Once again, codelens helps us to see the actual references and objects as they are passed and returned.

- + def doubleStuff(a_list): """ Return a new list in which contains doubles of the elements in a_list. """ diff --git a/pretext/Lists/RepetitionandReferences.ptx b/pretext/ComplexLogic/RepetitionandReferences.ptx similarity index 98% rename from pretext/Lists/RepetitionandReferences.ptx rename to pretext/ComplexLogic/RepetitionandReferences.ptx index 7e5bdaf90..b99bba568 100644 --- a/pretext/Lists/RepetitionandReferences.ptx +++ b/pretext/ComplexLogic/RepetitionandReferences.ptx @@ -44,7 +44,7 @@ print(newlist) Same reference

Here is the same example in codelens. Step through the code paying particular attention to the result of executing the assignment statement origlist[1] = 99.

- + origlist = [45, 76, 34, 55] diff --git a/pretext/Lists/TheAccumulatorPatternwithLists.ptx b/pretext/ComplexLogic/TheAccumulatorPatternwithLists.ptx similarity index 90% rename from pretext/Lists/TheAccumulatorPatternwithLists.ptx rename to pretext/ComplexLogic/TheAccumulatorPatternwithLists.ptx index 17462b8a3..f4dd36055 100644 --- a/pretext/Lists/TheAccumulatorPatternwithLists.ptx +++ b/pretext/ComplexLogic/TheAccumulatorPatternwithLists.ptx @@ -1,5 +1,5 @@ -
+
The Accumulator Pattern with Lists

Remember the ? Many algorithms involving lists make use of @@ -7,23 +7,23 @@ explore the use of the accumulator pattern with lists.

Let's take the problem of adding up all of the items in a list. The following program computes the sum of a list of numbers.

- + -sum = 0 +total = 0 for num in [1, 3, 5, 7, 9]: - sum = sum + num -print(sum) + total = total + num +print(total) -

The program begins by defining an accumulator variable, sum, and initializing it to 0 (line 1).

+

The program begins by defining an accumulator variable, total, and initializing it to 0 (line 1).

Next, the program iterates over the list (lines 2-3), and updates the sum on each - iteration by adding an item from the list (line 3). When the loop is finished, sum + iteration by adding an item from the list (line 3). When the loop is finished, total has accumulated the sum of all of the items in the list.

Take a moment to step through this program using CodeLens to see how it works. It's important to grasp the basic techniques.

Sometimes when we're accumulating, we don't want to add to our accumulator every time we iterate. Consider, for example, the following program which counts the number of names with more than 3 letters.

- + long_names = 0 for name in ["Joe", "Sally", "Amy", "Brad"]: @@ -39,7 +39,7 @@ print(long_names) it.

At the end, we have accumulated the total number of long names.

We can use conditionals to also count if particular items are in a string or list. The following code finds all occurrences of vowels in a string.

- + s = "what if we went to the zoo" num_vowels = 0 @@ -53,13 +53,13 @@ print(num_vowels) an o. If it is an o then we will update our counter.

a gif that shows code to check that "o" is in the phrase "onomatopoeia".
- + Accumulating the Max Value

We can also use the accumulation pattern with conditionals to find the maximum or minimum value. Instead of continuing to build up the accumulator value like we have when counting or finding a sum, we can reassign the accumulator variable to a different value.

The following example shows how we can get the maximum value from a list of integers.

- + nums = [9, 3, 8, 11, 5, 29, 2] best_num = 0 @@ -77,7 +77,7 @@ print(best_num) happen to our code? What if we were looking for the smallest number but we initialized best_num with zero? To get around this issue, we can initialize the accumulator variable using one of the numbers in the list.

- + nums = [9, 3, 8, 11, 5, 29, 2] best_num = nums[0] @@ -90,11 +90,11 @@ print(best_num)

The only thing we changed was the value of best_num on line 2 so that the value of best_num is the first element in nums, but the result is still the same!

- + Accumulating a String Result

The accumulator pattern can be used to convert a list of items to a string.

Consider the following program:

- + scores = [85, 95, 70] result = '' @@ -110,13 +110,13 @@ print("The scores are " + result)

The output of the program has some undesirable formatting problems: there is a trailing comma instead of a period, and there are no spaces between the items. The next activity lets you work to correct those problems.

- +

Let's work to improve the formatting of the sentence produced by the program above. Revise the following code so that it outputs the sentence:

The scores are 85, 95, and 70.
- + scores = [85, 95, 70] result = '' @@ -145,7 +145,7 @@ myTests().main() This solution works by iterating over all of the scores in the list except the last, and dealing with that one separately. - + scores = [85, 95, 70] result = '' for score in scores[:-1]: @@ -166,7 +166,7 @@ print("The scores are " + result)

Check your understanding

- +

What is printed by the following statements?

@@ -215,7 +215,7 @@ print(x)
- +

What is printed by the following statements?

@@ -264,11 +264,11 @@ print(min_value)
- +
diff --git a/pretext/Strings/TheAccumulatorPatternwithStrings.ptx b/pretext/ComplexLogic/TheAccumulatorPatternwithStrings.ptx similarity index 92% rename from pretext/Strings/TheAccumulatorPatternwithStrings.ptx rename to pretext/ComplexLogic/TheAccumulatorPatternwithStrings.ptx index 9ef9bc2ee..9212b4778 100644 --- a/pretext/Strings/TheAccumulatorPatternwithStrings.ptx +++ b/pretext/ComplexLogic/TheAccumulatorPatternwithStrings.ptx @@ -1,10 +1,10 @@ -
+
The Accumulator Pattern with Strings

Combining the in operator with string concatenation using + and the accumulator pattern, we can write a function that removes all the vowels from a string. The idea is to start with a string and iterate over each character, checking to see if the character is a vowel. As we process the characters, we will build up a new string consisting of only the nonvowel characters. To do this, we use the accumulator pattern.

Remember that the accumulator pattern allows us to keep a running total. With strings, we are not accumulating a numeric total. Instead we are accumulating characters onto a string.

- + def removeVowels(s): vowels = "aeiouAEIOU" @@ -36,7 +36,7 @@ if eachChar != 'a' and eachChar != 'e' and eachChar != 'i' and

Take a close look also at the initialization of sWithoutVowels. We start with an empty string and then begin adding new characters to the end.

Step through the function using codelens to see the accumulator variable grow.

- + def removeVowels(s): vowels = "aeiouAEIOU" @@ -52,7 +52,7 @@ print(removeVowels("compsci"))

Check your understanding

- +

What is printed by the following statements:

@@ -94,7 +94,7 @@ print(r)

This workspace is provided for your convenience. You can use this activecode window to try out anything you like.

- + diff --git a/pretext/Lists/UsingListsasParameters.ptx b/pretext/ComplexLogic/UsingListsasParameters.ptx similarity index 90% rename from pretext/Lists/UsingListsasParameters.ptx rename to pretext/ComplexLogic/UsingListsasParameters.ptx index c774a2aa8..52b7b5853 100644 --- a/pretext/Lists/UsingListsasParameters.ptx +++ b/pretext/ComplexLogic/UsingListsasParameters.ptx @@ -1,5 +1,5 @@ -
+
Using Lists as Parameters

Functions which take lists as arguments and change them during execution are called modifiers and the changes they make are called side effects. @@ -9,7 +9,7 @@ the same list that the argument is referencing. For example, the function below takes a list as an argument and multiplies each element in the list by 2:

- + def doubleStuff(aList): """ Overwrite each element in aList with double its value. """ @@ -29,7 +29,7 @@ print(things) If a function modifies the elements of a list parameter, the caller sees the change since the change is occurring to the original.

This can be easily seen in codelens. Note that after the call to doubleStuff, the formal parameter aList refers to the same object as the actual parameter things. There is only one copy of the list object itself.

- + def doubleStuff(aList): """ Overwrite each element in aList with double its value. """ diff --git a/pretext/Lists/WhichisBetter.ptx b/pretext/ComplexLogic/WhichisBetter.ptx similarity index 100% rename from pretext/Lists/WhichisBetter.ptx rename to pretext/ComplexLogic/WhichisBetter.ptx diff --git a/pretext/ComplexLogic/intro_ComplexLogic.ptx b/pretext/ComplexLogic/intro_ComplexLogic.ptx new file mode 100644 index 000000000..31d30702c --- /dev/null +++ b/pretext/ComplexLogic/intro_ComplexLogic.ptx @@ -0,0 +1,8 @@ + +
+ Complex Logic Introduction +

Believe it or not, in the past seven weeks we've covered nearly all of the fundamental constructs within programming. + While there is certainly much more to learn and discover, virtually any programming language you will see in the future offers some form + of branching, looping, and function definition. That is not to say we have mastered these topics yet. This week of class and this chapter + focuses on revisiting these fundamental topics and deepening our mastery of them.

+
diff --git a/pretext/ComplexLogic/toctree.ptx b/pretext/ComplexLogic/toctree.ptx new file mode 100644 index 000000000..1f9b76ea8 --- /dev/null +++ b/pretext/ComplexLogic/toctree.ptx @@ -0,0 +1,13 @@ + + +4Complex Logic +4 +4 +4 +4 +4 +4 +4 +4 +4 + diff --git a/pretext/ComplexLogic/video-complex.ptx b/pretext/ComplexLogic/video-complex.ptx new file mode 100644 index 000000000..6d785a21b --- /dev/null +++ b/pretext/ComplexLogic/video-complex.ptx @@ -0,0 +1,7 @@ + +
+ Practice Video Lecture +

Week 8 Video Lecture:

+
diff --git a/pretext/Debugging/HowtoAvoidDebugging.ptx b/pretext/Debugging/HowtoAvoidDebugging.ptx index 3b643b2a7..b88c6b05d 100644 --- a/pretext/Debugging/HowtoAvoidDebugging.ptx +++ b/pretext/Debugging/HowtoAvoidDebugging.ptx @@ -43,7 +43,7 @@

Ok, let's look at an example. Let's solve the problem posed in question 3 at the end of the Simple Python Data chapter. Ask the user for the time now (in hours 0 - 23), and ask for the number of hours to wait. Your program should output what the time will be on the clock when the alarm goes off. For example, if current_time is 8 and wait_time is 5, final_time should be 13 (1 pm).

So, where to start? The problem requires two pieces of input from the user, so let's start there and make sure we can get the data we need.

- + current_time = input("what is the current time (in hours)?") wait_time = input("How many hours do you want to wait") @@ -53,7 +53,7 @@ print(wait_time)

So far so good. Now let's take the next step. We need to figure out what the time will be after waiting wait_time number of hours. A reasonable solution is to simply add wait_time to current_time and print out the result. So lets try that.

- + current_time = input("What is the current time (in hours 0 - 23)?") wait_time = input("How many hours do you want to wait") @@ -66,7 +66,7 @@ print(final_time)

Hmm, when you run this example you see that something unexpected has happened. You would not realize this was an error unless you first knew what the program was supposed to do.

- +

Which of the following best describes what is wrong with the previous example?

@@ -99,7 +99,7 @@ print(final_time)

This error was probably pretty simple to spot, because we printed out the value of final_time and it is easy to see that the numbers were just concatenated together rather than added.

So what do we do about the problem? We will need to convert both current_time and wait_time to int. At this stage of your programming development, it can be a good idea to include the type of the variable in the variable name itself. So let's look at another iteration of the program that does that, and the conversion to integer.

- + current_time_str = input("What is the current time (in hours 0-23)?") wait_time_str = input("How many hours do you want to wait") @@ -114,7 +114,7 @@ print(final_time_int)

Now, that's a lot better, and in fact depending on the hours you chose, it may be exactly right. If you entered 8 for current_time and 5 for wait_time then 13 is correct. But if you entered 17 (5 pm) for current_time and 9 for wait_time then the result of 26 is not correct.

This illustrates an important aspect of testing: it is important to test your code on a range of inputs. It is especially important to test your code on boundary conditions. For this particular problem, you should test your program with current_time of 0, 23, and some values in between. You should test your wait_time for 0, and some larger values. What about negative numbers? Negative numbers don't make sense, and since we don't really have the tools to deal with telling the user when something is wrong we will not worry about that just yet.

So to account for those numbers that are bigger than 23, we need one final step: using the modulus operator.

- + current_time_str = input("What is the current time (in hours 0-23)?") wait_time_str = input("How many hours do you want to wait") diff --git a/pretext/Debugging/KnowyourerrorMessages.ptx b/pretext/Debugging/KnowyourerrorMessages.ptx index 9e83f8d55..4bf31b6ec 100644 --- a/pretext/Debugging/KnowyourerrorMessages.ptx +++ b/pretext/Debugging/KnowyourerrorMessages.ptx @@ -2,8 +2,11 @@
Know Your Error Messages +

Many problems in your program will lead to an error message. For example as I was writing and testing this chapter of the book I wrote the following version of the example program in the previous section.

+ + current_time_str = input("What is the current time (in hours 0-23)?") wait_time_str = input("How many hours do you want to wait") @@ -11,13 +14,17 @@ wait_time_str = input("How many hours do you want to wait") current_time_int = int(current_time_str) wait_time_int = int(wait_time_int) + final_time_int = current_time_int + wait_time_int print(final_time_int) + +

Can you see what is wrong, just by looking at the code? Maybe, maybe not. Our brain tends to see what we think is there, so sometimes it is very hard to find the problem just by looking at the code. Especially when it is our own code and we are sure that we have done everything right!

Let's try the program again, but this time in an activecode:

- + + current_time_str = input("What is the current time (in hours 0-23)?") wait_time_str = input("How many hours do you want to wait") @@ -29,13 +36,16 @@ final_time_int = current_time_int + wait_time_int print(final_time_int) +

Aha! Now we have an error message that might be useful. The name error tells us that wait_time_int is not defined. It also tells us that the error is on line 5. That's really useful information. Now look at line five and you will see that wait_time_int is used on both the left and the right hand side of the assignment statement.

+

The error descriptions you see in activecode may be different (and more understandable!) than in a regular Python interpreter. The interpreter in activecode is limited in many ways, but it is intended for beginners, including the wording chosen to describe errors.

- + +

In writing and using this book over the last few years we have collected a lot of statistics about the programs in this book. Here are some statistics about error messages for the exercise we have been looking at.

@@ -214,6 +225,7 @@ print(final_time_int)
+

Nearly 90% of the error messages encountered for this problem are ParseError, TypeError, NameError, or ValueError. We will look at these errors in three stages:

    @@ -228,27 +240,20 @@ print(final_time_int)

+
+ ParseError

Parse errors happen when you make an error in the syntax of your program. Syntax errors are like making grammatical errors in writing. If you don't use periods and commas in your writing then you are making it hard for other readers to figure out what you are trying to say. Similarly Python has certain grammatical rules that must be followed or else Python can't figure out what you are trying to say.

-

Usually ParseErrors can be traced back to missing punctuation characters, such as parentheses, quotation marks, or commas. Remember that in Python commas are used to separate parameters to functions. Parentheses must be balanced, or else Python thinks that you are trying to include everything that follows as a parameter to some function.

+ +

Usually ParseErrors can be traced back to missing punctuation characters, such as parentheses, quotation marks, or commas. Remember that in Python commas are used to separate values, such as when defining a list or giving multiple values to a function call. Parentheses must be balanced, or else Python thinks that you are trying to include everything that follows as a parameter to some function.

Here are a couple examples of Parse errors in the example program we have been using. See if you can figure out what caused them.

- - - - - current_time_str = input("What is the current time (in hours 0-23)?") -wait_time_str = input("How many hours do you want to wait" -current_time_int = int(current_time_str) -wait_time_int = int(wait_time_str) + -final_time_int = current_time_int + wait_time_int -print(final_time_int) - Since the error message points us to line 4 this might be a bit confusing. If you look at line four carefully you will see that there is no problem with the syntax. So, in this case the next step should be to back up and look at the previous line. In this case if you look at line 2 carefully you will see that there is a missing right parenthesis at the end of the line. Remember that parenthses must be balanced. Since Python allows statements to continue over multiple lines inside parentheses Python will continue to scan subsequent lines looking for the balancing right parenthesis. However in this case it finds the name current_time_int and it will want to interpret that as another parameter to the input function. But, there is not a comma to separate the previous string from the variable so as far as Python is concerned the error here is a missing comma. From your perspective its a missing parenthesis. - - + + current_time_str = input("What is the current time (in hours 0-23)?") wait_time_str = input("How many hours do you want to wait" @@ -258,25 +263,23 @@ wait_time_int = int(wait_time_str) final_time_int = current_time_int + wait_time_int print(final_time_int) -

Since the error message points us to line 4 this might be a bit confusing. If you look at line four carefully you will see that there is no problem with the syntax. So, in this case the next step should be to back up and look at the previous line. In this case if you look at line 2 carefully you will see that there is a missing right parenthesis at the end of the line. Remember that parenthses must be balanced. Since Python allows statements to continue over multiple lines inside parentheses Python will continue to scan subsequent lines looking for the balancing right parenthesis. However in this case it finds the name current_time_int and it will want to interpret that as another parameter to the input function. But, there is not a comma to separate the previous string from the variable so as far as Python is concerned the error here is a missing comma. From your perspective its a missing parenthesis.

-
-
-

Finding Clues How can you help yourself find these problems? One trick that can be very valuable in this situation is to simply start by commenting out the line number that is flagged as having the error. If you comment out line four, the error message now changes to point to line 5. Now you ask yourself, am I really that bad that I have two lines in a row that have errors on them? Maybe, so taken to the extreme, you could comment out all of the remaining lines in the program. Now the error message changes to TokenError: EOF in multi-line statement This is a very technical way of saying that Python got to the end of file (EOF) while it was still looking for something. In this case a right parenthesis.

- - - - - current_time_str = input("What is the "current time" (in hours 0-23)?") -wait_time_str = input("How many hours do you want to wait") -current_time_int = int(current_time_str) -wait_time_int = int(wait_time_str) -final_time_int = current_time_int + wait_time_int -print(final_time_int) - The error message points you to line 1 and in this case that is exactly where the error occurs. In this case your biggest clue is to notice the difference in highlighting on the line. Notice that the words current time are a different color than those around them. Why is this? Because current time is in double quotes inside another pair of double quotes Python thinks that you are finishing off one string, then you have some other names and finally another string. But you haven't separated these names or strings by commas, and you haven't added them together with the concatenation operator (+). So, there are several corrections you could make. First you could make the argument to input be as follows: "What is the 'current time' (in hours 0-23)" Notice that here we have correctly used single quotes inside double quotes. Another option is to simply remove the extra double quotes. Why were you quoting current time anyway? "What is the current time (in hours 0-23)" - - + +

+ Since the error message points us to line 4 this might be a bit confusing. If you look at line four carefully you will see that there is no problem with the syntax. So, in this case the next step should be to back up and look at the previous line. In this case if you look at line 2 carefully you will see that there is a missing right parenthesis at the end of the line. Remember that parentheses must be balanced. Since Python allows statements to continue over multiple lines inside parentheses Python will continue to scan subsequent lines looking for the balancing right parenthesis. However in this case it finds the name current_time_int and it will want to interpret that as another parameter to the input function. But, there is not a comma to separate the previous string from the variable so as far as Python is concerned the error here is a missing comma. From your perspective its a missing parenthesis. +

+ + +
+ +
+ +

Finding Clues How can you help yourself find these problems? One trick that can be very valuable in this situation is to simply start by commenting out the line number that is flagged as having the error. If you comment out line four, the error message now changes to point to line 5. Now you ask yourself, am I really that bad that I have two lines in a row that have errors on them? Maybe, so taken to the extreme, you could comment out all of the remaining lines in the program. Now the error message changes to TokenError: EOF in multi-line statement This is a very technical way of saying that Python got to the end of file (EOF) while it was still looking for something. In this case a right parenthesis.

+ + + + current_time_str = input("What is the "current time" (in hours 0-23)?") wait_time_str = input("How many hours do you want to wait") @@ -286,17 +289,26 @@ wait_time_int = int(wait_time_str) final_time_int = current_time_int + wait_time_int print(final_time_int) -

The error message points you to line 1 and in this case that is exactly where the error occurs. In this case your biggest clue is to notice the difference in highlighting on the line. Notice that the words current time are a different color than those around them. Why is this? Because current time is in double quotes inside another pair of double quotes Python thinks that you are finishing off one string, then you have some other names and finally another string. But you haven't separated these names or strings by commas, and you haven't added them together with the concatenation operator (+). So, there are several corrections you could make. First you could make the argument to input be as follows: "What is the 'current time' (in hours 0-23)" Notice that here we have correctly used single quotes inside double quotes. Another option is to simply remove the extra double quotes. Why were you quoting current time anyway? "What is the current time (in hours 0-23)"

- + + +

+ The error message points you to line 1 and in this case that is exactly where the error occurs. In this case your biggest clue is to notice the difference in highlighting on the line. Notice that the words current time are a different color than those around them. Why is this? Because current time is in double quotes inside another pair of double quotes Python thinks that you are finishing off one string, then you have some other names and finally another string. But you haven't separated these names or strings by commas, and you haven't added them together with the concatenation operator (+). So, there are several corrections you could make. First you could make the argument to input be as follows: "What is the 'current time' (in hours 0-23)" Notice that here we have correctly used single quotes inside double quotes. Another option is to simply remove the extra double quotes. Why were you quoting current time anyway? "What is the current time (in hours 0-23)" +

+ +
+ +

Finding Clues If you follow the same advice as for the last problem, comment out line one, you will immediately get a different error message. Here's where you need to be very careful and not panic. The error message you get now is: NameError: name 'current_time_str' is not defined on line 4. You might be very tempted to think that this is somehow related to the earlier problem and immediately conclude that there is something wrong with the variable name current_time_str but if you reflect for a minute you will see that by commenting out line one you have caused a new and unrelated error. That is you have commented out the creation of the name current_time_str. So of course when you want to convert it to an int you will get the NameError. Yes, this can be confusing, but it will become much easier with experience. It's also important to keep calm, and evaluate each new clue carefully so you don't waste time chasing problems that are not really there.

Uncomment line 1 and you are back to the ParseError. Another track is to eliminate a possible source of error. Rather than commenting out the entire line you might just try to assign current_time_str to a constant value. For example you might make line one look like this: current_time_str = "10" #input("What is the "current time" (in hours 0-23)?"). Now you have assigned current_time_str to the string 10, and commented out the input statement. And now the program works! So you conclude that the problem must have something to do with the input function.

+ TypeError

TypeErrors occur when you you try to combine two objects that are not compatible. For example you try to add together an integer and a string. Usually type errors can be isolated to lines that are using mathematical operators, and usually the line number given by the error message is an accurate indication of the line.

Here's an example of a type error created by a Polish learner. See if you can find and fix the error.

- + + a = input('wpisz godzine') x = input('wpisz liczbe godzin') @@ -310,8 +322,7 @@ print ('godzina teraz', a) - - Solution +

In finding this error there are few lessons to think about. First, you may find it very disconcerting that you cannot understand the whole program. Unless you speak Polish then this won't be an issue. But, learning what you can ignore, and what you need to focus on is a very important part of the debugging process. Second, types and good variable names are important and can be very helpful. In this case a and x are not particularly helpful names, and in particular they do not help you think about the types of your variables, which as the error message implies is the root of the problem here. The rest of the lessons we will get back to in a minute.

The error message provided to you gives you a pretty big hint. TypeError: unsupported operand type(s) for FloorDiv: 'str' and 'number' on line: 5 On line five we are trying to use integer division on x and 24. The error message tells you that you are tyring to divide a string by a number. In this case you know that 24 is a number so x must be a string. But how? You can see the function call on line 3 where you are converting x to an integer. int(x) or so you think. This is lesson three and is one of the most common errors we see in introductory programming. What is the difference between int(x) and x = int(x)

@@ -325,14 +336,20 @@ print ('godzina teraz', a)

So, the solution to this problem is to change lines 3 and 4 so they are assignment statements.

-
+
+

Finding Clues One thing that can help you in this situation is to print out the values and the types of the variables involved in the statement that is causing the error. You might try adding a print statement after line 4 print(x, type(x)) You will see that at least we have confirmed that x is of type string. Now you need to start to work backward through the program. You need to ask yourself, where is x used in the program? x is used on lines 2, 3, and of course 5 and 6 (where we are getting an error). So maybe you move the print statement to be after line 2 and again after 3. Line 3 is where you expect the value of x to be changed to an integer. Could line 4 be mysteriously changing x back to a string? Not very likely. So the value and type of x is just what you would expect it to be after line 2, but not after line 3. This helps you isolate the problem to line 3. In fact if you employ one of our earlier techniques of commenting out line 3 you will see that this has no impact on the error, and is a big clue that line 3 as it is currently written is useless.

+ + + NameError

Name errors almost always mean that you have used a variable before it has a value. Often NameErrors are simply caused by typos in your code. They can be hard to spot if you don't have a good eye for catching spelling mistakes. Other times you may simply mis-remember the name of a variable or even a function you want to call. You have seen one example of a NameError at the beginning of this section. Here is another one. See if you can get this program to run successfully:

- + + + str_time = input("What time is it now?") str_wait_time = input("What is the number of nours to wait?") @@ -344,11 +361,15 @@ print(time_when_alarm_go_off) - - Solution + +

In this example, the student seems to be a fairly bad speller, as there are a number of typos to fix. The first one is identified as wait_time is not defined on line 6. Now in this example you can see that there is str_wait_time on line 2, and wai_time on line 4 and wait_time on line 6. If you do not have very sharp eyes its easy to miss that there is a typo on line 4.

-
+
+
+ + +

Finding Clues With name errors one of the best things you can do is use the editor, or browser search function. Quite often if you search for the exact word in the error message one of two things will happen:

    @@ -361,7 +382,8 @@ print(time_when_alarm_go_off)

Here is another one for you to try:

- + + n = input("What time is it now (in hours)?") n = imt(n) @@ -371,25 +393,29 @@ q = m % 12 print("The time is now", q) + + - - Solution +

This one is once again a typo, but the typo is not in a variable name, but rather, the name of a function. The search strategy would help you with this one easily, but there is another clue for you as well. The editor in the textbook, as well as almost all Python editors in the world provide you with color clues. Notice that on line 2 the function imt is not highlighted blue like the word int on line 4.

-
-
+ +
+

And one last bit of code to fix.

- + + -present_time = input("Enter the present timein hours:") +present_time = input("Enter the present time in hours:") set_alarm = input("Set the hours for alarm:") int (present_time, set_time, alarm_time) alarm_time = present_time + set_alarm print(alarm_time) + + + - - Solution

In this example the error message is about set_time not defined on line 3. In this case the undefined name is not used in an assignment statement, but is used as a parameter (incorrectly) to a function call. A search on set_time reveals that in fact it is only used once in the program. Did the author mean set_alarm? If we make that assumption we immediately get another error NameError: name 'alarm_time' is not defined on line: 3. The variable alarm_time is defined on line 4, but that does not help us on line 3. Furthermore we now have to ask the question is this function call int(present_time, set_alarm, alarm_time) even the correct use of the int function? The answer to that is a resounding no. Let's list all of the things wrong with line 3:

    @@ -407,8 +433,8 @@ print(alarm_time)

-
+
@@ -419,10 +445,12 @@ print(alarm_time)
+ ValueError

Value errors occur when you pass a parameter to a function and the function is expecting a certain limitations on the values, and the value passed is not compatible. We can illustrate that with this particular program in two different ways.

- + + current_time_str = input("What is the current time (in hours 0-23)?") current_time_int = int(current_time_str) diff --git a/pretext/Debugging/intro-HowtobeaSuccessfulProgrammer.ptx b/pretext/Debugging/intro-HowtobeaSuccessfulProgrammer.ptx index 6f79d7f52..2d8fd2a2e 100644 --- a/pretext/Debugging/intro-HowtobeaSuccessfulProgrammer.ptx +++ b/pretext/Debugging/intro-HowtobeaSuccessfulProgrammer.ptx @@ -1,6 +1,6 @@
How to be a Successful Programmer -

One of the most important skills you need to aquire to complete this book successfully is the ability to debug your programs. Debugging might be the most under-appreciated, and under-taught, skill in introductory computer science. For that reason we are introducing a series of debugging interludes. Debugging is a skill that you need to master over time, and some of the tips and tricks are specific to different aspects of Python programming. So look for additional debugging interludes throughout the rest of this book.

+

One of the most important skills you need to acquire to complete this book successfully is the ability to debug your programs. Debugging might be the most under-appreciated, and under-taught skill in introductory computer science. For that reason we are including this debugging interlude. Debugging is a skill that you need to master over time, and some of the tips and tricks are specific to different aspects of Python programming.

Programming is an odd thing in a way. Here is why. As programmers we spend 99% of our time trying to get our program to work. We struggle, we stress, we spend hours deep in frustration trying to get our program to execute correctly. Then when we do get it going we celebrate, hand it in, and move on to the next homework assignment or programming task. But here is the secret, when you are successful, you are happy, your brain releases a bit of chemical that makes you feel good. You need to organize your programming so that you have lots of little successess. It turns out your brain doesn't care all that much if you have successfully written hello world, or a fast fourier transform (trust me its hard) you still get that little release that makes you happy. When you are happy you want to go on and solve the next little problem. Essentially I'm telling you once again, start small, get something small working, and then add to it.

diff --git a/pretext/Debugging/toctree.ptx b/pretext/Debugging/toctree.ptx index fafd30276..2f03e1bcc 100644 --- a/pretext/Debugging/toctree.ptx +++ b/pretext/Debugging/toctree.ptx @@ -1,10 +1,9 @@ -4Debugging Interlude 1 +4Debugging Interlude 4 4 4 4 4 -4 diff --git a/pretext/Dictionaries/Dictionaryoperations.ptx b/pretext/Dictionaries/Dictionaryoperations.ptx index fd1e32bc5..3d5629d27 100644 --- a/pretext/Dictionaries/Dictionaryoperations.ptx +++ b/pretext/Dictionaries/Dictionaryoperations.ptx @@ -4,7 +4,7 @@

The del statement removes a key-value pair from a dictionary. For example, the following dictionary contains the names of various fruits and the number of each fruit in stock. If someone buys all of the pears, we can remove the entry from the dictionary.

- + inventory = {'apples': 430, 'bananas': 312, 'oranges': 525, 'pears': 217} @@ -16,7 +16,7 @@ be modified by referencing an association on the left hand side of the assignment statement. In the previous example, instead of deleting the entry for pears, we could have set the inventory to 0.

- + inventory = {'apples': 430, 'bananas': 312, 'oranges': 525, 'pears': 217} @@ -25,7 +25,7 @@

Similarily, a new shipment of 200 bananas arriving could be handled like this.

- + inventory = {'apples': 430, 'bananas': 312, 'oranges': 525, 'pears': 217} inventory['bananas'] = inventory['bananas'] + 200 diff --git a/pretext/Dictionaries/intro-Dictionaries.ptx b/pretext/Dictionaries/intro-Dictionaries.ptx index 5caac95fc..58eced4aa 100644 --- a/pretext/Dictionaries/intro-Dictionaries.ptx +++ b/pretext/Dictionaries/intro-Dictionaries.ptx @@ -15,7 +15,7 @@

One way to create a dictionary is to start with the empty dictionary and add key-value pairs. The empty dictionary is denoted {}

- + eng2sp = {} eng2sp['one'] = 'uno' @@ -35,7 +35,7 @@ For our purposes we can think of this ordering as unpredictable.

Another way to create a dictionary is to provide a list of key-value pairs using the same syntax as the previous output.

- + eng2sp = {'three': 'tres', 'one': 'uno', 'two': 'dos'} print(eng2sp) @@ -46,7 +46,7 @@ accessed with keys, not with indices, so there is no need to care about ordering.

Here is how we use a key to look up the corresponding value.

- + eng2sp = {'three': 'tres', 'one': 'uno', 'two': 'dos'} diff --git a/pretext/Figures/ExtraTopics/Figures/complexity.png b/pretext/Figures/ExtraTopics/Figures/complexity.png new file mode 100644 index 000000000..5914ca96d Binary files /dev/null and b/pretext/Figures/ExtraTopics/Figures/complexity.png differ diff --git a/pretext/Files/AlternativeFileReadingMethods.ptx b/pretext/Files/AlternativeFileReadingMethods.ptx index 494965c5c..62d1c7908 100644 --- a/pretext/Files/AlternativeFileReadingMethods.ptx +++ b/pretext/Files/AlternativeFileReadingMethods.ptx @@ -20,26 +20,26 @@ of readline this moves the marker to the first character of the next line in the file. In the case of read or readlines the marker is moved to the end of the file.

-
>>> infile = open("ccdata.txt", "r")
->>> aline = infile.readline()
->>> aline
+  
>>> with open("ccdata.txt", "r") as infile:
+>>>  aline = infile.readline()
+>>>  aline
 '1850\-0.37\2.24E-7\n'
 >>>
->>> infile = open("ccdata.txt", "r")
->>> linelist = infile.readlines()
->>> print(len(linelist))
+>>> with open("ccdata.txt", "r") as infile:
+>>>  linelist = infile.readlines()
+>>>  print(len(linelist))
 18
->>> print(linelist[0:4])
+>>>  print(linelist[0:4])
 ['1850\-0.37\2.24E-7\n',
  '1860\-0.34\3.94E-7\n',
  '1870\-0.28\6.6E-7\n',
  '1880\-0.24\1.1\n']
 >>>
->>> infile = open("ccdata.txt", "r")
->>> filestring = infile.read()
->>> print(len(filestring))
+>>> with open("ccdata.txt", "r") as infile:
+>>>  filestring = infile.read()
+>>>  print(len(filestring))
 1282
->>> print(filestring[:256])
+>>>  print(filestring[:256])
 1850  -0.37  2.24E-7
 1860  -0.34  3.94E-7
 1870  -0.28  6.6E-7
@@ -120,7 +120,7 @@
     
   
   

Now let's look at another method of reading our file using a while loop. This is important because many other programming languages do not support the for loop style for reading files but they do support the pattern we'll show you here.

- + infile = open("ccdata.txt", "r") line = infile.readline() diff --git a/pretext/Files/FindingaFileonyourDisk.ptx b/pretext/Files/FindingaFileonyourDisk.ptx index 0f4e8a364..e0bc90208 100644 --- a/pretext/Files/FindingaFileonyourDisk.ptx +++ b/pretext/Files/FindingaFileonyourDisk.ptx @@ -30,9 +30,9 @@

You can access files in sub-folders, also called directories, under your home directory by adding a slash and the name of the folder. For example, if you had a file - called hello.py in a folder called CS150 that is inside a folder called - PyCharmProjects under your home directory, then the full name for the file - hello.py is /Users/yourname/PyCharmProjects/CS150/hello.py. + called hello.py in a folder called CS128 that is inside a folder called + VSCodeProjects under your home directory, then the full name for the file + hello.py is /Users/yourname/VSCodeProjects/CS128/hello.py. This is called an absolute file path. An absolute file path typically only works on a specific computer. Think about it for a second. What other computer in the world is going to have an absolute file path that starts with diff --git a/pretext/Files/Glossary.ptx b/pretext/Files/Glossary.ptx index a898e32ad..8cee89cbb 100644 --- a/pretext/Files/Glossary.ptx +++ b/pretext/Files/Glossary.ptx @@ -26,6 +26,26 @@ write

Will add characters to the end of a file that has been opened for writing.

+ + + csv +

An optional Python module that provides useful functions for interacting with data in the Comma Separated Value format.

+
+ + Comma Separated Value +

A format that text files can have. Promises that the file contains alphanumeric values separate by single commas and organized into one row of data per line.

+
+ + reader +

A function part of the csv module that creates an object for traversing csv file data.

+
+ + writerow +

A function part of the csv module that takes a list of data, formats it in the appropriate csv format, and writes it to a data file.

+
+ + writerows +

A function part of the csv module. Similar to writerow, but can write multiple rows of data at once if given a list of lists.

absolute file path diff --git a/pretext/Files/Iteratingoverlinesinafile.ptx b/pretext/Files/Iteratingoverlinesinafile.ptx index da9ccf0f7..03d6492b9 100644 --- a/pretext/Files/Iteratingoverlinesinafile.ptx +++ b/pretext/Files/Iteratingoverlinesinafile.ptx @@ -1,8 +1,27 @@
Iterating over lines in a file -

Recall the contents of the ccdata.txt file.

- + +

As an example, suppose we have a text file called ccdata.txt that contains + the following data representing statistics about climate change. Although it + would be possible to consider entering this data by hand each time it is used, + you can imagine that it would be time-consuming and error-prone to do this. In + addition, it is likely that there could be data from more sources and + other years. The format of the data file is as follows:

+
Year, Global Average Temperature, Global Emmision of CO2
+ + +

To open this file, we would call the open function. The variable, + fileref, now holds a reference to the file object returned by + open. Again, after the file is closed any further attempts to + use fileref will result in an error.

+
with open("ccdata.txt", "r") as fileref:
+  # do things
+ + + + +

We will now use this file as input in a program that will do some data processing. In the program, we will read each line of the file and print it with some additional text. Because text files are sequences of @@ -44,16 +61,13 @@ the split method, we can break each line into a list containing all the fields of interest about climate change. We can then take the values corresponding to year, global average temperature, and global emmisions to construct a simple sentence.

- + -ccfile = open("ccdata.txt", "r") - -for aline in ccfile: - values = aline.split() - print('In', values[0], 'the average temp. was', values[1], '°C and CO2 emmisions were', values[2], 'gigatons.') - -ccfile.close() - +with open("ccdata.txt", "r") as ccfile: + for aline in ccfile: + values = aline.split() + print('In', values[0], 'the average temp. was', values[1], '°C and CO2 emmisions were', values[2], 'gigatons.') +

You can obtain a line from the keyboard with the input function, and you can process lines of a file. diff --git a/pretext/Files/ReadingCSV.ptx b/pretext/Files/ReadingCSV.ptx new file mode 100644 index 000000000..c12bea915 --- /dev/null +++ b/pretext/Files/ReadingCSV.ptx @@ -0,0 +1,66 @@ + +

+ Reading Files as <c>csv</c> +

The climate change data file we have used as an example in this chapter has a more specific designation than just a .txt file. Because the file + consists of a series of values separated by commas, it can also be referred to as a Comma Separate Value file, or a .csv. Note that csv + files are still just text files, but knowing it is also a csv tells us something valuable about the structure of the data it contains. This makes it + easier to parse and interact with that data.

+ +

To interact with text files as csv's, we need to import the Python csv module and use it to create reader or writer objects. + This section focuses on reading csv, so we should use a reader. The reason we are interested in doing this is because the reader will automatically detect and remove + the commas separating values for us. Essentially, the reader will split the data up into lists for us. This makes it simpler to navigate and interact with the data in the file.

+

Let's see an example of this working.

+ + +import csv +with open("ccdata.csv", "r") as file: + csv_reader = csv.reader(file, delimiter=',') + for line in csv_reader: + print(line) + print(line[0]) # just years + + + +

Note that the value assigned to for loop line variable is a list rather than a single string like we would get with a standard file object. This allows us to acces column + data quickly with a single index value.

+ +

The following table describes the two csv functions you are most likely to want to use.

+
+ + + + Method Name + + + Use + + + Explanation + + + + + reader + + + csv.reader(file_object) + + + Creates a csv reader object that automatically splits lines of data but can be looped through like a regular file object. + + + + + writer + + + csv.writer(file_object) + + + Creates a csv writer object that takes lists of strings and writes them to the provided file in the correct csv format. + + + +
+
diff --git a/pretext/Files/WithStatements.ptx b/pretext/Files/WithStatements.ptx index d356755b3..b45707957 100644 --- a/pretext/Files/WithStatements.ptx +++ b/pretext/Files/WithStatements.ptx @@ -1,26 +1,79 @@
With Statements - -

This section is a bit of an advanced topic and can be easily skipped. But with statements are becoming very common and it doesn't hurt to know about them in case you run into one in the wild.

-
-

Now that you have seen and practiced a bit with opening and closing files, there is another mechanism that Python provides for us that cleans up the often forgotten close. Forgetting to close a file does not necessarily cause a runtime error in the kinds of programs you typically write in an introductory CS course. However if you are writing a program that may run for days or weeks at a time that does a lot of file reading and writing you may run into trouble.

-

In version 2.5 Python introduced the concept of a context manager. The context manager automates the process of doing common operations at the start of some task, as well as automating certain operations at the end of some task. In the context of reading and writing a file, the normal operation is to open the file and assign it to a variable. At the end of working with a file the common operation is to make sure that file is closed.

-

The Python with statement makes using context managers easy. The general form of a with statement is:

-
with <create some object that understands context> as <some name>:
-    do some stuff with the object
+  
+  
+  
+  

In order to open files easily in Python, we can use the following form:

+
with <some open statement> as <variable name for the file>:
+    do some stuff with the file
     ...
-

When the program exits the with block, the context manager handles the common stuff that normally happens. For example closing a file. A simple example will clear up all of this abstract discussion of contexts.

+

When the program exits the with block, the file is automatically closed. Consider the following example:

- - + + -with open('mydata.txt') as md: - print(md) - for line in md: +with open('mydata.txt') as myfile: + print(myfile) + for line in myfile: print(line) -print(md) +print(myfile) -

The first line of the with statement opens the file and assigns it to md then we can iterate over the file in any of the usual ways. and when we are done we simply stop indenting and let Python take care of closing the file and cleaning up.

+

The first line of the with statement opens the file and assigns it to myfile, then we can iterate over each line in the file. When we are done, we simply stop indenting and let Python take care of closing the file and cleaning up.

+

A word of caution: once you read all the lines from a file, it is "spent"; + that is to say, if we tried to read from myfile again in the example above, it would not + work. We would have to open the file again to read again.

+ +

In this course, you likely will not see the following syntax, but it's still useful to know that it's out there:

+

In Python, we can call the open function to open files before we can use them and the close function to close them when we are done with them. As you might expect, once a file is opened it becomes a Python object just like all other data. shows the functions and methods that can be used to open and close files.

+ + + + + Method Name + + + Use + + + Explanation + + + + + open + + + open(filename,'r') + + + Open a file called filename and use it for reading. This will return a reference to a file object. + + + + + open + + + open(filename,'w') + + + Open a file called filename and use it for writing. This will also return a reference to a file object. + + + + + close + + + filevariable.close() + + + File use is complete. + + + +
+
diff --git a/pretext/Files/WritingCSV.ptx b/pretext/Files/WritingCSV.ptx new file mode 100644 index 000000000..fdd8682d4 --- /dev/null +++ b/pretext/Files/WritingCSV.ptx @@ -0,0 +1,44 @@ + +
+ Writing Files as <c>csv</c> +

We already introduced the csv module in the previous section. Let's examine some examples using a csv.writer object.

+

Like the reader object, the csv.writer function takes a opened file object. Once set up, the csv file can be written to with writerow. + Make note that writerow does not need an explicit newline character \n to know when to end a line of text. This sets it apart for the the regular file.write() function.

+ + +import csv +data = [["Column_1_Name","Column_2_Name","Column_3_Name"], + [1,2,3], + [50,40,23] +] + +with open("new_file.csv", "w", newline='') as file: + csv_writer = csv.writer(file, delimiter=',') + for line in data: + csv_writer.writerow(line) + + +

This program will create a file called new_file.csv with the contents:

+
Column_1_Name,Column_2_Name,Column_3_Name
+1,2,3
+50,40,23
+

You may have noticed there is one new piece of code in our with open(... line. The newline='' parameter is needed here to avoid adding extra blank lines to our output file. + Try running this code locally without that part to see the difference.

+ +

There is one other writer method you may find useful, which is writerows. This allows you to write multiple rows of data at the same time. When applied to the previous + example, it saves us from having to set up a loop.

+ + +import csv + +data = [["Column_1_Name","Column_2_Name","Column_3_Name"], + [1,2,3], + [50,40,23] +] + +with open("new_file.csv", "w", newline='') as file: + csv_writer = csv.writer(file, delimiter=',') + csv_writer.writerows(data) + + +
diff --git a/pretext/Files/WritingTextFiles.ptx b/pretext/Files/WritingTextFiles.ptx index 4608f27c9..48ba0c089 100644 --- a/pretext/Files/WritingTextFiles.ptx +++ b/pretext/Files/WritingTextFiles.ptx @@ -9,38 +9,32 @@

To construct this file, we will approach the problem using a similar algorithm as above. After opening the file, we will iterate through the lines, break each line into its parts, choose the parts that we need, and then output them. Eventually, the output will be written to a file.

The program below solves part of the problem. Notice that it reads the data and creates a string consisting of the year of the climate change followed by the global emission. In this example, we simply print the lines as they are created.

- + -infile = open("ccdata.txt", "r") -aline = infile.readline() -print("Year\tEmmision\n") -while aline: - items = aline.split() - dataline = items[0] + '\t' + items[2] - print(dataline) +with open("ccdata.txt", "r") as infile: aline = infile.readline() - -infile.close() - + print("Year\tEmmision\n") + while aline: + items = aline.split() + dataline = items[0] + '\t' + items[2] + print(dataline) + aline = infile.readline() +

When we run this program, we see the lines of output on the screen. Once we are satisfied that it is creating the appropriate output, the next step is to add the necessary pieces to produce an output file and write the data lines to it. To start, we need to open a new output file by adding another call to the open function, outfile = open("emissiondata.txt",'w'), using the 'w' flag. We can choose any file name we like. If the file does not exist, it will be created. However, if the file does exist, it will be reinitialized as empty and you will lose any previous contents.

Once the file has been created, we just need to call the write method passing the string that we wish to add to the file. In this case, the string is already being printed so we will just change the print into a call to the write method. However, there is one additional part of the data line that we need to include. The newline character needs to be concatenated to the end of the line. The entire line now becomes outfile.write(dataline + '\n'). We also need to close the file when we are done.

The complete program is shown below.

- + -infile = open("ccdata.txt", "r") -outfile = open("emissiondata.txt", "w") - -aline = infile.readline() -outfile.write("Year \tEmmision\n") -while aline: - items = aline.split() - dataline = items[0] + '\t' + items[2] - outfile.write(dataline + '\n') - aline = infile.readline() - -infile.close() -outfile.close() - +with open("ccdata.txt", "r") as infile: + with open("emissiondata.txt", "w") as outfile: + aline = infile.readline() + outfile.write("Year \tEmmision\n") + while aline: + items = aline.split() + dataline = items[0] + '\t' + items[2] + outfile.write(dataline + '\n') + aline = infile.readline() +
diff --git a/pretext/Files/intro-WorkingwithDataFiles.ptx b/pretext/Files/intro-WorkingwithDataFiles.ptx index 2d7c753bb..e43b1797e 100644 --- a/pretext/Files/intro-WorkingwithDataFiles.ptx +++ b/pretext/Files/intro-WorkingwithDataFiles.ptx @@ -1,55 +1,7 @@
Working with Data Files -

So far, the data we have used in this book have all been either coded right into the program, or have been entered by the user. In real life data reside in files. For example the images we worked with in the image processing unit ultimately live in files on your hard drive. Web pages, and word processing documents, and music are other examples of data that live in files. In this short chapter we will introduce the Python concepts necessary to use data from files in our programs.

+

So far, the data we have used in this book have all been either coded right into the program, or have been entered by the user. In real life, data reside in files.

+

For example, web pages, word processing documents, and all data that live in files. In this chapter we will introduce the Python concepts necessary to use data from files in our programs.

For our purposes, we will assume that our data files are text files–that is, files filled with characters. The Python programs that you write are stored as text files. We can create these files in any of a number of ways. For example, we could use a text editor to type in and save the data. We could also download the data from a website and then save it in a file. Regardless of how the file is created, Python will allow us to manipulate the contents.

-

In Python, we must open files before we can use them and close them when we are done with them. As you might expect, once a file is opened it becomes a Python object just like all other data. shows the functions and methods that can be used to open and close files.

- - - - - Method Name - - - Use - - - Explanation - - - - - open - - - open(filename,'r') - - - Open a file called filename and use it for reading. This will return a reference to a file object. - - - - - open - - - open(filename,'w') - - - Open a file called filename and use it for writing. This will also return a reference to a file object. - - - - - close - - - filevariable.close() - - - File use is complete. - - - -
diff --git a/pretext/Files/toctree.ptx b/pretext/Files/toctree.ptx index a65214c9b..152ce4715 100644 --- a/pretext/Files/toctree.ptx +++ b/pretext/Files/toctree.ptx @@ -3,12 +3,13 @@ 4Files 4 4 -4 +4 + 4 4 4 -4 -4 + 4 -4 +4 diff --git a/pretext/Files/video-files.ptx b/pretext/Files/video-files.ptx new file mode 100644 index 000000000..57c4a08a0 --- /dev/null +++ b/pretext/Files/video-files.ptx @@ -0,0 +1,7 @@ + +
+ Practice Video Lecture +

Week 10 Video Lecture:

+
diff --git a/pretext/Functions/BooleanFunctions.ptx b/pretext/Functions/BooleanFunctions.ptx new file mode 100644 index 000000000..54b8f100d --- /dev/null +++ b/pretext/Functions/BooleanFunctions.ptx @@ -0,0 +1,163 @@ + +
+ Boolean Functions +

We have already seen that boolean values result from the evaluation of boolean expressions. Since the result of any + expression evaluation can be returned by a function (using the return statement), + functions can return boolean values. This turns out to be a very convenient way to hide the details of complicated tests. For example:

+ + + +def isDivisible(x, y): + if x % y == 0: + result = True + else: + result = False + + return result + +print(isDivisible(10, 5)) + + +

The name of this function is isDivisible. It is common to give boolean + functions names that sound like yes/no questions. isDivisible returns + either True or False to indicate whether the x is or is not + divisible by y. +

+

We can make the function more concise by taking advantage of the fact that the + condition of the if statement is itself a boolean expression. We can return + it directly, avoiding the if statement altogether:

+ + +def isDivisible(x, y): + return x % y == 0 + + +

Boolean functions are often used in conditional statements:

+ + +if isDivisible(x, y): + ... # do something ... +else: + ... # do something else ... + + +

It might be tempting to write something like + if isDivisible(x, y) == True: + but the extra comparison is redundant. You only need an == expression if you are comparing some other type than boolean. (isDivisible(x, y) == False can also be made more concise as + not isDivisible(x, y)). The following example shows the isDivisible function at work. Notice how + descriptive the code is when we move the testing details into a boolean function. Try it + with a few other actual parameters to see what is printed.

+ + +def isDivisible(x, y): + return x % y == 0 + +if isDivisible(10, 5): + print("That works") +else: + print("Those values are no good") + + +

Here is the same program in codelens. When we evaluate the if statement in the main part of the program, the evaluation of + the boolean expression causes a call to the isDivisible function. This is very easy to see in codelens.

+ + + def isDivisible(x, y): + return x % y == 0 + + if isDivisible(10, 5): + print("That works") + else: + print("Those values are no good") + + +

+ Check your understanding +

+ + +

What is a Boolean function?

+
+ + + +

A function that returns True or False

+
+ + A Boolean function is just like any other function, but it always returns True or False. + +
+ + +

A function that takes True or False as an argument

+
+ + A Boolean function may take any number of arguments (including 0, though that is rare), of any type. + +
+ + +

The same as a Boolean expression

+
+ + A Boolean expression is a statement that evaluates to True or False, e.g. 5+3==8. A function is a series of expressions grouped together with a name that are only executed when you call the function. + +
+
+
+ + +

Is the following statement legal in a Python function (assuming x, y and z are defined to be numbers)?

+ + +return x + y < z + + +
+ + + +

Yes

+
+ + It is perfectly valid to return the result of evaluating a Boolean expression. + +
+ + +

No

+
+ + x +y < z is a valid Boolean expression, which will evaluate to True or False. It is perfectly legal to return True or False from a function, and to have the statement to be evaluated in the same line as the return keyword. + +
+
+
+ + More Unit Testing +

When we write unit tests, we should also consider output equivalence classes that result in significantly different results.

+

The isDivisible function can return either True or False. These two different outputs give us two equivalence classes. We then choose inputs that should give each of the different results. It is important to have at least one test for each output equivalence class. +

+ + +def isDivisible(x, y): + '''is x evenly divisible by y?''' + return x % y == 0 + +if __name__ == "__main__": + import test + + + + Extend the program … +

Starting on line 7, write two unit tests (that should pass), one for each output equivalence class.

+
+

This workspace is provided for your convenience. You can use this activecode window to try out anything you like.

+ + + + + + +
+
diff --git a/pretext/Functions/Composition.ptx b/pretext/Functions/Composition.ptx index 182f5dfeb..7f5ce7620 100644 --- a/pretext/Functions/Composition.ptx +++ b/pretext/Functions/Composition.ptx @@ -24,7 +24,7 @@ return result

Wrapping that up in a function, we get:

- + def distance(x1, y1, x2, y2): dx = x2 - x1 diff --git a/pretext/Functions/Exercises.ptx b/pretext/Functions/Exercises.ptx index 8f9b97277..598405787 100644 --- a/pretext/Functions/Exercises.ptx +++ b/pretext/Functions/Exercises.ptx @@ -2,193 +2,12 @@ Exercises - -

Use the drawsquare function we wrote in this chapter in a program to draw - the image shown below. - Assume each side is 20 units. - (Hint: notice that the turtle has already moved away from the ending point of the last - square when the program ends.)

- -
- - - -import turtle - -def drawSquare(t, sz): - """Get turtle t to draw a square of sz side""" - - for i in range(4): - t.forward(sz) - t.left(90) - -wn = turtle.Screen() -wn.bgcolor("lightgreen") - -alex = turtle.Turtle() -alex.color("pink") - -drawSquare(alex,20) - -wn.exitonclick() - - - - - -import turtle - -def drawSquare(t, sz): - """Make turtle t draw a square of with side sz.""" - for i in range(4): - t.forward(sz) - t.left(90) - -wn = turtle.Screen() # Set up the window and its attributes -wn.bgcolor("lightgreen") - -alex = turtle.Turtle() # create alex -alex.color('hotpink') -alex.pensize(3) - -for i in range(5): - drawSquare(alex, 20) # Call the function to draw the square - alex.penup() - alex.forward(40) # move alex to the starting position for the next square - alex.pendown() - -wn.exitonclick() - - - -
- - -

Write a program to draw this. Assume the innermost square is 20 units per side, - and each successive square is 20 units bigger, per side, than the one inside it.

- -
- - - - - -
- - -

Write a non-fruitful function drawPoly(someturtle, somesides, somesize) which makes a turtle - draw a regular polygon. - When called with drawPoly(tess, 8, 50), it will draw a shape like this:

- -
- - - - - - - - -import turtle - -def drawPoly(t, num_sides, side_length): - for i in range(num_sides): - t.forward(side_length) - t.left(360/num_sides) - -wn = turtle.Screen() # Set up the window and its attributes -wn.bgcolor("lightgreen") - -tess = turtle.Turtle() -tess.color('hotpink') -tess.pensize(3) - -drawPoly(tess, 8, 50) - - - -
- - -

Draw this pretty pattern.

- -
- - - - - -
- - -

The two spirals in this picture differ only by the turn angle. Draw both.

- -
- - - - - - - - -import turtle - -def drawSpiral(t, angle): - ''' takes a turtle, t, and an angle in degrees ''' - length = 1 - for i in range(84): - t.forward(length) - t.right(angle) - length = length + 2 - - -wn = turtle.Screen() # Set up the window and its attributes -wn.bgcolor("lightgreen") - -guido = turtle.Turtle() # create guido -guido.color('blue') - -## draw the first spiral ## -# position guido -guido.penup() -guido.backward(110) -guido.pendown() - -# draw the spiral using a 90 degree turn angle -drawSpiral(guido, 90) - - -## draw the second spiral ## -# position guido -guido.home() -guido.penup() -guido.forward(90) -guido.pendown() - -drawSpiral(guido, 89) - - - -
- - -

Write a non-fruitful function drawEquitriangle(someturtle, somesize) which calls drawPoly from the - previous question to have its turtle draw a equilateral triangle.

-
- - - - - -
-

Write a fruitful function sumTo(n) that returns the sum of all integer numbers up to and including n. So sumTo(10) would be 1+2+3...+10 which would return the value 55. Use the equation (n * (n + 1)) / 2.

- + def sumTo(n): @@ -207,7 +26,7 @@ myTests().main() - + from test import testEqual @@ -226,11 +45,11 @@ print("The sum from 1 to 5 is",t)
- +

Write a function areaOfCircle(r) which returns the area of a circle of radius r. Make sure you use the math module in your solution.

- + def areaOfCircle(r): @@ -250,90 +69,12 @@ myTests().main()
- - -

Write a non-fruitful function to draw a five pointed star, where the length of each side is 100 units.

- -
- - - - - - - - -import turtle - -def drawFivePointStar(t): - for i in range(5): - t.forward(100) - t.left(216) - -wolfram = turtle.Turtle() -drawFivePointStar(wolfram) - - - -
- - -

Extend your program above. Draw five stars, but between each, pick up the pen, - move forward by 350 units, turn right by 144, put the pen down, and draw the next star. - You'll get something like this (note that you will need to move to the left before drawing your first star in order to fit everything in the window):

- -

What would it look like if you didn't pick up the pen?

-
- - - - - -
- - -

Extend the star function to draw an n pointed star. (Hint: n must be an odd number greater or - equal to 3).

-
- - - - - - - - -import turtle - -def drawStar(t, n): - for i in range(n): - t.forward(100) - t.left(180 - 180/n) - -stroustrup = turtle.Turtle() -drawStar(stroustrup, 7) - - - -
- - -

Write a function called drawSprite that will draw a sprite. The function will need parameters for - the turtle, the number of legs, and the length of the legs. Invoke the function to create a sprite - with 15 legs of length 120.

-
- - - - - -
- +

Rewrite the function sumTo(n) that returns the sum of all integer numbers up to and including n. This time use the accumulator pattern.

- + def sumTo(n): @@ -351,7 +92,7 @@ myTests().main() - + def sumTo(n): sum = 0 @@ -370,37 +111,12 @@ print("The sum from 1 to 5 is",t)
- - -

Write a function called mySqrt that will approximate the square root of a number, call it n, by using - Newton's algorithm. - Newton's approach is an iterative guessing algorithm where the initial guess is n/2 and each subsequent guess - is computed using the formula: newguess = (1/2) * (oldguess + (n/oldguess)).

-
- - - -def mySqrt(n): - # your code here ==== -from unittest.gui import TestCaseGui - -class myTests(TestCaseGui): - def testOne(self): - self.assertAlmostEqual(mySqrt(4.0),2.0,0,"Tested mySqrt on input 4.0") - self.assertAlmostEqual(mySqrt(9.0),3.0,4,"Tested accuracy of mySqrt on input 3.0") - self.assertAlmostEqual(mySqrt(36.0),6.0,5,"Tested accuracy of mySqrt on input 6.0") - self.assertAlmostEqual(mySqrt(100.0),10.0,4,"Tested accuracy of mySqrt on input 10.0. Try iterating more times.") - -myTests().main() - - -
- +

Write a function called myPi that will return an approximation of PI (3.14159…). Use the Leibniz approximation.

- + def myPi(iters): @@ -411,7 +127,7 @@ myTests().main() - + def myPi(iters): ''' Calculate an approximation of PI using the Leibniz @@ -433,12 +149,12 @@ print(pi_approx)
- +

Write a function called myPi that will return an approximation of PI (3.14159…). Use the Madhava approximation.

- + def myPi(iters): @@ -449,92 +165,4 @@ def myPi(iters):
- - -

Write a function called fancySquare that will draw a square with fancy corners (sprites on the corners). You should - implement and use the drawSprite function from above. For an even more interesting look, how about adding small - triangles to the ends of the sprite legs.

-
- - - - - - - - -import turtle - -def drawSprite(t, numlegs, leglength): - angle = 360/numlegs - for i in range(numlegs): - t.forward(leglength) - t.backward(leglength) - t.left(angle) - -def drawFancySquare(t, sz, lgs, lgl): - for i in range(4): - t.forward(sz) - drawSprite(t, lgs, lgl) - t.left(90) - -wn = turtle.Screen() -wn.bgcolor("lightgreen") - -alex = turtle.Turtle() -drawFancySquare(alex, 100, 10, 15) - -wn.exitonclick() - - - -
- - -

There was a whole program in to create a bar chart with specific data. Creating a bar chart is a useful idea in general. Write a non-fruitful function called barChart, that takes the numeric list of data as a parameter, and draws the bar chart. Write a full program calling this function. - The current version of the drawBar function unfortuately draws the top of the bar through the bottom of the label. A nice elaboration is to make the label appear completely above the top line. To keep the spacing consistent you might pass an extra parameter to drawBar for the distance to move up. For the barChart function make that parameter be some small fraction of maxheight+border. The fill action makes this modification particularly tricky: You will want to move past the top of the bar and write before or after drawing and filling the bar.

-
- - - -import turtle - -def drawBar(t, height): - """ Get turtle t to draw one bar, of height. """ - t.begin_fill() # start filling this shape - t.left(90) - t.forward(height) - t.write(str(height)) - t.right(90) - t.forward(40) - t.right(90) - t.forward(height) - t.left(90) - t.end_fill() # stop filling this shape - - - -xs = [48, 117, 200, 240, 160, 260, 220] # here is the data -maxheight = max(xs) -numbars = len(xs) -border = 10 - -wn = turtle.Screen() # Set up the window and its attributes -wn.setworldcoordinates(0-border, 0-border, 40*numbars+border, maxheight+border) -wn.bgcolor("lightgreen") - -tess = turtle.Turtle() # create tess and set some attributes -tess.color("blue") -tess.fillcolor("red") -tess.pensize(3) - - - -for a in xs: - drawBar(tess, a) - -wn.exitonclick() - - -
diff --git a/pretext/Functions/FlowofExecutionSummary.ptx b/pretext/Functions/FlowofExecutionSummary.ptx index 2c27227b0..b728f41b7 100644 --- a/pretext/Functions/FlowofExecutionSummary.ptx +++ b/pretext/Functions/FlowofExecutionSummary.ptx @@ -28,22 +28,22 @@

Check your understanding

- +

Consider the following Python code. Note that line numbers are included on the left.

-def pow(b, p): - y = b ** p - return y - -def square(x): - a = pow(x, 2) - return a - -n = 5 -result = square(n) -print(result) +def pow(b, p): #1 + y = b ** p #2 + return y #3 +#4 +def square(x): #5 + a = pow(x, 2) #6 + return a #7 +#8 +n = 5 #9 +result = square(n) #10 +print(result) #11

Which of the following best reflects the order in which these lines of code are processed in Python?

@@ -91,7 +91,7 @@ print(result)
- +

Consider the following Python code. Note that line numbers are included on the left.

diff --git a/pretext/Functions/Functionscancallotherfunctions.ptx b/pretext/Functions/Functionscancallotherfunctions.ptx index 47ae94418..d8d067edc 100644 --- a/pretext/Functions/Functionscancallotherfunctions.ptx +++ b/pretext/Functions/Functionscancallotherfunctions.ptx @@ -11,7 +11,7 @@ first function called square simply computes the square of a given number. The second function called sum_of_squares makes use of square to compute the sum of three numbers that have been squared.

- + def square(x): y = x * x @@ -41,100 +41,7 @@ for sum_of_squares. As you step through you will notice that x, and y are local variables in both functions and may even have different values. This illustrates that even though they are named the same, they are in fact, very different.

-

Now we will look at another example that uses two functions. This example illustrates an - important computer science problem solving technique called - generalization. Assume we want to write a - function to draw a square. The generalization step is to realize that a - square is just a special kind of rectangle.

-

To draw a rectangle we need to be able to call a function with different - arguments for width and height. Unlike the case of the square, - we cannot repeat the same thing 4 times, because the four sides are not equal. - However, it is the case that drawing the bottom and right sides are the - same sequence as drawing the top and left sides. So we eventually come up with - this rather nice code that can draw a rectangle.

- - -def drawRectangle(t, w, h): - """Get turtle t to draw a rectangle of width w and height h.""" - for i in range(2): - t.forward(w) - t.left(90) - t.forward(h) - t.left(90) - - -

The parameter names are chosen as single letters for conciseness. - In real programs, we will insist on better variable names than this. - The point is that the program doesn't understand that you're drawing a rectangle or that the - parameters represent the width and the height. Concepts like rectangle, width, and height are meaningful - for humans. They are not concepts that the program or the computer understands.

-

- Thinking like a computer scientist involves looking for patterns and - relationships. In the code above, we've done that to some extent. We did - not just draw four sides. Instead, we spotted that we could draw the - rectangle as two halves and used a loop to repeat that pattern twice.

-

But now we might spot that a square is a special kind of rectangle. A square - simply uses the same value for both the height and the width. - We already have a function that draws a rectangle, so we can use that to draw - our square.

- - -def drawSquare(tx, sz): # a new version of drawSquare - drawRectangle(tx, sz, sz) - - -

Here is the entire example with the necessary set up code.

- - -import turtle - -def drawRectangle(t, w, h): - """Get turtle t to draw a rectangle of width w and height h.""" - for i in range(2): - t.forward(w) - t.left(90) - t.forward(h) - t.left(90) - -def drawSquare(tx, sz): # a new version of drawSquare - drawRectangle(tx, sz, sz) - -wn = turtle.Screen() # Set up the window -wn.bgcolor("lightgreen") - -tess = turtle.Turtle() # create tess - -drawSquare(tess, 50) - -wn.exitonclick() - - -

There are some points worth noting here:

-

-

    -
  • -

    Functions can call other functions.

    -
  • -
  • -

    Rewriting drawSquare like this captures the relationship - that we've spotted.

    -
  • -
  • -

    A caller of this function might say drawSquare(tess, 50). The parameters - of this function, tx and sz, are assigned the values of the tess object, and - the integer 50 respectively.

    -
  • -
  • -

    In the body of the function, tz and sz are just like any other variable.

    -
  • -
  • -

    When the call is made to drawRectangle, the values in variables tx and sz - are fetched first, then the call happens. So as we enter the top of - function drawRectangle, its variable t is assigned the tess object, and w and - h in that function are both given the value 50.

    -
  • -
-

+

So far, it may not be clear why it is worth the trouble to create all of these new functions. Actually, there are a lot of reasons, but this example demonstrates two:

@@ -156,16 +63,4 @@ wn.exitonclick()

- - Lab -

-

    -
  • -

    - Drawing a Circle In this guided lab exercise we will work - through a simple problem solving exercise related to drawing a circle with the turtle.

    -
  • -
-

-
diff --git a/pretext/Functions/Functionsthatreturnvalues.ptx b/pretext/Functions/Functionsthatreturnvalues.ptx index 9cda6247f..7f120fcf4 100644 --- a/pretext/Functions/Functionsthatreturnvalues.ptx +++ b/pretext/Functions/Functionsthatreturnvalues.ptx @@ -5,7 +5,7 @@ job. For example, if you want to find the absolute value of a number, you have to indicate what the number is. Python has a built-in function for computing the absolute value:

- + print(abs(5)) @@ -13,17 +13,11 @@ print(abs(-5))

In this example, the arguments to the abs function are 5 and -5.

-

Some functions take more than one argument. For example the math module contains a function - called - pow which takes two arguments, the base and the exponent.

- - - +

Some functions take more than one argument. For example the range function that we saw with for loops.

+ -import math -print(math.pow(2, 3)) - -print(math.pow(7, 4)) + print(list(range(4, 8))) + print(list(range(1, 10))) @@ -31,7 +25,7 @@ print(math.pow(7, 4))

Another built-in function that takes more than one argument is max.

- + print(max(7, 11)) print(max(4, 1, 17, 2, 12)) @@ -43,12 +37,12 @@ print(max(3 * 11, 5 ** 3, 512 - 9, 1024 ** 0)) return the maximum value sent. The arguments can be either simple values or expressions. In the last example, 503 is returned, since it is larger than 33, 125, and 1. Note that max also works on lists of values.

-

Furthermore, functions like range, int, abs all return values that +

Furthermore, functions like int and abs all return values that can be used to build more complex expressions.

-

So an important difference between these functions and one like drawSquare is that - drawSquare was not executed because we wanted it to compute a value — on the contrary, - we wrote drawSquare because we wanted it to execute a sequence of steps that caused - the turtle to draw a specific shape.

+

So an important difference between these functions and one like printRange is that + printRange was not executed because we wanted it to compute a value — on the contrary, + we wrote printRange because we wanted it to execute a sequence of steps that caused + the list's range to print.

Functions that return values are sometimes called fruitful functions. In many other languages, a chunk that doesn't return a value is called a procedure, but we will stick here with the Python way of also calling it a function, or if we want @@ -61,7 +55,7 @@ print(max(3 * 11, 5 ** 3, 512 - 9, 1024 ** 0)) as a parameter and return the result of squaring that number. Here is the black-box diagram with the Python code following.

- + def square(x): y = x * x @@ -99,7 +93,7 @@ print("The result of", toSquare, "squared is", result) we will see later where it makes sense to have a return statement even when other statements follow, and the further statements are not executed.

- + def square(x): y = x * x @@ -136,7 +130,7 @@ print("The result of", toSquare, "squared is", squareResult) programmers. As you step through this example, pay very close attention to the return value in the local variables listing. Then look at what is printed when the function returns.

- + def square(x): y = x * x @@ -154,7 +148,7 @@ print("The result of", toSquare, "squared is", squareResult)

Check your understanding

- +

What is wrong with the following function definition:

@@ -200,7 +194,7 @@ def addEm(x, y, z):
- +

What will the following function return?

diff --git a/pretext/Functions/ProgramDevelopment.ptx b/pretext/Functions/ProgramDevelopment.ptx index d86150a1f..c2cbbf371 100644 --- a/pretext/Functions/ProgramDevelopment.ptx +++ b/pretext/Functions/ProgramDevelopment.ptx @@ -29,7 +29,7 @@ def distance(x1, y1, x2, y2): returns zero. But it is syntactically correct, and it will run, which means that we can test it before we make it more complicated.

We import the test module to enable us to write a unit test for the function.

- + import test def distance(x1, y1, x2, y2): @@ -83,7 +83,7 @@ def distance(x1, y1, x2, y2): should be 25).

Finally, using the fractional exponent 0.5 to find the square root, we compute and return the result.

- + import test def distance(x1, y1, x2, y2): diff --git a/pretext/Functions/TheAccumulatorPattern.ptx b/pretext/Functions/TheAccumulatorPattern.ptx index f875e2b51..7200d30ea 100644 --- a/pretext/Functions/TheAccumulatorPattern.ptx +++ b/pretext/Functions/TheAccumulatorPattern.ptx @@ -2,7 +2,7 @@
The Accumulator Pattern -