如果一个函数接受和/或返回一个函数,它被称为higher-order function (HOF)。对于来自 C、C++ 或 Java 的没有经验的程序员来说,高阶函数听起来很神奇,但它们非常简单。想象一个简单的函数,它返回 2 + 3 的结果:
(define (foo) (+ 2 3)) ;; (foo) => 5
这是一个无聊的函数,它总是将 2 加到 3。如果我们将它推广,让它不仅在 3 上加 2,而且在任何用户提供的数字上加 2?
(define (foo n) (+ 2 n)) ;; (foo 10) => 12
当一种语言不支持高阶函数时,你不得不认为函数和值(例如数字、布尔值、列表)是两个不同的东西。但是functional programming (FP) 模糊了它们之间的区别。想象一下,函数和值之间的唯一区别是可以调用函数,除了您可以对函数执行任何您可以对2 或#t 或'(a b c) 执行的操作:您可以将其作为参数,或从函数返回,或存储在变量中,或将其放入列表中。例如,让我们进一步推广我们的小函数,这样它不仅可以将 2 加到 n,还可以将 2 乘以 n,或者应用任何其他可以接受两个数字的函数:
(define (foo f n) (f 2 n))
;; (foo + 10) => 12
;; (foo * 10) => 20
;; (foo expt 10) => 1024
当您意识到可以像处理数字或字符串一样处理函数时,anonymous functions(在 FP 行话中称为“lambdas”)完全有意义。匿名函数其实比普通的命名函数更基础、更“正常”,命名函数只是匿名函数放入一个变量中,就像我们把一个数字放入一个变量中多次使用一样。
(+ 2 2) ;; is no different from:
(let ((a 2)) (+ a a))
(lambda (x y) (* x y)) ;; is no different from:
(define (foo x y) (* x y)) ;; which is an abbreviation for:
(define foo (lambda (x y) (* x y))).
因此,HOF 允许我们泛化我们的函数以使其超级灵活。如果你看看你的函数,看看它背后的逻辑,你就会意识到,如果 something 对你的数据进行操作,那么 something else 可能也可以。如果将 2 个数字相加,您可能可以将它们相乘、相减或取幂,或其他任何方式。不必每次都为每个案例编写一个新函数,您可以只接受一个附加参数,该参数必须是一个函数。
在 FP 中,我们一直使用 HOF,例如,在处理列表时。 3 个函数是 FP 的基础:map、filter 和 foldl。 map 接受一个带 1 个参数的函数,将此函数应用于列表的每个元素,并返回一个包含已更改元素的新列表。 filter 接受带有 1 个参数的谓词(返回布尔值的函数),将谓词应用于列表的每个元素,并返回一个新列表,其中删除了不满足谓词的元素。
(map (lambda (n) (+ n 1)) '(1 2 3 4 5) ;; '(2 3 4 5 6)
(define (foo n) (+ n 1))
(map foo '(1 2 3 4 5)) ;; '(2 3 4 5 6)
(filter (lambda (n) (> n 3)) '(1 2 3 4 5)) ;; '(4 5)
(define (bar n) (> n 3))
(filter bar '(1 2 3 4 5)) ;; '(4 5)
想象一下,你有一个 1-arity 函数列表——同样,你可以对函数做任何你想做的事情,并将它存储在一个数据结构中——并且你想将它们都应用到同一个数字上,并且获取结果列表。
(let ((xs (list (lambda (x) (+ x 1))
(lambda (x) (* x 2))
(lambda (x) (- x)))))
(map (lambda (f) (f 10)) xs)) ;; => (11 20 -10)
结论:当一种编程语言正确地支持函数式编程概念时,高阶函数允许灵活性和通用性,这使您的代码更加强大(您可以将相同的函数用于各种用例) 和简洁(无需编写一个函数的 10 个版本)。一些高阶函数在函数式编程中被大量使用,因此您可以摆脱低级和冗长的 for 循环,而是编写一个可以做所有事情的单行代码。
注意:foldl,与“left fold”或“left reduce”相同,更强大。如果你真的有兴趣并有时间,请阅读my answer using reduce的前半部分。虽然它不是为 Scheme/Racket 编写的,而是为 Common Lisp/Emacs Lisp 编写的,但您仍然可以理解 fold/reduce 背后的想法。