{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Functions\n",
    "\n",
    "Functions provide a way to package often-used code into reusable and easy to use components. For example, here is some code that multiplies together two lists"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "list1 = [2, 4, 6, 8]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "list2 = [10, 20, 30, 40]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "list3 = []"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "for x, y in zip(list1,list2):\n",
    "    list3.append(x * y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[20, 80, 180, 320]"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We don't want to keep typing out the above code every time we want to multiply the numbers in two lists. Instead, we can collect that code together into a function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def multiply(a, b):\n",
    "    c = []\n",
    "    for x,y in zip(a,b):\n",
    "        c.append(x*y)\n",
    "    return c"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can now call this function directly on our lists, e.g."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "list3 = multiply(list1, list2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[20, 80, 180, 320]"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The function is called using its name, and passing in the values as two arguments, e.g."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "list4 = multiply( [1,2,3], [4,5,6] )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[4, 10, 18]"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list4"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The arguments are placed inside the round brackets. These are copied, in order, to the function. For example, `[1,2,3]` is copied into `a`, and `[4,5,6]` is copied as `b`. The code in the function is then executed. We can watch this code being run by adding in print statements, e.g.\n",
    "\n",
    "```python\n",
    "def multiply(a, b):\n",
    "    print(\"a = %s\" % a)\n",
    "    print(\"b = %s\" % b)\n",
    "    c = []\n",
    "    for x,y in zip(a,b):\n",
    "        print(\"%s times %s equals %s\" % (x,y,x*y))\n",
    "        c.append(x*y)\n",
    "    print(\"c = %s\" % c)\n",
    "    return c\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You must pass the right number of arguments into a function. For example, this is what happens if you get the number of arguments wrong..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "multiply() missing 1 required positional argument: 'b'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-11-2ad4f6a161c6>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mlist5\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmultiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlist1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m: multiply() missing 1 required positional argument: 'b'"
     ]
    }
   ],
   "source": [
    "list5 = multiply(list1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can write functions that take as many (or as few) arguments as you want. For example, here is a function that takes no arguments, and then a function that takes lots"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def func0():\n",
    "    return \"no arguments to this function\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def func1(a, b, c, d, e=5):\n",
    "    return a+b+c+d+e"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'no arguments to this function'"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "func0()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "15"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "func1(1, 2, 3, 4, 5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "15"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "func1(1, 2, 3, 4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that with the last function we have set a default value of the argument `e`. This is given the value of 5 if it is not specified. This allows us to pass 4 arguments instead of 5. Changing the default value by editing the definition of the function above will thus change the output of `func1` when it is called with only four arguments."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Exercise\n",
    "\n",
    "Here is the morse code dictionary from the last session, together with the code that converts a message from english into morse code."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "letter_to_morse = {'a':'.-', 'b':'-...', 'c':'-.-.', 'd':'-..', 'e':'.', 'f':'..-.',\n",
    "                   'g':'--.', 'h':'....', 'i':'..', 'j':'.---', 'k':'-.-', 'l':'.-..', 'm':'--',\n",
    "                   'n':'-.', 'o':'---', 'p':'.--.', 'q':'--.-', 'r':'.-.', 's':'...', 't':'-',\n",
    "                   'u':'..-', 'v':'...-', 'w':'.--', 'x':'-..-', 'y':'-.--', 'z':'--..',\n",
    "                   '0':'-----', '1':'.----', '2':'..---', '3':'...--', '4':'....-',\n",
    "                   '5':'.....', '6':'-....', '7':'--...', '8':'---..', '9':'----.',\n",
    "                   ' ':'/' }"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "message = \"SOS We have hit an iceberg and need help quickly\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['...', '---', '...', '/', '.--', '.', '/', '....', '.-', '...-', '.', '/', '....', '..', '-', '/', '.-', '-.', '/', '..', '-.-.', '.', '-...', '.', '.-.', '--.', '/', '.-', '-.', '-..', '/', '-.', '.', '.', '-..', '/', '....', '.', '.-..', '.--.', '/', '--.-', '..-', '..', '-.-.', '-.-', '.-..', '-.--']\n"
     ]
    }
   ],
   "source": [
    "morse = []\n",
    "for letter in message:\n",
    "    morse.append( letter_to_morse[letter.lower()] )\n",
    "print(morse)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Exercise 1\n",
    "\n",
    "Create a function called `encode` that takes a message and returns the morse code equivalent. Test this function by encodig the message `SOS We have hit an iceberg and need help quickly` and check that you get the same result as in the last session. Now try using your function to encode other messages."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def encode(message):\n",
    "    morse = []\n",
    "    for letter in message:\n",
    "        morse.append( letter_to_morse[letter.lower()] )\n",
    "    return morse"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "encode(message) == morse"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['....', '.', '.-..', '.-..', '---', '/', '.--', '---', '.-.', '.-..', '-..']"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "encode(\"Hello World\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Exercise 2\n",
    "\n",
    "Using the answer from Exercise 2 in the dictionaries lesson, write a function called `decode` that converts a morse code message back to english. Check that you can decode the above morse code message back to english."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "morse_to_letter = {}\n",
    "for letter in letter_to_morse.keys():\n",
    "    morse_to_letter[ letter_to_morse[letter] ] = letter    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def decode(morse):\n",
    "    english = []\n",
    "    for code in morse:\n",
    "        english.append( morse_to_letter[code] )\n",
    "    return \"\".join(english)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'sos we have hit an iceberg and need help quickly'"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "decode(morse)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Exercise 3\n",
    "\n",
    "Below is a list of messages. Loop over the messages and check that encode( decode(message) ) equals the original message. Do any of the messages fail to encode and decode correctly? If so, why? How can your check be modified to account for the limitations of your encode and decode functions?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "messages = [ \"hello world\", \"this is a long message\", \"Oh no this may break\", \"This message is difficult to encode.\" ]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "checking for message 'hello world'...\n",
      "True\n",
      "checking for message 'this is a long message'...\n",
      "True\n",
      "checking for message 'Oh no this may break'...\n",
      "False\n",
      "checking for message 'This message is difficult to encode.'...\n"
     ]
    },
    {
     "ename": "KeyError",
     "evalue": "'.'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mKeyError\u001b[0m                                  Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-27-58f3e4df6a62>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mmessage\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mmessages\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      2\u001b[0m     \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"checking for message '%s'...\"\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0mmessage\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m     \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m \u001b[0mmessage\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m \u001b[0mencode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmessage\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m)\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m<ipython-input-20-39fdf9cb0c17>\u001b[0m in \u001b[0;36mencode\u001b[0;34m(message)\u001b[0m\n\u001b[1;32m      2\u001b[0m     \u001b[0mmorse\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      3\u001b[0m     \u001b[0;32mfor\u001b[0m \u001b[0mletter\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mmessage\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m         \u001b[0mmorse\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m \u001b[0mletter_to_morse\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mletter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlower\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      5\u001b[0m     \u001b[0;32mreturn\u001b[0m \u001b[0mmorse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mKeyError\u001b[0m: '.'"
     ]
    }
   ],
   "source": [
    "for message in messages:\n",
    "    print(\"checking for message '%s'...\" % message)\n",
    "    print( message == decode( encode(message) ) )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "checking for message 'hello world'...\n",
      "True\n",
      "checking for message 'this is a long message'...\n",
      "True\n",
      "checking for message 'Oh no this may break'...\n",
      "True\n",
      "checking for message 'This message is difficult to encode.'...\n"
     ]
    },
    {
     "ename": "KeyError",
     "evalue": "'.'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mKeyError\u001b[0m                                  Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-28-d00d649e627e>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mmessage\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mmessages\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      2\u001b[0m     \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"checking for message '%s'...\"\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0mmessage\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m     \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m \u001b[0mmessage\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlower\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mencode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmessage\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m<ipython-input-20-39fdf9cb0c17>\u001b[0m in \u001b[0;36mencode\u001b[0;34m(message)\u001b[0m\n\u001b[1;32m      2\u001b[0m     \u001b[0mmorse\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      3\u001b[0m     \u001b[0;32mfor\u001b[0m \u001b[0mletter\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mmessage\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m         \u001b[0mmorse\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m \u001b[0mletter_to_morse\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mletter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlower\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      5\u001b[0m     \u001b[0;32mreturn\u001b[0m \u001b[0mmorse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mKeyError\u001b[0m: '.'"
     ]
    }
   ],
   "source": [
    "for message in messages:\n",
    "    print(\"checking for message '%s'...\" % message)\n",
    "    print( message.lower() == decode(encode(message)) )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
