{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "afa9f3e2",
   "metadata": {},
   "source": [
    "# Lesson 06: Functions Demonstration\n",
    "\n",
    "This notebook provides simple examples of key function concepts in Python."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a32f418e",
   "metadata": {},
   "source": [
    "---\n",
    "## 1. Functions intro\n",
    "\n",
    "### 1.1. Defining functions\n",
    "\n",
    "A function is defined using the `def` keyword, followed by the function name and parentheses."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "613eb8df",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Simple function definition\n",
    "def greet():\n",
    "    print(\"Hello, World!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "17e713c9",
   "metadata": {},
   "source": [
    "### 1.2. Calling functions\n",
    "\n",
    "To execute a function, call it by using its name followed by parentheses."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "49487e18",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello, World!\n"
     ]
    }
   ],
   "source": [
    "# Call the function\n",
    "greet()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e67b8dff",
   "metadata": {},
   "source": [
    "---\n",
    "## 2. Function Arguments\n",
    "\n",
    "### 2.1. Single argument\n",
    "Functions can accept input values called arguments or parameters."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "b62c21e6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello, Alice!\n"
     ]
    }
   ],
   "source": [
    "# Function with arguments\n",
    "def greet_person(name):\n",
    "    print(f\"Hello, {name}!\")\n",
    "\n",
    "# Call the function with an argument\n",
    "greet_person(\"Alice\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "48c08617",
   "metadata": {},
   "source": [
    "### 2.2. Positional vs Keyword Arguments\n",
    "\n",
    "**Positional arguments** are matched by order.  \n",
    "**Keyword arguments** are matched by name."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "68a4c48b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "My name is Bob and I am 25 years old.\n",
      "My name is Carol and I am 30 years old.\n"
     ]
    }
   ],
   "source": [
    "# Function with multiple parameters\n",
    "def introduce(name, age):\n",
    "    print(f\"My name is {name} and I am {age} years old.\")\n",
    "\n",
    "# Positional arguments (order matters)\n",
    "introduce(\"Bob\", 25)\n",
    "\n",
    "# Keyword arguments (order doesn't matter)\n",
    "introduce(age=30, name=\"Carol\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "363691fa",
   "metadata": {},
   "source": [
    "### 2.3. Arbitrary Arguments\n",
    "\n",
    "#### Non-keyword Arbitrary Arguments (*args)\n",
    "\n",
    "Use `*args` to accept any number of positional arguments."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fc65158f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "6\n",
      "100\n"
     ]
    }
   ],
   "source": [
    "# Function with arbitrary positional arguments\n",
    "def add_numbers(*args):\n",
    "\n",
    "    total = 0\n",
    "\n",
    "    for num in args:\n",
    "        total += num\n",
    "\n",
    "    return total\n",
    "\n",
    "# Call with different numbers of arguments\n",
    "print(add_numbers(1, 2, 3))\n",
    "print(add_numbers(10, 20, 30, 40))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c7cd1895",
   "metadata": {},
   "source": [
    "#### Keyword Arbitrary Arguments (**kwargs)\n",
    "\n",
    "Use `**kwargs` to accept any number of keyword arguments."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "26d95128",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name: David\n",
      "age: 28\n",
      "city: New York\n"
     ]
    }
   ],
   "source": [
    "# Function with arbitrary keyword arguments\n",
    "def print_info(**kwargs):\n",
    "\n",
    "    for key, value in kwargs.items():\n",
    "        print(f\"{key}: {value}\")\n",
    "\n",
    "# Call with different keyword arguments\n",
    "print_info(name=\"David\", age=28, city=\"New York\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0dc814de",
   "metadata": {},
   "source": [
    "---\n",
    "## 3. Return Statement\n",
    "\n",
    "The `return` statement sends a value back to the caller."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "60f099a4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello\n",
      "Result: None\n"
     ]
    }
   ],
   "source": [
    "# Function without return (returns None)\n",
    "def say_hello():\n",
    "    print(\"Hello\")\n",
    "\n",
    "result = say_hello()\n",
    "print(f\"Result: {result}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "39f80686",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Result: 8\n"
     ]
    }
   ],
   "source": [
    "# Function with return\n",
    "def add(a, b):\n",
    "    return a + b\n",
    "\n",
    "result = add(5, 3)\n",
    "print(f\"Result: {result}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "24a5dec9",
   "metadata": {},
   "source": [
    "---\n",
    "## 4. Scope of Variables: Global vs Local\n",
    "\n",
    "**Global variables** are defined outside functions.  \n",
    "**Local variables** are defined inside functions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bd940e0e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Inside function - x: 10, y: 5\n",
      "Outside function - x: 10\n"
     ]
    },
    {
     "ename": "NameError",
     "evalue": "name 'y' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mNameError\u001b[39m                                 Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[23]\u001b[39m\u001b[32m, line 11\u001b[39m\n\u001b[32m      9\u001b[39m test_scope()\n\u001b[32m     10\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mOutside function - x: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mx\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n\u001b[32m---> \u001b[39m\u001b[32m11\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[43my\u001b[49m)  \u001b[38;5;66;03m# This would cause an error - y is not accessible outside the function\u001b[39;00m\n",
      "\u001b[31mNameError\u001b[39m: name 'y' is not defined"
     ]
    }
   ],
   "source": [
    "# Global variable\n",
    "x = 10\n",
    "\n",
    "def test_scope():\n",
    "\n",
    "    # Local variable\n",
    "    y = 5\n",
    "    print(f\"Inside function - x: {x}, y: {y}\")\n",
    "\n",
    "test_scope()\n",
    "print(f\"Outside function - x: {x}\")\n",
    "print(y)  # This would cause an error - y is not accessible outside the function"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9c1d18db",
   "metadata": {},
   "source": [
    "### 4.1. Modifying Global Variables\n",
    "\n",
    "Use the `global` keyword to modify a global variable inside a function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "043b422a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Counter before: 0\n",
      "Counter inside function: 1\n",
      "Counter inside function: 2\n",
      "Counter after: 2\n"
     ]
    }
   ],
   "source": [
    "# Global variable\n",
    "counter = 0\n",
    "\n",
    "def increment():\n",
    "\n",
    "    global counter\n",
    "\n",
    "    counter += 1\n",
    "    print(f\"Counter inside function: {counter}\")\n",
    "\n",
    "print(f\"Counter before: {counter}\")\n",
    "increment()\n",
    "increment()\n",
    "print(f\"Counter after: {counter}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "143ca29f",
   "metadata": {},
   "source": [
    "---\n",
    "## 5. Generator Functions\n",
    "\n",
    "Generator functions use `yield` instead of `return` to produce values one at a time."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "06d0949d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "2\n",
      "3\n",
      "4\n",
      "5\n"
     ]
    }
   ],
   "source": [
    "# Simple generator function\n",
    "def count_up_to(n):\n",
    "    count = 1\n",
    "    \n",
    "    while count <= n:\n",
    "        yield count\n",
    "        count += 1\n",
    "\n",
    "# Use the generator\n",
    "for num in count_up_to(5):\n",
    "    print(num)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7238a835",
   "metadata": {},
   "source": [
    "### 5.1. Next\n",
    "\n",
    "Use `next()` to manually get values from a generator."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "815adc12",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "2\n",
      "3\n"
     ]
    }
   ],
   "source": [
    "# Create a generator\n",
    "gen = count_up_to(3)\n",
    "\n",
    "# Get values one at a time using next()\n",
    "print(next(gen))  # 1\n",
    "print(next(gen))  # 2\n",
    "print(next(gen))  # 3\n",
    "# print(next(gen))  # This would cause StopIteration error"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "80aa8c1d",
   "metadata": {},
   "source": [
    "### 5.2. Iteration"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "3e745771",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "2\n",
      "3\n"
     ]
    }
   ],
   "source": [
    "gen = count_up_to(3)\n",
    "\n",
    "for number in gen:\n",
    "    print(number)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "28f077cd",
   "metadata": {},
   "source": [
    "---\n",
    "## 6. Lambda Functions\n",
    "\n",
    "Lambda functions are small, anonymous functions defined in a single line."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "2fe55e48",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "25\n"
     ]
    }
   ],
   "source": [
    "# Regular function\n",
    "def square(x):\n",
    "    return x ** 2\n",
    "\n",
    "print(square(5))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "443e0f89",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "25\n"
     ]
    }
   ],
   "source": [
    "# Lambda function (equivalent)\n",
    "square_lambda = lambda x: x ** 2\n",
    "\n",
    "print(square_lambda(5))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "5cf0a2f0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10\n"
     ]
    }
   ],
   "source": [
    "# Lambda with multiple arguments\n",
    "add = lambda a, b: a + b\n",
    "print(add(3, 7))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "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.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
