1
0
wiki/FormalSciences/ComputerScience/ProgrammingLanguage/Python/2.核心进阶/2.2-函数式编程.md
2024-10-13 20:52:05 +08:00

253 lines
8.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: 函数式编程
description: Python 函数式编程
keywords:
- Python
- 函数式编程
tags:
- Python/进阶
- 技术/程序语言
author: 7Wate
date: 2023-08-11
---
## 函数式编程
### 函数式编程是什么
函数式编程Functional Programming, FP是一种编程范式**主张用数学上的函数方式构建结构和元素之间的关系,而不是改变状态和数据。**在函数式编程中,函数是第一公民,这意味着函数可以被传递、返回和操作,就像其他的数据类型一样。函数在这里是「纯」的,意味着相同的输入始终产生相同的输出,并且没有副作用。
```Python
# 函数作为参数传递给另一个函数
def apply(func, value):
return func(value)
result = apply(lambda x: x*2, 5) # 输出10
```
### 函数式编程与其他编程范式的区别
函数式编程与命令式编程的区别在于,命令式编程关注如何完成任务,强调程序状态和改变状态的语句,而**函数式编程注重数据的映射和组合。**面向对象编程重视对象及其之间的交互,而函数式编程重视函数和数据处理。
### 函数式编程的优势和局限性
**优势**
- **简洁性**:函数式编程往往更简洁,可以用更少的代码做更多的事情。
- **可维护性**:由于函数式编程的代码没有副作用,它通常更容易维护和调试。
- **可重用性**:函数是高度模块化的,可以在多个地方重用。
**局限性**
- **内存使用**:由于函数式编程倾向于复制数据而不是改变它,它可能使用更多的内存。
- **难度**:对于不熟悉该范式的开发者来说,函数式编程可能较难学习。
## Python 中的基础函数式工具
Python 提供了一些内置的函数式工具,如 `lambda``map()``filter()` 和 `reduce()`,它们可以帮助你以函数式的方式处理数据。
### `lambda`
`lambda` 允许我们定义简短的匿名函数。
```python
double = lambda x: x * 2
print(double(5)) # 输出10
```
### `map()`
`map()` 函数将指定函数应用于序列的每一个元素。
```python
nums = [1, 2, 3, 4]
squared = list(map(lambda x: x**2, nums)) # 输出[1, 4, 9, 16]
```
### `filter()`
`filter()` 函数根据指定函数的判断结果来过滤序列。
```python
nums = [1, 2, 3, 4, 5]
evens = list(filter(lambda x: x % 2 == 0, nums)) # 输出[2, 4]
```
### `reduce()`
`reduce()` 函数对序列中的元素进行连续、累计地应用指定函数。
```python
from functools import reduce
nums = [1, 2, 3, 4]
product = reduce(lambda x, y: x*y, nums) # 输出24
```
### `functools` 模块
`functools` 模块提供了一些用于函数式编程的实用工具,如偏函数等。
```python
from functools import partial
def multiply(x, y):
return x * y
double = partial(multiply, 2)
print(double(4)) # 输出8
```
## 高阶函数
### 高阶函是数什么
高阶函数接受一个或多个函数作为参数,或者返回一个函数。
```python
def greet(type_):
if type_ == 'hello':
return lambda name: "Hello, " + name
else:
return lambda name: "Hi, " + name
greeting = greet('hello')
print(greeting('Alice')) # 输出'Hello, Alice'
```
### 在 Python 中创建和使用高阶函数
除了 Python 内置的如 map、filter 和 reduce 这样的高阶函数外,我们也可以创建自己的高阶函数。
```python
def apply(func, data):
return [func(item) for item in data]
nums = [1, 2, 3, 4]
result = apply(lambda x: x*2, nums) # 输出[2, 4, 6, 8]
```
## 纯函数和不变性
### 纯函数是什么
纯函数是函数式编程的核心概念之一。一个函数被认为是纯的,当它满足以下条件时:
- **给定相同的输入,总是返回相同的输出。**
- **没有任何副作用(例如修改全局状态、修改传入的参数、进行 I/O 操作等)。**
这些特性使纯函数变得可预测且容易测试。
```python
# 纯函数示例
def add(x, y):
return x + y
# 不纯的函数示例,因为它改变了外部状态
counter = 0
def increment():
global counter
counter += 1
return counter
```
### 数据的不变性
在函数式编程中,数据是不可变的。这意味着一旦一个数据结构被创建,就不能再改变它。而是每次需要修改数据时,都会返回一个新的数据副本。
这一特性增加了代码的可读性和可预测性,因为你不必担心数据在不知情的情况下被修改。
```python
# 使用列表作为示例
lst = [1, 2, 3]
# 错误的做法:修改原始列表
lst.append(4)
# 正确的做法:创建新的列表
new_lst = lst + [4]
```
## 装饰器
### Python 中的装饰器
装饰器是 Python 中的一个强大工具,它允许开发者**在不修改原始函数代码的情况下,给函数增加新的功能。**它们通常用于日志、权限检查、统计或其他跨越多个函数或方法的通用任务。
```python
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()
```
### 如何利用装饰器优化代码
如上所示,装饰器是一个返回另一个函数的函数。要使用装饰器,只需在你想要装饰的函数上方加上 `@decorator_name`
```python
def repeat(num):
def decorator_repeat(func):
def wrapper(*args, **kwargs):
for _ in range(num):
result = func(*args, **kwargs)
return result
return wrapper
return decorator_repeat
@repeat(num=4)
def greet(name):
print(f"Hello, {name}")
greet("Alice") # 输出四次 "Hello, Alice"
```
## 闭包和自由变量
闭包是一种特殊的函数,它可以记住在其所在作用域中声明的自由变量的值,即使它们在函数外部是不可用的。在更简单的语言中,闭包允许函数携带与之相关的数据。
**在 Python 中,当内部函数引用了外部函数中的变量,内部函数就被认为是闭包。**
```python
def outer_function(x):
def inner_function(y):
return x + y
return inner_function
closure = outer_function(10)
print(closure(5)) # 输出15
```
## 递归
递归是一种编程技巧,其中函数调用自身以解决较小的问题实例。递归通常与某种终止条件结合使用,以防止无限的自我调用。递归函数的经典例子是计算阶乘:
```python
# 此函数会不断调用自己直到n为1。
def factorial(n):
if n == 1:
return 1
return n * factorial(n - 1)
print(factorial(5)) # 输出120
```
### 递归、迭代对比
| 对比点 | 递归 | 迭代 |
| ------------ | ------------------------------------------------------------ | ------------------------------------------------------ |
| **直观性** | 通常更直观和更容易实现 | 通常需要使用循环结构,可能不如递归直观 |
| **自然选择** | 对于某些问题,如树的遍历,递归是自然的选择 | 对于基本的数据结构,如数组和链表,迭代是自然选择 |
| **函数调用** | 可能会导致大量的函数调用,从而可能达到调用堆栈的限制 | 由于是循环结构,不会导致函数调用的堆栈溢出 |
| **效率** | 对于大量的递归,可能不如迭代高效 | 对于简单的循环,迭代可能更加高效 |
| **内存使用** | 每次调用自己都需要额外的内存来存储变量和信息,可能会导致调用堆栈溢出 | 通常更为内存高效,因为它不需要为每次循环存储额外的信息 |
| **实现方式** | 函数调用自己,直到满足某个条件 | 使用循环结构,如 `for``while` |