В математике и информатике функция высшего порядка (также функциональная, функциональная форма или функтор; не путать с концепцией функтора в теории категорий) - это функция, которая выполняет как минимум одно из следующих действий:

1. принимает одну или несколько функций в качестве аргументов

2. в качестве результата возвращает функцию.

Это формальное определение, но что они на самом деле и зачем они нам нужны?

Иногда важно выполнить какую-либо операцию над элементами набора данных, но эта конкретная операция определяется во время выполнения. Иногда просто невозможно узнать, какое действие мы хотим предпринять, за исключением того, что нам нужно что-то сделать в какой-то конкретный момент, но это совершенно неизвестно.

Давайте посмотрим на пример.

let x = [1..100] 
for each item in x do
    print(item)

Здесь мы печатаем каждое число в списке x, вызывая функцию print. Что, если нам нужно сделать что-то еще с каждым элементом? Нужно ли нам реализовывать все возможные кейсы, которые, по нашему мнению, мы будем использовать?

Давайте определим функцию, которая будет вызываться для каждого числа (элемента) в x.

let succ (x) -> x + 1

Мы определили функцию преемника, которая в основном получает значение и возвращает его преемника. Домен succ - это числа Int, которые можно вывести из тела succ, поскольку мы добавляем 1 к аргументу и говорим, что succ имеет тип Int - ›Int.

Теперь давайте рассмотрим предыдущий пример.

let map (f, list) = 
    for each item in list do
        f(item)

Здесь map является функцией более высокого порядка, поскольку она получила функцию f в качестве аргумента, которая будет выполняться для каждого элемента из списка. На данный момент мы определяем map, нам не нужно знать, каким будет значение f, за исключением того, что f как тип Int - ›Некоторые. Невозможно вывести возвращаемое значение f из предыдущего кода, но мы знаем, что он должен получить Int.

Другой пример - функция, возвращающая другую функцию. Давайте рассмотрим это.

let plus2 (x) =
    let succ (y) = y + 1
    
    succ (succ x)
    
let r = plus2 (1)

В этом случае plus2 является функцией типа Int - ›Int -› Int или Int - ›(Int -› Int). Обычно он принимает Int и возвращает функцию, которая принимает другой Int и возвращает Int. r будет равно 3 после оценки.

Теперь, когда мы показали, каковы эти виды функций, давайте посмотрим, как современные языки программирования определяют их.

В Scala мы можем определить функцию map типа (a - ›b, List [a]) -› List [b] следующим образом :

def map[a,b](f: a => b, v: List[a]): List[b] = {
    for ( i <- v )
      yield f(i)
  }
  
val squares = map( (x: Int) => x * x, 1.to(100).toList )

Для каждого i в v мы применяем функцию f и возвращаем ее значение. Квадраты переменных будут иметь значения квадратов чисел от 1 до 100. Обратите внимание, что мы определяем функцию в квадрате в момент вызова map, поэтому map ничего не знает о функции f. Мы можем легко сказать:

def succ (x: Int): Int = x + 1
val successors = map(succ, 1.to(100).toList)

Мы просто изменили функцию, переданную на карту, и получили другой результат. Этот уровень абстракции делает map как функцию более высокого порядка очень мощной.

Даже языки программирования, которые не работают, такие как C #, добавили эти абстракции, поэтому они могут быть гибкими для многих случаев использования.

В C # нам нужно реализовать делегат, чтобы получить ту же функциональность, но он приносит то же значение языку. Поскольку функции высшего уровня или функции очень важны, в .NET Framework уже реализованы некоторые делегаты, поэтому давайте использовать их.

List<b> map<a>(Func<a,b> f, List<a> list) 
{
    foreach (a x in list) 
    {
        yield return f(x);
    }
}
var successors = map(x => x + 1, someIntList);

Мы достигли той же функциональности, что и карта в Scala. Единственное отличие состоит в том, что C # не имеет функций высшего порядка и должен использовать конструкции делегаты, но в результате мы получаем ту же функциональность. Обратите внимание, что в C # нет необходимости объявлять тип аргументов функции succ, когда мы передаем его в map, C # предполагает это!

В F # это становится очень интересным, потому что язык поддерживает парадигму функционального программирования (FP). В F # мы делаем:

let map f = function
    |[]     ->[]
    |h::t   -> (f h) @ (t |> map f) 
let successors = [1..100] |> map (fun x -> x + 1)

Кажется сложным, но на самом деле это та же карта, которую мы видели ранее. Единственные дополнения - это стиль функционального программирования и хвостовая рекурсия, типичные для языков функционального программирования. Оператор канала | › - это просто языковая конструкция, вместо этого мы могли бы сказать map ft или map (fun x -› x + 1) [1..100 ]. Обратите внимание, что система вывода типов неплохо работает в F #. Мы не определили никаких типов; однако предполагается, что карта получает функцию и список и возвращает преобразованный список.

Функция plus2 в F # будет иметь следующий вид:

let plus2 x = 
    let succ y = y + 1  
    succ succ y

Для некоторых людей в жизни обычны функции высшего порядка. Эти функции встроены во многие языки и фреймворки; тем не менее, я видел вокруг них некоторую путаницу. Языкам, которые их не поддерживают, необходимо искать альтернативы, которые иногда нелегко использовать и реализовать. Язык C использует указатели на функции, C # использует делегаты, а другие используют объекты в качестве заменителей. С другой стороны, есть те, которые поддерживают функции более высокого порядка из коробки. Scala, Python и F # - это лишь несколько примеров.

Несмотря на то, что иногда мы используем эти функции, не задумываясь о них, мы должны знать, как они реализованы, чтобы получить от них еще большую пользу. Тем, кто плохо знаком с этой конструкцией программирования, которую мы унаследовали от лямбда исчисления, следует изучить их и познакомиться с ними, поскольку тенденция функционального программирования, похоже, набирает обороты. Функции высшего порядка являются одними из столпов языков программирования FP, и, похоже, это будущее.

Хакерский полдень - это то, с чего хакеры начинают свои дни. Мы часть семьи @AMI. Сейчас мы принимаем заявки и рады обсудить рекламные и спонсорские возможности.

Чтобы узнать больше, прочтите нашу страницу о нас, поставьте лайк / напишите нам в Facebook или просто tweet / DM @HackerNoon.

Если вам понравился этот рассказ, мы рекомендуем прочитать наши Последние технические истории и Современные технические истории. До следующего раза не воспринимайте реалии мира как должное!