{"id":"return-values-small-functions","slug":"small-functions","title":"Building with small functions","text":"First a quick recap of two words you'll meet all the time. In a function's definition, the names in the parentheses are its **parameters**. In a call, the values in the parentheses are the **arguments**:\n\n```python\ndef seat(name, row, number):\n    print(f\"{name}: row {row}, seat {number}\")\n\n\nseat(\"Ada\", 4, 12)\nguest = \"Linus\"\nseat(guest, 2 + 1, 7)\n```\n\n```output\nAda: row 4, seat 12\nLinus: row 3, seat 7\n```\n\nWhen a function is called, the value of each argument is assigned to the parameter in the same position, for that call only. An argument can be a variable or a calculation: only its value is passed on, so the variable `guest` becomes the parameter `name`, and `2 + 1` becomes `row`.\n\nSince only the positions count, arguments in the wrong order go to the wrong parameters, and Python doesn't notice: `seat(12, 4, \"Ada\")` happily prints `12: row 4, seat Ada`. Python complains only about a wrong number of arguments, with a `TypeError`: with too few, it names the parameter that got no value; with too many, it says how many arguments the function takes and how many it was given. Check the order of the arguments yourself.\n\n### Small functions inside bigger ones\n\nIn the last module you saw that a function can call another one. That's the key to writing larger programs: write a small function, try it until it works, and then build a bigger one on top of it. Here `price_line` writes one line of a receipt, and `receipt` calls it for every line it needs:\n\n```python\ndef price_line(item, price):\n    print(f\"{item}: {price} euros\")\n\n\ndef receipt(first, first_price, second, second_price):\n    print(\"RECEIPT\")\n    price_line(first, first_price)\n    price_line(second, second_price)\n    price_line(\"Total\", first_price + second_price)\n\n\nreceipt(\"Coffee\", 3, \"Bun\", 2)\nprint()\nreceipt(\"Tea\", 2, \"Soup\", 7)\n```\n\n```output\nRECEIPT\nCoffee: 3 euros\nBun: 2 euros\nTotal: 5 euros\n\nRECEIPT\nTea: 2 euros\nSoup: 7 euros\nTotal: 9 euros\n```\n\n`receipt` doesn't need to know how a line looks: it only decides which lines there are. To write `EUR` instead of `euros`, you would change one line in `price_line`, and every receipt would follow. Look at what `receipt` passes on, too: its own parameters, a string of its own (`\"Total\"`) and a calculation.\n\nAn argument doesn't have to come from the caller's parameters at all. Here `bar` draws one progress bar, and `loading` calls it in a loop, with the number of filled cells taken from a counter:\n\n```python\ndef bar(filled, size):\n    print(\"[\" + \"#\" * filled + \"-\" * (size - filled) + \"]\")\n\n\ndef loading(size):\n    done = 0\n    while done <= size:\n        bar(done, size)\n        done += 1\n\n\nloading(4)\n```\n\n```output\n[----]\n[#---]\n[##--]\n[###-]\n[####]\n```\n\nEach piece stays short and easy to check, and once `bar` works, every function built on it draws its bars right. When a bigger function goes wrong, try the smaller ones on their own in the main block first: that tells you which piece has the bug.\n\nThe same idea helps when a task looks too big to start: look for the small function hidden inside it. Write that one first and run it with a call or two. Only when it prints what you expect, write the function that uses it.\n","task":"Fill in the blanks so that `battery_drain(size)` shows a battery running empty: it calls `bar` with all `size` cells filled first, then one fewer each time, down to none. `battery_drain(3)` prints:\n\n```\n[###]\n[##-]\n[#--]\n[---]\n```\n","kind":"fill","seed":"def bar(filled, size):\n    print(\"[\" + \"#\" * filled + \"-\" * (size - filled) + \"]\")\n\n\ndef battery_drain(size):\n    left = size\n    while left >= 0:\n        bar([[left]], [[size]])\n        left -= 1\n\n\nif __name__ == \"__main__\":\n    battery_drain(3)\n","solution":"def bar(filled, size):\n    print(\"[\" + \"#\" * filled + \"-\" * (size - filled) + \"]\")\n\n\ndef battery_drain(size):\n    left = size\n    while left >= 0:\n        bar(left, size)\n        left -= 1\n\n\nif __name__ == \"__main__\":\n    battery_drain(3)\n","hints":["`bar` needs two things: how many cells are filled right now, and how many cells there are in all.","The filled cells are the counter `left`, which goes down in the loop. The size of the battery doesn't change: it's `battery_drain`'s own parameter `size`."],"tests":[{"name":"battery_drain(3)","stdin":"","call":"battery_drain(3)\n","expect":"[###]\n[##-]\n[#--]\n[---]","hidden":false},{"name":"battery_drain(1)","stdin":"","call":"battery_drain(1)\n","expect":"[#]\n[-]","hidden":true},{"name":"battery_drain(5)","stdin":"","call":"battery_drain(5)\n","expect":"[#####]\n[####-]\n[###--]\n[##---]\n[#----]\n[-----]","hidden":true}],"require":[],"forbid":[],"more":[{"task":"Write the function `frame(width, height)`, which draws a hollow box of `#` characters: a full row at the top and at the bottom, and between them rows with a `#` at each end and spaces in the middle. The box is `width` characters wide and `height` rows tall (both at least 2). Use the two functions in the starter code to draw the rows. `frame(5, 4)` prints:\n\n```\n#####\n#   #\n#   #\n#####\n```\n","kind":"code","seed":"def full_row(width):\n    print(\"#\" * width)\n\n\ndef hollow_row(width):\n    print(\"#\" + \" \" * (width - 2) + \"#\")\n\n\ndef frame(width, height):\n    pass\n\n\nif __name__ == \"__main__\":\n    frame(5, 4)\n","solution":"def full_row(width):\n    print(\"#\" * width)\n\n\ndef hollow_row(width):\n    print(\"#\" + \" \" * (width - 2) + \"#\")\n\n\ndef frame(width, height):\n    full_row(width)\n    count = 0\n    while count < height - 2:\n        hollow_row(width)\n        count += 1\n    full_row(width)\n\n\nif __name__ == \"__main__\":\n    frame(5, 4)\n","hints":["Start with `full_row(width)` for the top and end with it for the bottom. Run it: that's already the box for height 2.","Between them come `height - 2` hollow rows: a `while` loop with a counter calls `hollow_row(width)` that many times.","Check `frame(4, 2)`: with no hollow rows, the loop must not run at all."],"tests":[{"name":"frame(5, 4)","stdin":"","call":"frame(5, 4)\n","expect":"#####\n#   #\n#   #\n#####","hidden":false},{"name":"frame(3, 3)","stdin":"","call":"frame(3, 3)\n","expect":"###\n# #\n###","hidden":true},{"name":"frame(4, 2)","stdin":"","call":"frame(4, 2)\n","expect":"####\n####","hidden":true},{"name":"frame(8, 5)","stdin":"","call":"frame(8, 5)\n","expect":"########\n#      #\n#      #\n#      #\n########","hidden":true}],"require":[{"pattern":"\\bhollow_row\\s*\\(","min":2,"message":"Draw the middle rows by calling `hollow_row`."}],"forbid":[]},{"task":"What does this program print? Type each line it prints.\n","kind":"predict","seed":"def show(first, second):\n    print(first, \"then\", second)\n\n\ndef swapped(first, second):\n    show(second, first)\n\n\nfirst = \"tea\"\nsecond = \"cake\"\nshow(second, first)\nswapped(first, second)\nshow(first, first)\n","solution":"def show(first, second):\n    print(first, \"then\", second)\n\n\ndef swapped(first, second):\n    show(second, first)\n\n\nfirst = \"tea\"\nsecond = \"cake\"\nshow(second, first)\nswapped(first, second)\nshow(first, first)\n","hints":["Arguments go to parameters by position, not by name: in `show(second, first)`, the parameter `first` gets the value `\"cake\"`.","`swapped(first, second)` calls `show` with its two values the other way around.","In the last call, both parameters get the value `\"tea\"`."],"tests":[{"name":"Output","stdin":"","expect":"cake then tea\ncake then tea\ntea then tea","hidden":false}],"require":[],"forbid":[],"lines":["cake then tea","cake then tea","tea then tea"]}]}