编辑
2024-07-20
后端
00
请注意,本文编写于 293 天前,最后修改于 293 天前,其中某些信息可能已经过时。

在计算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

在柯里化中,一个函数接受一个参数并返回一个新的函数,这个新的函数接受下一个参数,依此类推,直到所有参数都被接受并最终返回结果。

Scala这样的支持函数式的编程语言,自带柯理化特性,

java
// 正常函数 // 定义一个接受两个参数的函数 def add(x: Int, y: Int): Int = x + y // 使用原始的add函数 val result1 = add(3, 4) println(s"Result of add(3, 4): $result1") // 柯理化函数 // 使用柯里化版本的add函数 val result2 = curriedAdd(3)(4) println(s"Result of curriedAdd(3)(4): $result2") // 部分应用函数 val add3 = curriedAdd(3) _ val result3 = add3(4) println(s"Result of add3(4): $result3")

Java里面我们需要依赖Java把里面的函数式接口去模拟柯理化

java
// 柯里化版本的add函数 public static Function<Integer, Function<Integer, Integer>> curriedAdd() { return a -> b -> a + b; } // 使用柯里化版本的add函数 Function<Integer, Function<Integer, Integer>> curriedAdd = curriedAdd(); Function<Integer, Integer> add3 = curriedAdd.apply(3); int result2 = add3.apply(4); System.out.println("Result of curriedAdd(3)(4): " + result2); // 进一步简化 int result3 = curriedAdd().apply(3).apply(4); System.out.println("Result of curriedAdd(3)(4): " + result3);

C++里面我们也是借助lambda和函数包装器实现

cpp
// 定义一个接受两个参数的函数 int add(int a, int b) { return a + b; } // 柯里化版本的add函数 std::function<int(int)> curriedAdd(int a) { return [a](int b) { return add(a, b); }; } // 使用原始的add函数 int result1 = add(3, 4); std::cout << "Result of add(3, 4): " << result1 << std::endl; // 使用柯里化版本的add函数 auto add3 = curriedAdd(3); int result2 = add3(4); std::cout << "Result of curriedAdd(3)(4): " << result2 << std::endl; // 进一步简化 int result3 = curriedAdd(3)(4); std::cout << "Result of curriedAdd(3)(4): " << result3 << std::endl;

Kotlin里面我们可以通过返回函数的方法来实现柯理化

kotlin
// 定义一个接受两个参数的函数 fun add(a: Int, b: Int): Int { return a + b } // 柯里化版本的add函数 fun curriedAdd(a: Int): (Int) -> Int { return { b -> add(a, b) } } fun main() { // 使用原始的add函数 val result1 = add(3, 4) println("Result of add(3, 4): $result1") // 使用柯里化版本的add函数 val add3 = curriedAdd(3) val result2 = add3(4) println("Result of curriedAdd(3)(4): $result2") // 进一步简化 val result3 = curriedAdd(3)(4) println("Result of curriedAdd(3)(4): $result3") }

Golang里函数是一等公民,可以通过返回函数来实现柯理化

go
// 定义一个接受两个参数的函数 func add(a int, b int) int { return a + b } // 柯里化版本的add函数 func curriedAdd(a int) func(int) int { return func(b int) int { return add(a, b) } } // 使用原始的add函数 result1 := add(3, 4) fmt.Println("Result of add(3, 4):", result1) // 使用柯里化版本的add函数 add3 := curriedAdd(3) result2 := add3(4) fmt.Println("Result of curriedAdd(3)(4):", result2) // 进一步简化 result3 := curriedAdd(3)(4) fmt.Println("Result of curriedAdd(3)(4):", result3)

Rust实现柯理化

rust
// 定义一个接受两个参数的函数 fn add(a: i32, b: i32) -> i32 { a + b } // 柯里化版本的add函数 fn curried_add(a: i32) -> impl Fn(i32) -> i32 { move |b| add(a, b) } // 使用原始的add函数 let result1 = add(3, 4); println!("Result of add(3, 4): {}", result1); // 使用柯里化版本的add函数 let add3 = curried_add(3); let result2 = add3(4); println!("Result of curried_add(3)(4): {}", result2); // 进一步简化 let result3 = curried_add(3)(4); println!("Result of curried_add(3)(4): {}", result3);

js也可以很简单的实现柯理化

js
// 定义一个接受两个参数的函数 function add(a, b) { return a + b; } // 柯里化版本的add函数 function curriedAdd(a) { return function(b) { return add(a, b); }; } // 使用原始的add函数 const result1 = add(3, 4); console.log("Result of add(3, 4):", result1); // 使用柯里化版本的add函数 const add3 = curriedAdd(3); const result2 = add3(4); console.log("Result of curriedAdd(3)(4):", result2); // 进一步简化 const result3 = curriedAdd(3)(4); console.log("Result of curriedAdd(3)(4):", result3);

Haskell是FP语言的老大哥,函数都是默认柯理化

kotlin
-- 定义一个接受两个参数的函数 add :: Int -> Int -> Int add x y = x + y -- 使用原始的add函数 result1 :: Int result1 = add 3 4 -- 使用柯里化版本的add函数 add3 :: Int -> Int add3 = add 3 result2 :: Int result2 = add3 4

Python实现柯理化

python
def add(a, b): return a + b def curried_add(a): def inner(b): return add(a, b) return inner add3 = curried_add(3) print(add3(4)) # 输出: 7

上面就是大部分主流语言实现柯理化的思路,观察所有语言实现思路可以得知,尽管有的语言函数不是一等公民,还有的语言内置柯理化特性,但是实现柯理化的思路都是:定义一个接受部分参数并返回一个新函数的函数。

观察以上的例子我们也可以得出柯理化的作用,参数复用(预设参数),延迟执行

还有一个特性是函数组合,我们可以将多个函数组合起来

js
const compose = (f, g) => x => f(g(x)); const add1 = x => x + 1; const multiplyBy2 = x => x * 2; const add1ThenMultiplyBy2 = compose(multiplyBy2, add1); console.log(add1ThenMultiplyBy2(3)); // 输出: 8

好啦,现在实践吧!

之前没学FP的时候不了解什么是柯理化,现在学了FP,才知道柯理化无处不在,就比如说Go后端场景

go
// 例1 type Logger func(string) func NewLogger(level string) Logger { return func(message string) { fmt.Printf("[%s] %s\n", level, message) } } func main() { errorLogger := NewLogger("ERROR") infoLogger := NewLogger("INFO") errorLogger("Something went wrong!") infoLogger("Everything is fine.") } // 例2 type HTTPRequest func(string) ([]byte, error) func NewHTTPRequest(method string, headers map[string]string) HTTPRequest { return func(url string) ([]byte, error) { req, err := http.NewRequest(method, url, nil) if err != nil { return nil, err } for key, value := range headers { req.Header.Add(key, value) } client := &http.Client{} resp, err := client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() return ioutil.ReadAll(resp.Body) } } func main() { getRequest := NewHTTPRequest("GET", map[string]string{"User-Agent": "Go-http-client/1.1"}) response, err := getRequest("https://api.github.com") if err != nil { fmt.Println("Error:", err) return } fmt.Println("Response:", string(response)) } // 例3 type QueryFunc func(string) ([]map[string]interface{}, error) func NewQueryFunc(db *sql.DB) QueryFunc { return func(query string) ([]map[string]interface{}, error) { rows, err := db.Query(query) if err != nil { return nil, err } defer rows.Close() columns, err := rows.Columns() if err != nil { return nil, err } results := []map[string]interface{}{} for rows.Next() { values := make([]interface{}, len(columns)) pointers := make([]interface{}, len(columns)) for i := range values { pointers[i] = &values[i] } if err := rows.Scan(pointers...); err != nil { return nil, err } result := make(map[string]interface{}) for i, colName := range columns { val := values[i] result[colName] = val } results = append(results, result) } return results, nil } } func main() { db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/dbname") if err != nil { fmt.Println("Error connecting to database:", err) return } defer db.Close() queryFunc := NewQueryFunc(db) results, err := queryFunc("SELECT * FROM users") if err != nil { fmt.Println("Error executing query:", err) return } for _, result := range results { fmt.Println(result) } }

其实这些算是柯理化

本文作者:yowayimono

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!