Если функция принимает и / или возвращает функцию, она называется функцией высшего порядка (HOF). Для неопытных программистов, происходящих из C, C ++ или Java, функции высшего порядка звучат как волшебство, но они очень просты. Представьте себе простую функцию, которая возвращает результат 2 + 3:
(define (foo) (+ 2 3)) ;; (foo) => 5
Это скучная функция, она всегда добавляет 2 к 3. Что если мы ее обобщим, чтобы она добавляла 2 не только к 3, но и к любому числу, предоставленному пользователем?
(define (foo n) (+ 2 n)) ;; (foo 10) => 12
Когда язык не поддерживает функции высшего порядка, вы вынуждены думать, что функции и значения (например, числа, логические значения, списки) - это две разные вещи. Но функциональное программирование (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
Когда вы понимаете, что функция может обрабатываться так же, как обрабатывается число или строка, анонимные функции (называемые «лямбда-выражения» на жаргонном языке FP) имеют полный смысл. Анонимные функции на самом деле более простые и «нормальные», чем обычные именованные функции, именованные функции - это просто анонимные функции, помещенные в переменную, точно так же, как мы помещаем число в переменную, чтобы использовать его несколько раз.
(+ 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 позволяют нам обобщать наши функции, чтобы сделать их сверхгибкими. Если вы посмотрите на свою функцию, увидите логику, стоящую за ней, вы поймете, что если что-то работает с вашими данными, то что-то еще , вероятно, тоже может. Если вы сложите 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-арных функций - опять же, вы можете делать с функцией все, что хотите, и также сохранять ее в структуре данных - и вы хотите применить их все к одному и тому же номеру, и получить список результатов.
(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
, что аналогично «левому сгибу» или «левому уменьшению», является еще более мощным. Если вы действительно заинтересованы и у вас есть время, пожалуйста, прочитайте первую половину моего ответа, используя сокращение . Хотя он не написан для Scheme / Racket, но вместо этого для Common Lisp / Emacs Lisp, вы все равно можете понять идею, лежащую в основе Fold / Reduce.