Поделиться через


Функции

Функции — это основная единица выполнения программы на любом языке программирования. Как и в других языках, функция F# имеет имя, может иметь параметры и принимать аргументы и иметь текст. F# также поддерживает функциональные конструкции программирования, такие как обработка функций как значений, использование неименованных функций в выражениях, композиция функций для формирования новых функций, курриированных функций и неявного определения функций путем частичного применения аргументов функций.

Вы определяете функции с помощью ключевого let слова или, если функция рекурсивна, let rec сочетание ключевых слов.

Синтаксис

// Non-recursive function definition.
let [inline] function-name parameter-list [: return-type ] = function-body
// Recursive function definition.
let rec function-name parameter-list = recursive-function-body

Замечания

Имя функции — это идентификатор, представляющий функцию. Список параметров состоит из последовательных параметров, разделенных пробелами. Можно указать явный тип для каждого параметра, как описано в разделе "Параметры". Если не указать определенный тип аргумента, компилятор пытается определить тип из текста функции. Тело функции состоит из выражения. Выражение, составляющие тело функции, обычно является составным выражением, состоящим из ряда выражений, которые завершаются в окончательном выражении, которое является возвращаемым значением. Возвращаемый тип является двоеточием, за которым следует тип и является необязательным. Если тип возвращаемого значения явно не указан, компилятор определяет тип возвращаемого значения из окончательного выражения.

Простое определение функции напоминает следующее:

let f x = x + 1

В предыдущем примере f является именем функции, x — аргументом, имеющим тип int, x + 1 является телом функции, а возвращаемое значение имеет тип int.

Функции можно пометить inline. Дополнительные сведения см. в inlineразделе "Встроенные функции".

Область действия

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

let list1 = [ 1; 2; 3 ]
// Error: duplicate definition.
let list1 = []

let function1 () =
    let list1 = [ 1; 2; 3 ]
    let list1 = []
    list1

Но следующий код является приемлемым на любом уровне области:

let list1 = [ 1; 2; 3 ]

let sumPlus x =
    // OK: inner list1 hides the outer list1.
    let list1 = [ 1; 5; 10 ]
    x + List.sum list1

Параметры

Имена параметров перечислены после имени функции. Тип параметра можно указать, как показано в следующем примере:

let f (x: int) = x + 1

Если указать тип, он следует имени параметра и отделяется от имени двоеточием. Если не указан тип параметра, тип параметра выводится компилятором. Например, в следующем определении функции аргумент x выводится как тип, так как 1 имеет тип intint.

let f x = x + 1

Однако компилятор попытается сделать функцию максимально универсальной. Например, обратите внимание на следующий код:

let f x = (x, x)

Функция создает кортеж из одного аргумента любого типа. Так как тип не указан, функция может использоваться с любым типом аргумента. Дополнительные сведения см. в разделе "Автоматическое обобщение".

Тела функций

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

let cylinderVolume radius length =
    // Define a local value pi.
    let pi = 3.14159
    length * pi * radius * radius

Дополнительные сведения см. в руководствах по форматированию кода и подробном синтаксисе.

Возвращаемые значения

Компилятор использует окончательное выражение в теле функции для определения возвращаемого значения и типа. Компилятор может определить тип окончательного выражения из предыдущих выражений. В функции cylinderVolume, показанной в предыдущем разделе, тип pi определяется из типа литерала 3.14159float. Компилятор использует тип pi для определения типа выражения length * pi * radius * radiusfloat. Таким образом, общий тип возвращаемого значения функции .float

Чтобы явно указать тип возврата, напишите код следующим образом:

let cylinderVolume radius length : float =
    // Define a local value pi.
    let pi = 3.14159
    length * pi * radius * radius

Как описано выше, компилятор применяет float ко всей функции; Если вы хотите применить его к типам параметров, используйте следующий код:

let cylinderVolume (radius: float) (length: float) : float

Вызов функции

Вы вызываете функции, указав имя функции, за которым следует пробел, а затем все аргументы, разделенные пробелами. Например, чтобы вызвать функцию цилиндраVolume и назначить результат значению vol, напишите следующий код:

let vol = cylinderVolume 2.0 3.0

Частичное применение аргументов

Если указать меньше указанного числа аргументов, создайте новую функцию, которая ожидает оставшиеся аргументы. Этот метод обработки аргументов называется карриингом и является характеристикой функциональных языков программирования, таких как F#. Например, предположим, что вы работаете с двумя размерами канала: один имеет радиус 2,0 , а другой имеет радиус 3,0. Можно создать функции, определяющие объем канала следующим образом:

let smallPipeRadius = 2.0
let bigPipeRadius = 3.0

// These define functions that take the length as a remaining
// argument:

let smallPipeVolume = cylinderVolume smallPipeRadius
let bigPipeVolume = cylinderVolume bigPipeRadius

Затем вы предоставите окончательный аргумент по мере необходимости для различных длин канала двух разных размеров:

let length1 = 30.0
let length2 = 40.0
let smallPipeVol1 = smallPipeVolume length1
let smallPipeVol2 = smallPipeVolume length2
let bigPipeVol1 = bigPipeVolume length1
let bigPipeVol2 = bigPipeVolume length2

Рекурсивные функции

Рекурсивные функции — это функции, которые вызывают себя. Им требуется указать ключевое слово rec после ключевого слова let . Вызов рекурсивной функции из текста функции так же, как и вызов любого вызова функции. Следующая рекурсивная функция вычисляет n-й номер Fibonacci. Последовательность чисел Fibonacci известна с эпохи и является последовательностью, в которой каждый последовательный номер является суммой предыдущих двух чисел в последовательности.

let rec fib n =
    if n < 2 then 1 else fib (n - 1) + fib (n - 2)

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

Значения функций

В F#все функции считаются значениями; на самом деле они называются значениями функций. Поскольку функции являются значениями, их можно использовать в качестве аргументов для других функций или в других контекстах, где используются значения. Ниже приведен пример функции, которая принимает значение функции в качестве аргумента:

let apply1 (transform: int -> int) y = transform y

Вы указываете тип значения функции с помощью маркера -> . В левой части этого токена используется тип аргумента, а справа — возвращаемое значение. В предыдущем примере apply1 — это функция, которая принимает функцию transform в качестве аргумента, где transform является функция, которая принимает целое число и возвращает другое целое число. В следующем коде показано, как использовать apply1:

let increment x = x + 1

let result1 = apply1 increment 100

Значение result будет равно 101 после выполнения предыдущего кода.

Несколько аргументов разделены последовательными -> маркерами, как показано в следующем примере:

let apply2 (f: int -> int -> int) x y = f x y

let mul x y = x * y

let result2 = apply2 mul 10 20

Результатом является 200.

Лямбда-выражения

Лямбда-выражение — это неименованная функция. В предыдущих примерах вместо определения добавочных именованных функций и mul можно использовать лямбда-выражения следующим образом:

let result3 = apply1 (fun x -> x + 1) 100

let result4 = apply2 (fun x y -> x * y) 10 20

Вы определяете лямбда-выражения с помощью ключевого fun слова. Лямбда-выражение напоминает определение функции, за исключением того, -> что вместо = маркера маркер используется для разделения списка аргументов из текста функции. Как и в обычном определении функции, типы аргументов можно вывести или указать явным образом, а возвращаемый тип лямбда-выражения выводится из типа последнего выражения в теле. Дополнительные сведения см. в лямбда-выражениях: ключевое fun слово.

Трубопроводы

Оператор канала |> широко используется при обработке данных в F#. Этот оператор позволяет устанавливать "конвейеры" функций гибким образом. Конвейерная привязка позволяет объединять вызовы функций в цепочку в виде последовательных операций:

let result = 100 |> function1 |> function2

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

/// Square the odd values of the input and add one, using F# pipe operators.
let squareAndAddOdd values =
    values |> List.filter (fun x -> x % 2 <> 0) |> List.map (fun x -> x * x + 1)

let numbers = [ 1; 2; 3; 4; 5 ]

let result = squareAndAddOdd numbers

Результатом является [2; 10; 26]. В предыдущем примере используются функции обработки списка, демонстрирующие, как функции можно использовать для обработки данных при создании конвейеров. Сам оператор конвейера определен в основной библиотеке F# следующим образом:

let (|>) x f = f x

Композиция функции

Функции в F# можно создавать из других функций. Композиция двух функций function1 и function2 — это другая функция, представляющая приложение функции1 , за которой следует приложение function2:

let function1 x = x + 1
let function2 x = x * 2
let h = function1 >> function2
let result5 = h 100

Результатом является 202.

Оператор композиции принимает две функции и возвращает функцию. В отличие от этого, оператор >>|> конвейера принимает значение и функцию и возвращает значение. В следующем примере кода показано различие между операторами конвейера и композиции, показывая различия в сигнатурах и использовании функций.

// Function composition and pipeline operators compared.
let addOne x = x + 1
let timesTwo x = 2 * x

// Composition operator
// ( >> ) : ('T1 -> 'T2) -> ('T2 -> 'T3) -> 'T1 -> 'T3
let Compose2 = addOne >> timesTwo

// Backward composition operator
// ( << ) : ('T2 -> 'T3) -> ('T1 -> 'T2) -> 'T1 -> 'T3
let Compose1 = addOne << timesTwo

// Result is 5
let result1 = Compose1 2

// Result is 6
let result2 = Compose2 2

// Pipelining
// Pipeline operator
// ( |> ) : 'T1 -> ('T1 -> 'U) -> 'U
let Pipeline2 x = addOne x |> timesTwo

// Backward pipeline operator
// ( <| ) : ('T -> 'U) -> 'T -> 'U
let Pipeline1 x = addOne <| timesTwo x

// Result is 5
let result3 = Pipeline1 2

// Result is 6
let result4 = Pipeline2 2

Перегрузка функций

Можно перегружать методы типа, но не функции. Дополнительные сведения см. в разделе "Методы".

См. также