柯里化 #
一、柯里化概述 #
1.1 什么是柯里化 #
柯里化(Currying)是将接受多个参数的函数转换为一系列接受单个参数的函数的技术。
1.2 普通函数 vs 柯里化函数 #
普通函数:
scala
def add(a: Int, b: Int): Int = a + b
add(1, 2)
柯里化函数:
scala
def add(a: Int)(b: Int): Int = a + b
add(1)(2)
二、定义柯里化函数 #
2.1 多参数列表语法 #
scala
def multiply(a: Int)(b: Int): Int = a * b
def greet(prefix: String)(name: String): String = s"$prefix, $name!"
def process(data: List[Int])(transform: Int => Int): List[Int] =
data.map(transform)
2.2 多个参数列表 #
scala
def fold[A, B](list: List[A])(initial: B)(f: (B, A) => B): B =
list.foldLeft(initial)(f)
fold(List(1, 2, 3))(0)(_ + _)
2.3 调用柯里化函数 #
scala
def add(a: Int)(b: Int): Int = a + b
add(10)(20)
val addTen = add(10) _
addTen(5)
三、部分应用 #
3.1 固定部分参数 #
scala
def multiply(a: Int)(b: Int)(c: Int): Int = a * b * c
val multiplyByTwo = multiply(2) _
multiplyByTwo(3)(4)
val multiplyByTwoAndThree = multiply(2)(3) _
multiplyByTwoAndThree(4)
3.2 创建专用函数 #
scala
def formatNumber(prefix: String)(suffix: String)(number: Int): String =
s"$prefix$number$suffix"
val formatCurrency = formatNumber("$")("") _
val formatPercent = formatNumber("")("%") _
formatCurrency(100)
formatPercent(75)
3.3 配置函数 #
scala
def connect(host: String)(port: Int)(timeout: Int): Connection =
???
val connectLocalhost = connect("localhost") _
val connectLocalhostDefault = connect("localhost")(8080) _
connectLocalhost(8080)(5000)
connectLocalhostDefault(5000)
四、柯里化的优势 #
4.1 参数复用 #
scala
def validate(min: Int)(max: Int)(value: Int): Boolean =
value >= min && value <= max
val isValidAge = validate(0)(150) _
val isValidPercent = validate(0)(100) _
isValidAge(25)
isValidPercent(105)
4.2 配置与数据分离 #
scala
def process(config: Config)(data: Data): Result =
???
val processor = process(defaultConfig) _
val result1 = processor(data1)
val result2 = processor(data2)
4.3 类型推断辅助 #
scala
def eq[A](a: A)(b: A): Boolean = a == b
eq(1)(2)
eq("hello")("world")
4.4 构建 DSL #
scala
object DSL:
def select(columns: String*): QueryBuilder = ???
class QueryBuilder(columns: Seq[String]):
def from(table: String): FromBuilder = ???
class FromBuilder(columns: Seq[String], table: String):
def where(condition: String): WhereBuilder = ???
import DSL._
select("id", "name")
.from("users")
.where("age > 18")
五、现有函数柯里化 #
5.1 使用 curried 方法 #
scala
def add(a: Int, b: Int): Int = a + b
val curriedAdd = (add _).curried
val addFive = curriedAdd(5)
addFive(10)
5.2 手动转换 #
scala
def add(a: Int, b: Int): Int = a + b
def addCurried(a: Int)(b: Int): Int = add(a, b)
val addFive = addCurried(5) _
5.3 匿名函数柯里化 #
scala
val add = (a: Int, b: Int) => a + b
val curriedAdd = add.curried
val addFive = curriedAdd(5)
addFive(10)
六、反柯里化 #
6.1 使用 Function.uncurried #
scala
def add(a: Int)(b: Int): Int = a + b
val uncurriedAdd = Function.uncurried(add _)
uncurriedAdd(5, 10)
6.2 手动转换 #
scala
def add(a: Int)(b: Int): Int = a + b
def addUncurried(a: Int, b: Int): Int = add(a)(b)
七、柯里化与高阶函数 #
7.1 柯里化 map/filter #
scala
def map[A, B](f: A => B)(list: List[A]): List[B] =
list.map(f)
def filter[A](p: A => Boolean)(list: List[A]): List[A] =
list.filter(p)
val doubleAll = map((_: Int) * 2) _
val filterEvens = filter((_: Int) % 2 == 0) _
doubleAll(List(1, 2, 3))
filterEvens(List(1, 2, 3, 4, 5))
7.2 柯里化 fold #
scala
def foldLeft[A, B](initial: B)(f: (B, A) => B)(list: List[A]): B =
list.foldLeft(initial)(f)
val sum = foldLeft(0)(_ + _) _
val product = foldLeft(1)(_ * _) _
sum(List(1, 2, 3, 4, 5))
product(List(1, 2, 3, 4, 5))
7.3 函数组合 #
scala
def compose[A, B, C](f: B => C)(g: A => B): A => C =
x => f(g(x))
val double = (x: Int) => x * 2
val addOne = (x: Int) => x + 1
val doubleThenAddOne = compose(addOne)(double)
val addOneThenDouble = compose(double)(addOne)
doubleThenAddOne(5)
addOneThenDouble(5)
八、实战示例 #
8.1 HTTP 客户端 #
scala
def httpRequest(method: String)(url: String)(body: Option[String]): Response =
???
val get = httpRequest("GET") _
val post = httpRequest("POST") _
val getUser = get("/users/1") _
val createUser = post("/users") _
getUser(None)
createUser(Some("""{"name": "Alice"}"""))
8.2 数据验证 #
scala
def validate[T](field: String)(rules: (T => Boolean, String)*)(value: T): Either[List[String], T] =
val errors = rules.collect {
case (rule, message) if !rule(value) => message
}
if errors.isEmpty then Right(value)
else Left(errors.map(e => s"$field: $e"))
val validateAge = validate("age")(
(_: Int) > 0 -> "must be positive",
(_: Int) < 150 -> "must be less than 150"
) _
validateAge(25)
validateAge(-5)
validateAge(200)
8.3 构建器模式 #
scala
case class Query(
table: String,
columns: List[String] = List("*"),
where: Option[String] = None,
limit: Option[Int] = None
)
def query(table: String): Query = Query(table)
def select(columns: String*)(q: Query): Query =
q.copy(columns = columns.toList)
def where(condition: String)(q: Query): Query =
q.copy(where = Some(condition))
def limit(n: Int)(q: Query): Query =
q.copy(limit = Some(n))
val buildQuery = query("users")
.pipe(select("id", "name"))
.pipe(where("age > 18"))
.pipe(limit(10))
九、柯里化与部分应用的区别 #
9.1 柯里化 #
scala
def add(a: Int)(b: Int): Int = a + b
val addFive = add(5) _
addFive(10)
9.2 部分应用 #
scala
def add(a: Int, b: Int): Int = a + b
val addFive = add(5, _: Int)
addFive(10)
9.3 对比 #
| 特性 | 柯里化 | 部分应用 |
|---|---|---|
| 定义 | 多参数列表 | 单参数列表 |
| 调用 | f(a)(b) | f(a, b) |
| 部分应用 | f(a) _ | f(a, _: B) |
| 类型推断 | 更好 | 一般 |
十、总结 #
柯里化要点 #
- 将多参数函数转换为单参数函数链
- 支持部分应用和参数复用
- 有助于类型推断
- 适合构建 DSL
使用场景 #
| 场景 | 示例 |
|---|---|
| 参数复用 | validate(min)(max)(value) |
| 配置分离 | process(config)(data) |
| 类型推断 | eqA(b) |
| DSL 构建 | select().from().where() |
下一步,让我们学习 偏函数!
最后更新:2026-03-27