{"id":"calculator","title":"A command calculator","summary":"A calculator you talk to in commands such as add 3 4 or sum 1 2 3. A function for each operation, a line taken apart with split, clear messages for bad input instead of a crash, a history of what you calculated, and ans for the last answer.","after":"strings-and-lists","intro":"In this project you build a calculator that you use by typing commands, one per line. Each command is a word that says what to do, followed by the numbers to do it with: `add 3 4` adds 3 and 4, and `sum 1 2 3 4` adds up any number of numbers. The calculator answers every line, and keeps going until you type `quit`.\n\nWhen it's finished, a session looks like this. The person types these lines:\n\n```text\nadd 3 4\ndiv ans 2\nsum 1 2 3 4\nmul ans ans\npow 2 8\ndiv 5 0\nadd 2 three\nhistory\nquit\n```\n\nand the calculator answers them, with its prompt `> ` in front of each typed line:\n\n```text\n> add 3 4\n7\n> div ans 2\n3.5\n> sum 1 2 3 4\n10\n> mul ans ans\n100\n> pow 2 8\nError: unknown command pow\n> div 5 0\nError: can't divide by zero\n> add 2 three\nError: three is not a number\n> history\n1: add 3 4 = 7\n2: div 7 2 = 3.5\n3: sum 1 2 3 4 = 10\n4: mul 10 10 = 100\n> quit\nBye!\n```\n\nA mistake never stops the program: it gets a message that says what's wrong, and the next line is read as usual. `ans` stands for the last answer, and `history` lists every calculation that worked.\n\n### What you need\n\nEverything up to module 18: `while` loops with `break` and `continue` (modules 9 to 12), strings and their indexes (module 11), functions that take parameters and return values, with the program's own code under `if __name__ == \"__main__\":` (modules 13 and 14), lists and `for` loops (modules 15 and 16), f-strings (module 17), and the string methods `split` and `replace` (module 18).\n\n### How you build it\n\nEach milestone starts from your own code of the milestone before, and adds one thing to it:\n\n1. **Four operations:** a function for each operation, and `calculate`, which answers one line.\n2. **Errors in commands:** unknown commands and missing numbers get a message instead of a crash.\n3. **Is it a number?** Your own check for numbers, which also lets in decimals such as `2.5`.\n4. **Any number of numbers:** `sum`, `max`, `min` and `mean` take as many numbers as you type.\n5. **History:** the calculator remembers what it calculated.\n6. **The last answer:** `ans` in a command means the answer before it.\n\nSome checks call your functions directly, as in the lessons, and others type lines into the whole program and compare everything it prints.\n","milestones":[{"id":"operations","title":"Four operations","task":"The calculator reads one line at a time and answers it, until the line is `quit`. The starting code already has that loop in `main`: for every line, it prints what `calculate` returns. Your part is the calculating.\n\nA command is a word and two whole numbers, separated by spaces:\n\n- `add 3 4` adds the numbers: 7\n- `sub 10 4` subtracts the second number from the first: 6\n- `mul 6 7` multiplies them: 42\n- `div 7 2` divides the first by the second: 3.5\n\n1. Next to `add`, write the functions `subtract(a, b)`, `multiply(a, b)` and `divide(a, b)`. Each one returns its result. `divide` uses `/`, so `divide(7, 2)` returns `3.5`.\n2. Write `calculate(line)`. It gets one line, such as `\"add 3 4\"`, and returns the answer **as a string**: `\"7\"`. It takes the line apart, turns the two numbers into integers with `int`, calls the function for the command, and turns the result into a string with `str`.\n\nThe string method `split` from module 18 takes a line apart at its spaces, and gives the pieces as a list of strings:\n\n```pycon\n>>> parts = \"add 3 4\".split()\n>>> parts\n['add', '3', '4']\n>>> parts[0]\n'add'\n>>> int(parts[1]) + int(parts[2])\n7\n```\n\nFor now, every line is a correct command; the next milestone deals with mistakes. With the input `add 3 4`, `sub 10 4`, `mul 6 7`, `div 7 2` and `quit`, the program prints:\n\n```\n> add 3 4\n7\n> sub 10 4\n6\n> mul 6 7\n42\n> div 7 2\n3.5\n> quit\nBye!\n```\n","kind":"code","seed":"# A calculator that reads commands such as \"add 3 4\" and answers them.\n\n\ndef add(a, b):\n    return a + b\n\n\ndef calculate(line):\n    # Take the line apart, do the calculation it asks for,\n    # and return the answer as a string.\n    return \"\"\n\n\ndef main():\n    while True:\n        line = input(\"> \")\n        if line == \"quit\":\n            print(\"Bye!\")\n            break\n        print(calculate(line))\n\n\nif __name__ == \"__main__\":\n    main()\n","solution":"# A calculator that reads commands such as \"add 3 4\" and answers them.\n\n\ndef add(a, b):\n    return a + b\n\n\ndef subtract(a, b):\n    return a - b\n\n\ndef multiply(a, b):\n    return a * b\n\n\ndef divide(a, b):\n    return a / b\n\n\ndef calculate(line):\n    parts = line.split()\n    command = parts[0]\n    a = int(parts[1])\n    b = int(parts[2])\n    if command == \"add\":\n        return str(add(a, b))\n    if command == \"sub\":\n        return str(subtract(a, b))\n    if command == \"mul\":\n        return str(multiply(a, b))\n    return str(divide(a, b))\n\n\ndef main():\n    while True:\n        line = input(\"> \")\n        if line == \"quit\":\n            print(\"Bye!\")\n            break\n        print(calculate(line))\n\n\nif __name__ == \"__main__\":\n    main()\n","hints":["Write the three new functions just like `add`: each has one line, such as `return a - b`.","In `calculate`, start with `parts = line.split()`. Then `command = parts[0]`, `a = int(parts[1])` and `b = int(parts[2])`.","Then one `if` for each command: under `if command == \"add\":` comes `return str(add(a, b))`, and the same for `sub`, `mul` and `div` with their own functions."],"tests":[{"name":"Four commands","stdin":"add 3 4\nsub 10 4\nmul 6 7\ndiv 7 2\nquit\n","expect":"> add 3 4\n7\n> sub 10 4\n6\n> mul 6 7\n42\n> div 7 2\n3.5\n> quit\nBye!","hidden":false},{"name":"The four functions","stdin":"","call":"print(add(2, 3), subtract(2, 3), multiply(2, 3), divide(3, 2))\n","expect":"5 -1 6 1.5","hidden":false},{"name":"Negative and even answers","stdin":"sub 3 10\nmul 0 5\ndiv 6 3\ndiv 10 4\nadd 100 250\nquit\n","expect":"> sub 3 10\n-7\n> mul 0 5\n0\n> div 6 3\n2.0\n> div 10 4\n2.5\n> add 100 250\n350\n> quit\nBye!","hidden":true},{"name":"Calculate one line","stdin":"","call":"calculate(\"mul 12 12\")\n","expect":"'144'","hidden":true,"returns":true},{"name":"Calculate a division","stdin":"","call":"calculate(\"div 9 4\")\n","expect":"'2.25'","hidden":true,"returns":true},{"name":"Quit at once","stdin":"quit\n","expect":"> quit\nBye!","hidden":true}],"require":[],"forbid":[{"pattern":"\\beval\\s*\\(","message":"Do the calculation with your own functions instead of `eval`."}]},{"id":"errors","title":"Errors in commands","task":"People make typing mistakes. Right now `add 3` stops the whole program with an `IndexError`, because there is no `parts[2]`, and `div 5 0` stops it with a `ZeroDivisionError`. A calculator should answer a mistake with a message and carry on. Change `calculate` so that it returns these messages instead of crashing, checking them in this order:\n\n1. A command that isn't `add`, `sub`, `mul` or `div` returns `Error: unknown command pow` (with the word that was typed).\n2. A command with too few or too many numbers returns `Error: add needs 2 numbers` (with the command's name).\n3. Dividing by zero returns `Error: can't divide by zero`.\n\nAnd one change in `main`: a line that is empty, or has only spaces, gets no answer at all. The calculator just shows its prompt again.\n\nWith the input `add 3`, `pow 2 8`, `div 5 0`, an empty line, `mul 2 3 4`, `mul 2 3` and `quit`, the program prints:\n\n```\n> add 3\nError: add needs 2 numbers\n> pow 2 8\nError: unknown command pow\n> div 5 0\nError: can't divide by zero\n>\n> mul 2 3 4\nError: mul needs 2 numbers\n> mul 2 3\n6\n> quit\nBye!\n```\n\nThe numbers are still whole numbers; the next milestone deals with text that isn't a number at all.\n","kind":"code","seed":"# A calculator that reads commands such as \"add 3 4\" and answers them.\n\n\ndef add(a, b):\n    return a + b\n\n\ndef subtract(a, b):\n    return a - b\n\n\ndef multiply(a, b):\n    return a * b\n\n\ndef divide(a, b):\n    return a / b\n\n\ndef calculate(line):\n    parts = line.split()\n    command = parts[0]\n    a = int(parts[1])\n    b = int(parts[2])\n    if command == \"add\":\n        return str(add(a, b))\n    if command == \"sub\":\n        return str(subtract(a, b))\n    if command == \"mul\":\n        return str(multiply(a, b))\n    return str(divide(a, b))\n\n\ndef main():\n    while True:\n        line = input(\"> \")\n        if line == \"quit\":\n            print(\"Bye!\")\n            break\n        print(calculate(line))\n\n\nif __name__ == \"__main__\":\n    main()\n","solution":"# A calculator that reads commands such as \"add 3 4\" and answers them.\n\n\ndef add(a, b):\n    return a + b\n\n\ndef subtract(a, b):\n    return a - b\n\n\ndef multiply(a, b):\n    return a * b\n\n\ndef divide(a, b):\n    return a / b\n\n\ndef calculate(line):\n    parts = line.split()\n    command = parts[0]\n    if command not in [\"add\", \"sub\", \"mul\", \"div\"]:\n        return f\"Error: unknown command {command}\"\n    if len(parts) != 3:\n        return f\"Error: {command} needs 2 numbers\"\n    a = int(parts[1])\n    b = int(parts[2])\n    if command == \"add\":\n        return str(add(a, b))\n    if command == \"sub\":\n        return str(subtract(a, b))\n    if command == \"mul\":\n        return str(multiply(a, b))\n    if b == 0:\n        return \"Error: can't divide by zero\"\n    return str(divide(a, b))\n\n\ndef main():\n    while True:\n        line = input(\"> \")\n        if line == \"quit\":\n            print(\"Bye!\")\n            break\n        if len(line.split()) == 0:\n            continue\n        print(calculate(line))\n\n\nif __name__ == \"__main__\":\n    main()\n","hints":["Check before you use: test the command and the number of parts first, and only then read `parts[1]` and `parts[2]`. A function can `return` early, as soon as it knows the answer is an error.","`if command not in [\"add\", \"sub\", \"mul\", \"div\"]:` catches every unknown command. A correct command has exactly 3 parts, so check `len(parts) != 3` next.","For the empty line, `line.split()` gives an empty list `[]`, and `line.strip()` an empty string. In `main`, `continue` goes straight back to the top of the loop when `len(line.split()) == 0`."],"tests":[{"name":"Mistakes get messages","stdin":"add 3\npow 2 8\ndiv 5 0\n\nmul 2 3 4\nmul 2 3\nquit\n","expect":"> add 3\nError: add needs 2 numbers\n> pow 2 8\nError: unknown command pow\n> div 5 0\nError: can't divide by zero\n>\n> mul 2 3 4\nError: mul needs 2 numbers\n> mul 2 3\n6\n> quit\nBye!","hidden":false},{"name":"Only spaces and odd commands","stdin":"   \nhello\nsub\ndiv 0 5\nadd   2    3\nADD 2 3\nquit\n","expect":">\n> hello\nError: unknown command hello\n> sub\nError: sub needs 2 numbers\n> div 0 5\n0.0\n> add   2    3\n5\n> ADD 2 3\nError: unknown command ADD\n> quit\nBye!","hidden":true},{"name":"An error as a return value","stdin":"","call":"print(calculate(\"div 3 0\"))\nprint(calculate(\"sub 1\"))\nprint(calculate(\"times 2 3\"))\nprint(calculate(\"div 3 4\"))\n","expect":"Error: can't divide by zero\nError: sub needs 2 numbers\nError: unknown command times\n0.75","hidden":true}],"require":[],"forbid":[{"pattern":"\\beval\\s*\\(","message":"Do the calculation with your own functions instead of `eval`."}]},{"id":"numbers","title":"Is it a number?","task":"Two problems are left with the numbers. `add 2 three` still crashes with a `ValueError` in `int`, and the calculator can't do decimals: `add 2.5 1` crashes too. Both are solved by looking at the text before turning it into a number.\n\n1. Write `is_number(text)`. It returns `True` when the text is a number the calculator accepts, and `False` otherwise. A number has at least one digit; apart from digits it may have **one** decimal point `.` anywhere, and a minus sign `-` only as its very first character. So `12`, `-3`, `0.5` and `-2.25` are numbers, but `\"\"`, `-`, `1.2.3`, `4-` and `12a` are not.\n2. Write `to_number(text)`, for a text that `is_number` accepts. It returns the number as an integer when the text has no decimal point, and as a float when it has one: `to_number(\"12\")` returns `12`, and `to_number(\"2.5\")` returns `2.5`.\n3. In `calculate`, after the checks of the command and the count, check each number: the first one that isn't a number returns `Error: three is not a number` (with the text that was typed). Then turn the numbers into numbers with `to_number` instead of `int`.\n\nThe check calls `is_number` on several texts:\n\n```python\nprint(is_number(\"12\"), is_number(\"-3\"), is_number(\"0.5\"), is_number(\"-2.25\"))\nprint(is_number(\"\"), is_number(\"-\"), is_number(\"1.2.3\"), is_number(\"4-\"), is_number(\"12a\"))\n```\n\nand expects:\n\n```\nTrue True True True\nFalse False False False False\n```\n\nWith the input `add 2.5 1`, `mul -3 4`, `add 2 three`, `sub 1.5 0.25`, `div 1 0.0` and `quit`, the program prints:\n\n```text\n> add 2.5 1\n3.5\n> mul -3 4\n-12\n> add 2 three\nError: three is not a number\n> sub 1.5 0.25\n1.25\n> div 1 0.0\nError: can't divide by zero\n> quit\nBye!\n```\n","kind":"code","seed":"# A calculator that reads commands such as \"add 3 4\" and answers them.\n\n\ndef add(a, b):\n    return a + b\n\n\ndef subtract(a, b):\n    return a - b\n\n\ndef multiply(a, b):\n    return a * b\n\n\ndef divide(a, b):\n    return a / b\n\n\ndef calculate(line):\n    parts = line.split()\n    command = parts[0]\n    if command not in [\"add\", \"sub\", \"mul\", \"div\"]:\n        return f\"Error: unknown command {command}\"\n    if len(parts) != 3:\n        return f\"Error: {command} needs 2 numbers\"\n    a = int(parts[1])\n    b = int(parts[2])\n    if command == \"add\":\n        return str(add(a, b))\n    if command == \"sub\":\n        return str(subtract(a, b))\n    if command == \"mul\":\n        return str(multiply(a, b))\n    if b == 0:\n        return \"Error: can't divide by zero\"\n    return str(divide(a, b))\n\n\ndef main():\n    while True:\n        line = input(\"> \")\n        if line == \"quit\":\n            print(\"Bye!\")\n            break\n        if len(line.split()) == 0:\n            continue\n        print(calculate(line))\n\n\nif __name__ == \"__main__\":\n    main()\n","solution":"# A calculator that reads commands such as \"add 3 4\" and answers them.\n\n\ndef add(a, b):\n    return a + b\n\n\ndef subtract(a, b):\n    return a - b\n\n\ndef multiply(a, b):\n    return a * b\n\n\ndef divide(a, b):\n    return a / b\n\n\ndef is_number(text):\n    digits = 0\n    points = 0\n    for i in range(len(text)):\n        character = text[i]\n        if character in \"0123456789\":\n            digits += 1\n        elif character == \".\":\n            points += 1\n        elif character != \"-\" or i > 0:\n            return False\n    return digits > 0 and points <= 1\n\n\ndef to_number(text):\n    if \".\" in text:\n        return float(text)\n    return int(text)\n\n\ndef calculate(line):\n    parts = line.split()\n    command = parts[0]\n    if command not in [\"add\", \"sub\", \"mul\", \"div\"]:\n        return f\"Error: unknown command {command}\"\n    if len(parts) != 3:\n        return f\"Error: {command} needs 2 numbers\"\n    for text in parts[1:]:\n        if not is_number(text):\n            return f\"Error: {text} is not a number\"\n    a = to_number(parts[1])\n    b = to_number(parts[2])\n    if command == \"add\":\n        return str(add(a, b))\n    if command == \"sub\":\n        return str(subtract(a, b))\n    if command == \"mul\":\n        return str(multiply(a, b))\n    if b == 0:\n        return \"Error: can't divide by zero\"\n    return str(divide(a, b))\n\n\ndef main():\n    while True:\n        line = input(\"> \")\n        if line == \"quit\":\n            print(\"Bye!\")\n            break\n        if len(line.split()) == 0:\n            continue\n        print(calculate(line))\n\n\nif __name__ == \"__main__\":\n    main()\n","hints":["Go through the text one character at a time, counting the digits and the decimal points. `character in \"0123456789\"` is `True` for a digit.","A character that is neither a digit nor a point is only allowed when it is a `-` at index 0; anything else means `return False` at once. After the loop, the text is a number when it had at least one digit and at most one point.","In `to_number`, `\".\" in text` tells which kind it is: `float(text)` for a decimal, `int(text)` for a whole number. In `calculate`, a `for` loop over `parts[1:]` can return the error for the first text that `is_number` turns down."],"tests":[{"name":"Which texts are numbers","stdin":"","call":"print(is_number(\"12\"), is_number(\"-3\"), is_number(\"0.5\"), is_number(\"-2.25\"))\nprint(is_number(\"\"), is_number(\"-\"), is_number(\"1.2.3\"), is_number(\"4-\"), is_number(\"12a\"))\n","expect":"True True True True\nFalse False False False False","hidden":false},{"name":"Decimals and words","stdin":"add 2.5 1\nmul -3 4\nadd 2 three\nsub 1.5 0.25\ndiv 1 0.0\nquit\n","expect":"> add 2.5 1\n3.5\n> mul -3 4\n-12\n> add 2 three\nError: three is not a number\n> sub 1.5 0.25\n1.25\n> div 1 0.0\nError: can't divide by zero\n> quit\nBye!","hidden":false},{"name":"More texts to check","stdin":"","call":"print(is_number(\".\"), is_number(\"--1\"), is_number(\"1e5\"), is_number(\"3 \"), is_number(\"+7\"))\nprint(is_number(\".5\"), is_number(\"7.\"), is_number(\"007\"), is_number(\"-0.125\"))\n","expect":"False False False False False\nTrue True True True","hidden":true},{"name":"Whole numbers and decimals","stdin":"","call":"[to_number(\"12\"), to_number(\"-0.5\"), to_number(\"7.\"), to_number(\"-40\")]\n","expect":"[12, -0.5, 7.0, -40]","hidden":true,"returns":true},{"name":"The first text that isn't a number","stdin":"add x 1\nmul 2 2,5\nsub - 1\ndiv 3 -0.5\npow two 3\nadd 1\nquit\n","expect":"> add x 1\nError: x is not a number\n> mul 2 2,5\nError: 2,5 is not a number\n> sub - 1\nError: - is not a number\n> div 3 -0.5\n-6.0\n> pow two 3\nError: unknown command pow\n> add 1\nError: add needs 2 numbers\n> quit\nBye!","hidden":true}],"require":[],"forbid":[{"pattern":"\\beval\\s*\\(","message":"Do the calculation with your own functions instead of `eval`."}]},{"id":"many-numbers","title":"Any number of numbers","task":"Adding up a long column of numbers two at a time is slow. Four new commands take **any number of numbers**, at least one:\n\n- `sum 1 2 3 4` adds them all up: 10\n- `max 4 -2 9.5 3` gives the largest: 9.5\n- `min 4 -2 9.5 3` gives the smallest: -2\n- `mean 1 2 3 4` gives their average, the sum divided by how many there are: 2.5\n\n1. Write a function for each one: `total(numbers)`, `largest(numbers)`, `smallest(numbers)` and `mean(numbers)`. Each takes a list of numbers and returns its result. (The names `sum`, `max` and `min` are Python's own functions, which yours may use; giving your functions the same names would hide them.)\n2. In `calculate`, these four commands are known as well. One of them without any numbers returns `Error: sum needs at least 1 number` (with the command's name). The numbers are checked with `is_number` as before, and every text that isn't a number gets the same message as before.\n\nThe two-number commands work as they did, `add 1 2 3` is still an error, and the checks keep their order: the command, then the count, then each number, then dividing by zero.\n\nWith the input `sum 1 2 3 4`, `max 4 -2 9.5 3`, `min 4 -2 9.5 3`, `mean 1 2 3 4`, `sum`, `mean 7` and `quit`, the program prints:\n\n```\n> sum 1 2 3 4\n10\n> max 4 -2 9.5 3\n9.5\n> min 4 -2 9.5 3\n-2\n> mean 1 2 3 4\n2.5\n> sum\nError: sum needs at least 1 number\n> mean 7\n7.0\n> quit\nBye!\n```\n","kind":"code","seed":"# A calculator that reads commands such as \"add 3 4\" and answers them.\n\n\ndef add(a, b):\n    return a + b\n\n\ndef subtract(a, b):\n    return a - b\n\n\ndef multiply(a, b):\n    return a * b\n\n\ndef divide(a, b):\n    return a / b\n\n\ndef is_number(text):\n    digits = 0\n    points = 0\n    for i in range(len(text)):\n        character = text[i]\n        if character in \"0123456789\":\n            digits += 1\n        elif character == \".\":\n            points += 1\n        elif character != \"-\" or i > 0:\n            return False\n    return digits > 0 and points <= 1\n\n\ndef to_number(text):\n    if \".\" in text:\n        return float(text)\n    return int(text)\n\n\ndef calculate(line):\n    parts = line.split()\n    command = parts[0]\n    if command not in [\"add\", \"sub\", \"mul\", \"div\"]:\n        return f\"Error: unknown command {command}\"\n    if len(parts) != 3:\n        return f\"Error: {command} needs 2 numbers\"\n    for text in parts[1:]:\n        if not is_number(text):\n            return f\"Error: {text} is not a number\"\n    a = to_number(parts[1])\n    b = to_number(parts[2])\n    if command == \"add\":\n        return str(add(a, b))\n    if command == \"sub\":\n        return str(subtract(a, b))\n    if command == \"mul\":\n        return str(multiply(a, b))\n    if b == 0:\n        return \"Error: can't divide by zero\"\n    return str(divide(a, b))\n\n\ndef main():\n    while True:\n        line = input(\"> \")\n        if line == \"quit\":\n            print(\"Bye!\")\n            break\n        if len(line.split()) == 0:\n            continue\n        print(calculate(line))\n\n\nif __name__ == \"__main__\":\n    main()\n","solution":"# A calculator that reads commands such as \"add 3 4\" and answers them.\n\n\ndef add(a, b):\n    return a + b\n\n\ndef subtract(a, b):\n    return a - b\n\n\ndef multiply(a, b):\n    return a * b\n\n\ndef divide(a, b):\n    return a / b\n\n\ndef total(numbers):\n    return sum(numbers)\n\n\ndef largest(numbers):\n    return max(numbers)\n\n\ndef smallest(numbers):\n    return min(numbers)\n\n\ndef mean(numbers):\n    return sum(numbers) / len(numbers)\n\n\ndef is_number(text):\n    digits = 0\n    points = 0\n    for i in range(len(text)):\n        character = text[i]\n        if character in \"0123456789\":\n            digits += 1\n        elif character == \".\":\n            points += 1\n        elif character != \"-\" or i > 0:\n            return False\n    return digits > 0 and points <= 1\n\n\ndef to_number(text):\n    if \".\" in text:\n        return float(text)\n    return int(text)\n\n\ndef calculate(line):\n    parts = line.split()\n    command = parts[0]\n    texts = parts[1:]\n    if command in [\"add\", \"sub\", \"mul\", \"div\"]:\n        if len(texts) != 2:\n            return f\"Error: {command} needs 2 numbers\"\n    elif command in [\"sum\", \"max\", \"min\", \"mean\"]:\n        if len(texts) == 0:\n            return f\"Error: {command} needs at least 1 number\"\n    else:\n        return f\"Error: unknown command {command}\"\n    numbers = []\n    for text in texts:\n        if not is_number(text):\n            return f\"Error: {text} is not a number\"\n        numbers.append(to_number(text))\n    if command == \"add\":\n        return str(add(numbers[0], numbers[1]))\n    if command == \"sub\":\n        return str(subtract(numbers[0], numbers[1]))\n    if command == \"mul\":\n        return str(multiply(numbers[0], numbers[1]))\n    if command == \"div\":\n        if numbers[1] == 0:\n            return \"Error: can't divide by zero\"\n        return str(divide(numbers[0], numbers[1]))\n    if command == \"sum\":\n        return str(total(numbers))\n    if command == \"max\":\n        return str(largest(numbers))\n    if command == \"min\":\n        return str(smallest(numbers))\n    return str(mean(numbers))\n\n\ndef main():\n    while True:\n        line = input(\"> \")\n        if line == \"quit\":\n            print(\"Bye!\")\n            break\n        if len(line.split()) == 0:\n            continue\n        print(calculate(line))\n\n\nif __name__ == \"__main__\":\n    main()\n","hints":["The four new functions are one line each with Python's `sum`, `max`, `min` and `len`: `mean` returns `sum(numbers) / len(numbers)`.","In `calculate`, turn all the texts after the command into a list of numbers first: start from `numbers = []`, and in a loop over `parts[1:]` return the error for a text that isn't a number, or append `to_number(text)`. Then `numbers[0]` and `numbers[1]` are the two numbers of `add`.","Check the count separately for the two kinds of commands: `if command in [\"add\", \"sub\", \"mul\", \"div\"]:` needs exactly 2 numbers, `elif command in [\"sum\", \"max\", \"min\", \"mean\"]:` at least 1, and `else` is an unknown command."],"tests":[{"name":"Long commands","stdin":"sum 1 2 3 4\nmax 4 -2 9.5 3\nmin 4 -2 9.5 3\nmean 1 2 3 4\nsum\nmean 7\nquit\n","expect":"> sum 1 2 3 4\n10\n> max 4 -2 9.5 3\n9.5\n> min 4 -2 9.5 3\n-2\n> mean 1 2 3 4\n2.5\n> sum\nError: sum needs at least 1 number\n> mean 7\n7.0\n> quit\nBye!","hidden":false},{"name":"Functions on lists","stdin":"","call":"print(total([1, 2, 3]), largest([5, 1, 7]), smallest([5, 1, 7]), mean([1, 2]))\n","expect":"6 7 1 1.5","hidden":false},{"name":"Mistakes in long commands","stdin":"sum 2.5 2.5\nmax 3\nmin 3 x\nmean 2 4 nine 1\nmax\nadd 1 2 3\nsum -1 -2 -3\naverage 1 2\nquit\n","expect":"> sum 2.5 2.5\n5.0\n> max 3\n3\n> min 3 x\nError: x is not a number\n> mean 2 4 nine 1\nError: nine is not a number\n> max\nError: max needs at least 1 number\n> add 1 2 3\nError: add needs 2 numbers\n> sum -1 -2 -3\n-6\n> average 1 2\nError: unknown command average\n> quit\nBye!","hidden":true},{"name":"Long commands as strings","stdin":"","call":"print(calculate(\"mean 10 20 60\"))\nprint(calculate(\"min 8\"))\nprint(calculate(\"sum 1 2 3 4 5 6 7 8 9 10\"))\nprint(calculate(\"div 7 0\"))\n","expect":"30.0\n8\n55\nError: can't divide by zero","hidden":true}],"require":[],"forbid":[{"pattern":"\\beval\\s*\\(","message":"Do the calculation with your own functions instead of `eval`."},{"pattern":"\\bdef\\s+(sum|max|min)\\s*\\(","message":"Name your functions `total`, `largest` and `smallest`: a function called `sum`, `max` or `min` hides Python's own."}]},{"id":"history","title":"History","task":"Now the calculator remembers. Every calculation that works is saved in a list, as one string with the line that was typed and its answer: `add 3 4 = 7`. A line that gets an error message isn't saved.\n\n1. In `main`, keep the history in a list that starts empty, and add each calculation that worked to it. Every answer that isn't an error message is a calculation that worked.\n2. Write `print_history(history)`, which prints the saved calculations numbered from 1, as `1: add 3 4 = 7`. When the list is empty, it prints `No calculations yet.`\n3. Two new commands, which `main` handles itself before calling `calculate`. They aren't saved in the history:\n   - `history` prints the history with `print_history`.\n   - `clear` empties the history and prints `History cleared.`\n\nWith the input `history`, `add 3 4`, `pow 2 2`, `mean 1 2`, `history`, `clear`, `history`, `mul 5 5`, `history` and `quit`, the program prints:\n\n```\n> history\nNo calculations yet.\n> add 3 4\n7\n> pow 2 2\nError: unknown command pow\n> mean 1 2\n1.5\n> history\n1: add 3 4 = 7\n2: mean 1 2 = 1.5\n> clear\nHistory cleared.\n> history\nNo calculations yet.\n> mul 5 5\n25\n> history\n1: mul 5 5 = 25\n> quit\nBye!\n```\n","kind":"code","seed":"# A calculator that reads commands such as \"add 3 4\" and answers them.\n\n\ndef add(a, b):\n    return a + b\n\n\ndef subtract(a, b):\n    return a - b\n\n\ndef multiply(a, b):\n    return a * b\n\n\ndef divide(a, b):\n    return a / b\n\n\ndef total(numbers):\n    return sum(numbers)\n\n\ndef largest(numbers):\n    return max(numbers)\n\n\ndef smallest(numbers):\n    return min(numbers)\n\n\ndef mean(numbers):\n    return sum(numbers) / len(numbers)\n\n\ndef is_number(text):\n    digits = 0\n    points = 0\n    for i in range(len(text)):\n        character = text[i]\n        if character in \"0123456789\":\n            digits += 1\n        elif character == \".\":\n            points += 1\n        elif character != \"-\" or i > 0:\n            return False\n    return digits > 0 and points <= 1\n\n\ndef to_number(text):\n    if \".\" in text:\n        return float(text)\n    return int(text)\n\n\ndef calculate(line):\n    parts = line.split()\n    command = parts[0]\n    texts = parts[1:]\n    if command in [\"add\", \"sub\", \"mul\", \"div\"]:\n        if len(texts) != 2:\n            return f\"Error: {command} needs 2 numbers\"\n    elif command in [\"sum\", \"max\", \"min\", \"mean\"]:\n        if len(texts) == 0:\n            return f\"Error: {command} needs at least 1 number\"\n    else:\n        return f\"Error: unknown command {command}\"\n    numbers = []\n    for text in texts:\n        if not is_number(text):\n            return f\"Error: {text} is not a number\"\n        numbers.append(to_number(text))\n    if command == \"add\":\n        return str(add(numbers[0], numbers[1]))\n    if command == \"sub\":\n        return str(subtract(numbers[0], numbers[1]))\n    if command == \"mul\":\n        return str(multiply(numbers[0], numbers[1]))\n    if command == \"div\":\n        if numbers[1] == 0:\n            return \"Error: can't divide by zero\"\n        return str(divide(numbers[0], numbers[1]))\n    if command == \"sum\":\n        return str(total(numbers))\n    if command == \"max\":\n        return str(largest(numbers))\n    if command == \"min\":\n        return str(smallest(numbers))\n    return str(mean(numbers))\n\n\ndef main():\n    while True:\n        line = input(\"> \")\n        if line == \"quit\":\n            print(\"Bye!\")\n            break\n        if len(line.split()) == 0:\n            continue\n        print(calculate(line))\n\n\nif __name__ == \"__main__\":\n    main()\n","solution":"# A calculator that reads commands such as \"add 3 4\" and answers them.\n\n\ndef add(a, b):\n    return a + b\n\n\ndef subtract(a, b):\n    return a - b\n\n\ndef multiply(a, b):\n    return a * b\n\n\ndef divide(a, b):\n    return a / b\n\n\ndef total(numbers):\n    return sum(numbers)\n\n\ndef largest(numbers):\n    return max(numbers)\n\n\ndef smallest(numbers):\n    return min(numbers)\n\n\ndef mean(numbers):\n    return sum(numbers) / len(numbers)\n\n\ndef is_number(text):\n    digits = 0\n    points = 0\n    for i in range(len(text)):\n        character = text[i]\n        if character in \"0123456789\":\n            digits += 1\n        elif character == \".\":\n            points += 1\n        elif character != \"-\" or i > 0:\n            return False\n    return digits > 0 and points <= 1\n\n\ndef to_number(text):\n    if \".\" in text:\n        return float(text)\n    return int(text)\n\n\ndef calculate(line):\n    parts = line.split()\n    command = parts[0]\n    texts = parts[1:]\n    if command in [\"add\", \"sub\", \"mul\", \"div\"]:\n        if len(texts) != 2:\n            return f\"Error: {command} needs 2 numbers\"\n    elif command in [\"sum\", \"max\", \"min\", \"mean\"]:\n        if len(texts) == 0:\n            return f\"Error: {command} needs at least 1 number\"\n    else:\n        return f\"Error: unknown command {command}\"\n    numbers = []\n    for text in texts:\n        if not is_number(text):\n            return f\"Error: {text} is not a number\"\n        numbers.append(to_number(text))\n    if command == \"add\":\n        return str(add(numbers[0], numbers[1]))\n    if command == \"sub\":\n        return str(subtract(numbers[0], numbers[1]))\n    if command == \"mul\":\n        return str(multiply(numbers[0], numbers[1]))\n    if command == \"div\":\n        if numbers[1] == 0:\n            return \"Error: can't divide by zero\"\n        return str(divide(numbers[0], numbers[1]))\n    if command == \"sum\":\n        return str(total(numbers))\n    if command == \"max\":\n        return str(largest(numbers))\n    if command == \"min\":\n        return str(smallest(numbers))\n    return str(mean(numbers))\n\n\ndef print_history(history):\n    if len(history) == 0:\n        print(\"No calculations yet.\")\n    for i in range(len(history)):\n        print(f\"{i + 1}: {history[i]}\")\n\n\ndef main():\n    history = []\n    while True:\n        line = input(\"> \")\n        if line == \"quit\":\n            print(\"Bye!\")\n            break\n        if len(line.split()) == 0:\n            continue\n        if line == \"history\":\n            print_history(history)\n        elif line == \"clear\":\n            history = []\n            print(\"History cleared.\")\n        else:\n            answer = calculate(line)\n            print(answer)\n            if \"Error\" not in answer:\n                history.append(f\"{line} = {answer}\")\n\n\nif __name__ == \"__main__\":\n    main()\n","hints":["Create the list before the loop in `main`: `history = []`. Keep the answer of `calculate` in a variable, print it, and append `f\"{line} = {answer}\"` when the answer is not an error. `\"Error\" not in answer` tells you that.","In `print_history`, loop over the indexes with `range(len(history))`: the number to show is the index plus one.","Put `if line == \"history\":` and `elif line == \"clear\":` before the `else` that calls `calculate`. Clearing can simply start a new empty list: `history = []`."],"tests":[{"name":"Remember and forget","stdin":"history\nadd 3 4\npow 2 2\nmean 1 2\nhistory\nclear\nhistory\nmul 5 5\nhistory\nquit\n","expect":"> history\nNo calculations yet.\n> add 3 4\n7\n> pow 2 2\nError: unknown command pow\n> mean 1 2\n1.5\n> history\n1: add 3 4 = 7\n2: mean 1 2 = 1.5\n> clear\nHistory cleared.\n> history\nNo calculations yet.\n> mul 5 5\n25\n> history\n1: mul 5 5 = 25\n> quit\nBye!","hidden":false},{"name":"Printing a history","stdin":"","call":"print_history([\"add 1 2 = 3\", \"max 4 9 = 9\"])\n","expect":"1: add 1 2 = 3\n2: max 4 9 = 9","hidden":false},{"name":"An empty history","stdin":"","call":"print_history([])\n","expect":"No calculations yet.","hidden":true},{"name":"Errors are not saved","stdin":"sum 1 1 1\ndiv 1 0\nsub 2 x\ndiv 1 4\n\nmax 2 8 5\nhistory\nclear\nclear\nhistory\nquit\n","expect":"> sum 1 1 1\n3\n> div 1 0\nError: can't divide by zero\n> sub 2 x\nError: x is not a number\n> div 1 4\n0.25\n>\n> max 2 8 5\n8\n> history\n1: sum 1 1 1 = 3\n2: div 1 4 = 0.25\n3: max 2 8 5 = 8\n> clear\nHistory cleared.\n> clear\nHistory cleared.\n> history\nNo calculations yet.\n> quit\nBye!","hidden":true}],"require":[],"forbid":[{"pattern":"\\beval\\s*\\(","message":"Do the calculation with your own functions instead of `eval`."}]},{"id":"last-answer","title":"The last answer","task":"A calculation often goes on from the one before: double that, then take a quarter of it. The word `ans` in a command stands for the last answer, so `mul ans 2` doubles it.\n\n- `ans` is the answer of the last calculation that worked. Before the first one, it is 0.\n- An error message doesn't change it, and neither do `history` and `clear`.\n- In the history, a calculation is saved with the number in place of `ans`: `mul 7 2 = 14`, not `mul ans 2 = 14`.\n\nDo it in `main`, before the line goes to `calculate`: put the last answer into the line where it says `ans`. The string method `replace` from module 18 does exactly that, and `calculate` then gets an ordinary line of numbers. Keep the last answer as a string, as `calculate` returns it, so it can go straight into the line.\n\nWith the input `add 3 4`, `mul ans 2`, `div ans 4`, `sub ans ans`, `history` and `quit`, the program prints:\n\n```\n> add 3 4\n7\n> mul ans 2\n14\n> div ans 4\n3.5\n> sub ans ans\n0.0\n> history\n1: add 3 4 = 7\n2: mul 7 2 = 14\n3: div 14 4 = 3.5\n4: sub 3.5 3.5 = 0.0\n> quit\nBye!\n```\n","kind":"code","seed":"# A calculator that reads commands such as \"add 3 4\" and answers them.\n\n\ndef add(a, b):\n    return a + b\n\n\ndef subtract(a, b):\n    return a - b\n\n\ndef multiply(a, b):\n    return a * b\n\n\ndef divide(a, b):\n    return a / b\n\n\ndef total(numbers):\n    return sum(numbers)\n\n\ndef largest(numbers):\n    return max(numbers)\n\n\ndef smallest(numbers):\n    return min(numbers)\n\n\ndef mean(numbers):\n    return sum(numbers) / len(numbers)\n\n\ndef is_number(text):\n    digits = 0\n    points = 0\n    for i in range(len(text)):\n        character = text[i]\n        if character in \"0123456789\":\n            digits += 1\n        elif character == \".\":\n            points += 1\n        elif character != \"-\" or i > 0:\n            return False\n    return digits > 0 and points <= 1\n\n\ndef to_number(text):\n    if \".\" in text:\n        return float(text)\n    return int(text)\n\n\ndef calculate(line):\n    parts = line.split()\n    command = parts[0]\n    texts = parts[1:]\n    if command in [\"add\", \"sub\", \"mul\", \"div\"]:\n        if len(texts) != 2:\n            return f\"Error: {command} needs 2 numbers\"\n    elif command in [\"sum\", \"max\", \"min\", \"mean\"]:\n        if len(texts) == 0:\n            return f\"Error: {command} needs at least 1 number\"\n    else:\n        return f\"Error: unknown command {command}\"\n    numbers = []\n    for text in texts:\n        if not is_number(text):\n            return f\"Error: {text} is not a number\"\n        numbers.append(to_number(text))\n    if command == \"add\":\n        return str(add(numbers[0], numbers[1]))\n    if command == \"sub\":\n        return str(subtract(numbers[0], numbers[1]))\n    if command == \"mul\":\n        return str(multiply(numbers[0], numbers[1]))\n    if command == \"div\":\n        if numbers[1] == 0:\n            return \"Error: can't divide by zero\"\n        return str(divide(numbers[0], numbers[1]))\n    if command == \"sum\":\n        return str(total(numbers))\n    if command == \"max\":\n        return str(largest(numbers))\n    if command == \"min\":\n        return str(smallest(numbers))\n    return str(mean(numbers))\n\n\ndef print_history(history):\n    if len(history) == 0:\n        print(\"No calculations yet.\")\n    for i in range(len(history)):\n        print(f\"{i + 1}: {history[i]}\")\n\n\ndef main():\n    history = []\n    while True:\n        line = input(\"> \")\n        if line == \"quit\":\n            print(\"Bye!\")\n            break\n        if len(line.split()) == 0:\n            continue\n        if line == \"history\":\n            print_history(history)\n        elif line == \"clear\":\n            history = []\n            print(\"History cleared.\")\n        else:\n            answer = calculate(line)\n            print(answer)\n            if \"Error\" not in answer:\n                history.append(f\"{line} = {answer}\")\n\n\nif __name__ == \"__main__\":\n    main()\n","solution":"# A calculator that reads commands such as \"add 3 4\" and answers them.\n\n\ndef add(a, b):\n    return a + b\n\n\ndef subtract(a, b):\n    return a - b\n\n\ndef multiply(a, b):\n    return a * b\n\n\ndef divide(a, b):\n    return a / b\n\n\ndef total(numbers):\n    return sum(numbers)\n\n\ndef largest(numbers):\n    return max(numbers)\n\n\ndef smallest(numbers):\n    return min(numbers)\n\n\ndef mean(numbers):\n    return sum(numbers) / len(numbers)\n\n\ndef is_number(text):\n    digits = 0\n    points = 0\n    for i in range(len(text)):\n        character = text[i]\n        if character in \"0123456789\":\n            digits += 1\n        elif character == \".\":\n            points += 1\n        elif character != \"-\" or i > 0:\n            return False\n    return digits > 0 and points <= 1\n\n\ndef to_number(text):\n    if \".\" in text:\n        return float(text)\n    return int(text)\n\n\ndef calculate(line):\n    parts = line.split()\n    command = parts[0]\n    texts = parts[1:]\n    if command in [\"add\", \"sub\", \"mul\", \"div\"]:\n        if len(texts) != 2:\n            return f\"Error: {command} needs 2 numbers\"\n    elif command in [\"sum\", \"max\", \"min\", \"mean\"]:\n        if len(texts) == 0:\n            return f\"Error: {command} needs at least 1 number\"\n    else:\n        return f\"Error: unknown command {command}\"\n    numbers = []\n    for text in texts:\n        if not is_number(text):\n            return f\"Error: {text} is not a number\"\n        numbers.append(to_number(text))\n    if command == \"add\":\n        return str(add(numbers[0], numbers[1]))\n    if command == \"sub\":\n        return str(subtract(numbers[0], numbers[1]))\n    if command == \"mul\":\n        return str(multiply(numbers[0], numbers[1]))\n    if command == \"div\":\n        if numbers[1] == 0:\n            return \"Error: can't divide by zero\"\n        return str(divide(numbers[0], numbers[1]))\n    if command == \"sum\":\n        return str(total(numbers))\n    if command == \"max\":\n        return str(largest(numbers))\n    if command == \"min\":\n        return str(smallest(numbers))\n    return str(mean(numbers))\n\n\ndef print_history(history):\n    if len(history) == 0:\n        print(\"No calculations yet.\")\n    for i in range(len(history)):\n        print(f\"{i + 1}: {history[i]}\")\n\n\ndef main():\n    history = []\n    last = \"0\"\n    while True:\n        line = input(\"> \")\n        if line == \"quit\":\n            print(\"Bye!\")\n            break\n        if len(line.split()) == 0:\n            continue\n        if line == \"history\":\n            print_history(history)\n        elif line == \"clear\":\n            history = []\n            print(\"History cleared.\")\n        else:\n            line = line.replace(\"ans\", last)\n            answer = calculate(line)\n            print(answer)\n            if \"Error\" not in answer:\n                history.append(f\"{line} = {answer}\")\n                last = answer\n\n\nif __name__ == \"__main__\":\n    main()\n","hints":["Keep the last answer in a variable that starts as `\"0\"` before the loop, a string like the answers of `calculate`.","Just before calling `calculate`, `line = line.replace(\"ans\", last)` puts the number in. The history then saves that line, numbers and all.","Change the last answer only next to `history.append(...)`, where you already know the answer isn't an error: `last = answer`."],"tests":[{"name":"Going on from the last answer","stdin":"add 3 4\nmul ans 2\ndiv ans 4\nsub ans ans\nhistory\nquit\n","expect":"> add 3 4\n7\n> mul ans 2\n14\n> div ans 4\n3.5\n> sub ans ans\n0.0\n> history\n1: add 3 4 = 7\n2: mul 7 2 = 14\n3: div 14 4 = 3.5\n4: sub 3.5 3.5 = 0.0\n> quit\nBye!","hidden":false},{"name":"Before the first answer, and after an error","stdin":"add ans 5\ndiv ans 0\nmul ans 3\nsum ans ans ans\nsub 1 50\nclear\nmul ans ans\nhistory\nquit\n","expect":"> add ans 5\n5\n> div ans 0\nError: can't divide by zero\n> mul ans 3\n15\n> sum ans ans ans\n45\n> sub 1 50\n-49\n> clear\nHistory cleared.\n> mul ans ans\n2401\n> history\n1: mul -49 -49 = 2401\n> quit\nBye!","hidden":true},{"name":"Long commands keep working","stdin":"","call":"print(calculate(\"max 4 10 2\"))\nprint(is_number(\"4.5.6\"))\nprint_history([\"add 1 1 = 2\"])\n","expect":"10\nFalse\n1: add 1 1 = 2","hidden":true}],"require":[],"forbid":[{"pattern":"\\beval\\s*\\(","message":"Do the calculation with your own functions instead of `eval`."}]}]}