{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Python Functions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## What is a function?\n",
    "\n",
    "* A function is a block of organized, reusable code that is used to perform a single, related action.\n",
    "* Single, organized, related always ? :)\n",
    "\n",
    "\n",
    "### DRY - Do not Repeat Yourself principle\n",
    "\n",
    "* *Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.*\n",
    "http://wiki.c2.com/?DontRepeatYourself\n",
    "\n",
    "* Contrast WET - We Enjoy Typing, Write Everything Twice, Waste Everyone's Time"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def myFirstFunc():\n",
    "    print(\"Running My first func\")\n",
    "myFirstFunc()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "myFirstFunc()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def SecondFun():\n",
    "    print(\"my second func\")\n",
    "    myFirstFunc()\n",
    "SecondFun()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Passing parameters(arguments)\n",
    "def add(a, b):\n",
    "    print(a+b)\n",
    "add(4,6)\n",
    "add(9,233)\n",
    "add(\"Hello \",\"Riga\")\n",
    "add([1,2,7],list(range(6,12)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# We make Docstrings with '''Helpful function description inside'''\n",
    "def mult(a, b):\n",
    "    '''Returns \n",
    "    multiple from first two arguments'''\n",
    "    print(\"Look ma I am multiplying!\")\n",
    "    return(a*b)\n",
    "print(mult(5,7))\n",
    "print(mult([3,6],4))\n",
    "print(mult(\"ggust\", 4))\n",
    "?mult"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def sub(a, b):\n",
    "    print(a-b)\n",
    "    return(a-b)\n",
    "sub(20, 3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def add3(a,b,c):\n",
    "    print(a+b+c)\n",
    "    return(a+b+c)\n",
    "add3(13,26,864)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def isPrime(num):\n",
    "    '''\n",
    "    Super simple method of checking for prime. \n",
    "    '''\n",
    "    for n in range(2,num): #How could we optimize this?\n",
    "        if num % n == 0:\n",
    "            print(f'{num} is not prime, it divides by {n}')\n",
    "            break\n",
    "    else: # runs when no divisors found\n",
    "        print(f'{num} is prime')\n",
    "isPrime(53)\n",
    "isPrime(51)\n",
    "isPrime(59)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def isPrimeO(num):\n",
    "    '''\n",
    "    Faster method of checking for prime. \n",
    "    '''\n",
    "    if num % 2 == 0 and num > 2: \n",
    "        return False\n",
    "    for i in range(3, int(num**0.5) + 1, 2): ## notice we only care about odd numbers  and do not need to check past sqrt of num\n",
    "        if num % i == 0:\n",
    "            return False\n",
    "    return True\n",
    "isPrimeO(23)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Jupyter magic\n",
    "* *%%HTML* lets you render cell as HTML\n",
    "* *%%time* times your cell operation, *%time* times your single line run time\n",
    "* *%%timeit* runs your cell multiple time and gives you average\n",
    "\n",
    "### Magic docs: http://ipython.readthedocs.io/en/stable/interactive/magics.html"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%timeit isPrime(100001)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%timeit isPrimeO(100001)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Why are the tests not comparable?\n",
    "# Hint: What is different about the function outputs?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import random\n",
    "def guessnum():\n",
    "    '''\n",
    "    Plays the number guessing game\n",
    "    '''\n",
    "    secret = random.randrange(100)\n",
    "    #print(secret)\n",
    "    x=-1 #Why did we need this declaration? How could we change the code to not require this assignment?\n",
    "    while x != secret:\n",
    "        x = int(input(\"Enter an integer please! \"))\n",
    "        if x > secret:\n",
    "            print(\"your number is too large\")\n",
    "        elif x < secret:\n",
    "            print(\"your number is too small\")\n",
    "        else:\n",
    "            print(\"YOU WON!\")\n",
    "            print(f\"secret number is {secret}\")\n",
    "            break\n",
    "guessnum()\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "## Possible improvements, count how many tries it took to play the game"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def lazypow(a, b=2):\n",
    "    '''Returns a taken to the power of b\n",
    "    b default is 2'''\n",
    "    return(a**b)\n",
    "print(lazypow(3,4))\n",
    "print(lazypow(11))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Chaining function calls\n",
    "print(lazypow(mult(2,6)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Returning multiple values\n",
    "def multdiv(a=6,b=3):\n",
    "    '''Returns two values as a tuple!:\n",
    "    1. multiplication of arguments\n",
    "    2. a/b\n",
    "    '''\n",
    "    return(a*b, a/b)\n",
    "print(multdiv())\n",
    "print(multdiv(12))\n",
    "print(multdiv(b=4))\n",
    "print(multdiv(15,3))\n",
    "# we could just return two values separately"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def fizzbuzz(a,b,beg=1,end=100):\n",
    "    for i in range(beg,end+1):\n",
    "        if i % a == 0 and i % b == 0:\n",
    "            print(\"FizzBuzz\")\n",
    "        elif i % a == 0:\n",
    "            print(\"Fizz\")\n",
    "        elif i % b == 0:\n",
    "            print(\"Buzz\")\n",
    "        else:\n",
    "            print(i)\n",
    "#fizzbuzz(3,5)\n",
    "fizzbuzz(5,7)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def lazybuzz():\n",
    "    print([\"Fizz\"*(x%3 == 0)+\"Buzz\"*(x%5 == 0) or x for x in range(1,101)])\n",
    "lazybuzz()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Create a lazybuzz function which takes four arguments with default values of 3,5 , 1 and 100 representing the two divisors the beggining and end"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Side effects\n",
    "\n",
    "In computer science, a function or expression is said to have a side effect if it modifies some state outside its scope or has an observable interaction with its calling functions or the outside world besides returning a value.\n",
    "* Ideal (Platonic?) function has none, but not always possible(input/output, globals)\n",
    "* Functional programming style strives towards this ideal, but real life is mixture of styles"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%time\n",
    "import time #this time library has nothing to do with %%time Jupyter command\n",
    "def hello():\n",
    "    print(\"HW\")\n",
    "    time.sleep(.100)\n",
    "    print(\"Awake\")\n",
    "    \n",
    "hello()\n",
    "hello()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "##Built-in Functions\t\t\n",
    "abs()   dict()\thelp()\tmin()\tsetattr()\n",
    "all()\tdir()\thex()\tnext()\tslice()\n",
    "any()\tdivmod()\tid()\tobject()\tsorted()\n",
    "ascii()\tenumerate()\tinput()\toct()\tstaticmethod()\n",
    "bin()\teval()\tint()\topen()\tstr()\n",
    "bool()\texec()\tisinstance()\tord()\tsum()\n",
    "bytearray()\tfilter()\tissubclass()\tpow()\tsuper()\n",
    "bytes()\tfloat()\titer()\tprint()\ttuple()\n",
    "callable()\tformat()\tlen()\tproperty()\ttype()\n",
    "chr()\tfrozenset()\tlist()\trange()\tvars()\n",
    "classmethod()\tgetattr()\tlocals()\trepr()\tzip()\n",
    "compile()\tglobals()\tmap()\treversed()\t__import__()\n",
    "complex()\thasattr()\tmax()\tround()\t \n",
    "delattr()\thash()\tmemoryview()\tset()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### More info on builtin functions: https://docs.python.org/3/library/functions.html"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Usage of *args \n",
    "\n",
    " *args and **kwargs are mostly used in function definitions. *args and **kwargs allow you to pass a variable number of arguments to a function. What does variable mean here is that you do not know before hand that how many arguments can be passed to your function by the user so in this case you use these two keywords. *args is used to send a non-keyworded variable length argument list to the function.\n",
    " \n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def test_var_args(f_arg, *argv):\n",
    "    print(\"first normal arg:\", f_arg)\n",
    "    for arg in argv:\n",
    "        print(\"another arg through *argv :\", arg)\n",
    "\n",
    "test_var_args('yasoob','python','eggs','test')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#Usage of **kwargs\n",
    "\n",
    "  **kwargs allows you to pass keyworded variable length of arguments to a function. You should use **kwargs if you want to handle named arguments in a function.\n",
    "  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def greetMe(**kwargs):\n",
    "    if kwargs is not None:\n",
    "        for key, value in kwargs.items():\n",
    "            print(f\"{key} == {value}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "greetMe(name=\"Valdis\",hobby=\"biking\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Homework Problems"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Easy\n",
    "# Write a function to calculate volume for Rectangular Cuboid (visas malas ir taisnsturas 3D objektam)\n",
    "def getRectVol(l,w,h):\n",
    "    '''\n",
    "    '''\n",
    "    return None #You should be returning something not None!\n",
    "getRectVol(2,5,7) == 70"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Medium\n",
    "# Write a function to check if string is a palindrome\n",
    "def isPalindrome(s):\n",
    "    '''\n",
    "    '''\n",
    "    return None\n",
    "print(isPalindrome('alusariirasula') == True)\n",
    "print(isPalindrome('normaltext') == False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dir(\"string\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# One liner is possible! Okay to do it a longer way\n",
    "# Hints: dir(\"mystring\") for string manipulation(might need more than one method)\n",
    "# Also remember one \"unique\" data structure we covered\n",
    "\n",
    "import string\n",
    "print(string.ascii_lowercase)\n",
    "def isPangram(s, a=string.ascii_lowercase):\n",
    "    '''\n",
    "    '''\n",
    "    return None\n",
    "assert(isPangram('dadfafd') == False)\n",
    "assert(isPangram(\"The quick brown fox jumps over the lazy dog\") == True)\n",
    "assert(isPangram(\"The five boxing wizards jump quickly\") == True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<function myfunc at 0x055B0FA8>\n",
      "<built-in function print> <built-in function print> 1769760 1769760\n",
      "<function myfunc at 0x055B0FA8> <function myfunc at 0x055B0FA8> 89853864 89853864\n",
      "14\n",
      "hello there\n"
     ]
    }
   ],
   "source": [
    "# Functions are first-class citizens in Python.\n",
    "\n",
    "# They can be passed as arguments to other functions,\n",
    "# returned as values from other functions, and\n",
    "# assigned to variables and stored in data structures.\n",
    "\n",
    "def myfunc(a, b):\n",
    "    return a + b\n",
    "\n",
    "funcs = [myfunc,print]\n",
    "funcs[1](funcs[0])\n",
    "print(funcs[1], print, id(print), id(funcs[1]))\n",
    "print(myfunc,funcs[0], id(myfunc), id(funcs[0]))\n",
    "\n",
    "funcs[1](funcs[0](5, 9))\n",
    "funcs[1](\"hello there\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "int('0x055B0FA8', 10)"
   ]
  }
 ],
 "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.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}