# Python函数教程
# =============

# 1. 函数的基本定义和调用
# --------------------

# 定义一个简单的函数
def greet():
    print("Hello, World!")

# 调用函数
greet()

# 2. 带参数的函数
# -------------

# 带位置参数的函数
def greet_person(name):
    print(f"Hello, {name}!")

greet_person("Alice")

# 带多个参数的函数
def add_numbers(a, b):
    return a + b

result = add_numbers(5, 3)
print(f"5 + 3 = {result}")

# 3. 默认参数
# ----------

def greet_with_title(name, title="Mr."):
    print(f"Hello, {title} {name}!")

greet_with_title("Smith")  # 使用默认title
greet_with_title("Smith", "Dr.")  # 覆盖默认title

# 4. 关键字参数
# ------------

def describe_pet(animal_type, pet_name):
    print(f"I have a {animal_type} named {pet_name}.")

# 使用位置参数
describe_pet("dog", "Buddy")

# 使用关键字参数
describe_pet(pet_name="Whiskers", animal_type="cat")

# 5. 可变参数
# ----------

# *args - 接收任意数量的位置参数
def make_pizza(*toppings):
    print("Making a pizza with the following toppings:")
    for topping in toppings:
        print(f"- {topping}")

make_pizza("pepperoni")
make_pizza("mushrooms", "green peppers", "extra cheese")

# **kwargs - 接收任意数量的关键字参数
def build_profile(first, last, **user_info):
    profile = {}
    profile['first_name'] = first
    profile['last_name'] = last
    for key, value in user_info.items():
        profile[key] = value
    return profile

user_profile = build_profile('albert', 'einstein', 
                           location='princeton',
                           field='physics')
print(user_profile)

# 6. 解包参数 (* 和 **)
# -------------------

a = [1, 2, 3]
b = {'a': 1, 'b': 2}
print(*a)  # 解包列表作为位置参数

# 使用 ** 解包字典作为关键字参数
def my_function(a, b, c):
    print(f"a={a}, b={b}, c={c}")

params = {'a': 1, 'b': 2, 'c': 3}
my_function(**params)  # 将字典解包为关键字参数

# 7. Lambda函数(匿名函数)
# ---------------------

# 简单的lambda函数
square = lambda x: x ** 2
print(f"Square of 5: {square(5)}")

# 在高阶函数中使用lambda
numbers = [1, 2, 3, 4, 5]
squared_numbers = list(map(lambda x: x**2, numbers))
print(f"Squared numbers: {squared_numbers}")

# 8. 作用域和全局变量
# ----------------

# 全局变量
counter = 0

def increment():
    global counter  # 声明使用全局变量
    counter += 1
    return counter

print(f"Counter before: {counter}")
print(f"Counter after increment: {increment()}")

# 9. 文档字符串
# ------------

def factorial(n):
    """
    计算n的阶乘
    
    Args:
        n (int): 需要计算阶乘的非负整数
    
    Returns:
        int: n的阶乘值
    
    Raises:
        ValueError: 当n为负数时抛出异常
    """
    if n < 0:
        raise ValueError("Factorial is not defined for negative numbers")
    if n == 0 or n == 1:
        return 1
    result = 1
    for i in range(2, n+1):
        result *= i
    return result

print(f"Factorial of 5: {factorial(5)}")

# 10. 函数装饰器基础
# ----------------

def my_decorator(func):
    def wrapper():
        print("Something is happening before the function is called.")
        func()
        print("Something is happening after the function is called.")
    return wrapper

@my_decorator
def say_hello():
    print("Hello!")

say_hello()

# 11. 递归函数
# ----------

# 添加记忆化版本的斐波那契函数
def fibonacci_memo(n, memo={}):
    """
    使用记忆化技术计算斐波那契数列的第n项
    避免重复计算,提高效率
    
    Args:
        n (int): 斐波那契数列的索引
        memo (dict): 存储已计算结果的字典
    
    Returns:
        int: 斐波那契数列第n项的值
    """
    # 如果已经计算过,直接返回结果
    if n in memo:
        return memo[n]
    
    # 基础情况
    if n <= 1:
        return n
    
    # 递归计算并存储结果
    memo[n] = fibonacci_memo(n-1, memo) + fibonacci_memo(n-2, memo)
    return memo[n]

# 使用装饰器实现记忆化功能
def memoize(func):
    cache = {}
    def wrapper(n):
        if n not in cache:
            cache[n] = func(n)
        return cache[n]
    return wrapper

@memoize
def fibonacci_decorated(n):
    """使用装饰器实现记忆化的斐波那契函数"""
    if n <= 1:
        return n
    else:
        return fibonacci_decorated(n-1) + fibonacci_decorated(n-2)

print(f"Fibonacci sequence (first 10 terms) using memoization:")
for i in range(10):
    print(f"F({i}) = {fibonacci_memo(i)}")

print(f"\nFibonacci sequence (first 10 terms) using decorator:")
for i in range(10):
    print(f"F({i}) = {fibonacci_decorated(i)}")

# 原始版本(保留作对比)
def fibonacci(n):
    """计算斐波那契数列的第n项"""
    if n <= 1:
        return n
    else:
        return fibonacci(n-1) + fibonacci(n-2)

print(f"Fibonacci sequence (first 10 terms):")
for i in range(10):
    print(f"F({i}) = {fibonacci(i)}")