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


Параметры и аргументы (F#)

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

Параметры и аргументы

Параметрами называются имена значений, которые требуется предоставить.Аргументами называются значения, предоставляемые для каждого параметра.

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

Шаблоны параметров

Параметры, передаваемые функциям и методам, как правило, являются шаблонами, разделенными пробелами.Это означает, что в принципе, любой из шаблонов, описанных в разделе Выражения match (F#), можно использовать в списке параметров для функции или члена.

Аргументы обычно передаются методам в виде кортежей.Это обеспечивает более четкие результаты относительно других языков .NET, поскольку передача аргументов в кортеже аналогична способу их передаче в методах .NET.

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

В следующем псевдокоде приведены примеры аргументов в виде кортежа и в каррированной форме.

// Tuple form.
member this.SomeMethod(param1, param2) = ...
// Curried form.
let function1 param1 param2 = ...

Также можно использовать комбинированные формы, когда лишь часть аргументов передается в кортеже.

let function2 param1 (param2a, param2b) param3 = ...

В списках параметров можно использовать и другие шаблоны, но если шаблон параметров покрывает не все возможные варианты указания параметров, может возникнуть неполное соответствие во время выполнения.Если значение аргумента не совпадает с шаблонами, указанными в списке параметров, выдается исключение MatchFailureException.Компилятор выдает предупреждение, если шаблон параметров оставляет возможность неполных совпадений.В списках параметров часто имеет смысл использовать шаблон с подстановочным знаком.Этот шаблон используется, если требуется игнорировать любые передаваемые аргументы.В следующем коде демонстрируется использование шаблона с подстановочным знаком в списке аргументов.

let makeList _ = [ for i in 1 .. 100 -> i * i ]
// The arguments 100 and 200 are ignored.
let list1 = makeList 100
let list2 = makeList 200

Шаблон с подстановочным знаком можно использовать, когда передаваемые аргументы не требуются, например в главной точке входа программы, если не требуется обрабатывать аргументы командной строки, обычно передаваемые в виде массива строк; пример такого случая показан в следующем коде.

[<EntryPoint>]
let main _ =
    printfn "Entry point!"
    0

В аргументах также может использоваться шаблон as и шаблоны идентификаторов, связанные с размеченными объединениями и активными шаблонами.Шаблон с одиночным размеченным объединением можно использовать следующим образом.

type Slice = Slice of int * int * string

let GetSubstring1 (Slice(p0, p1, text)) = 
    printfn "Data begins at %d and ends at %d in string %s" p0 p1 text
    text.[p0..p1]

let substring = GetSubstring1 (Slice(0, 4, "Et tu, Brute?"))
printfn "Substring: %s" substring

Выходные данные выглядят следующим образом.

Data begins at 0 and ends at 4 in string Et tu, Brute?
Et tu

Активные шаблоны могут быть полезны в качестве параметров, например, при преобразовании аргумент в нужный формат, например в следующем примере:

type Point = { x : float; y : float }
let (| Polar |) { x = x; y = y} =
    ( sqrt (x*x + y*y), System.Math.Atan (y/ x) )

let radius (Polar(r, _)) = r
let angle (Polar(_, theta)) = theta

С помощью шаблона as можно сохранить соответствующее шаблону значение как локальное значение, как показано в следующей строке кода.

let GetSubstring2 (Slice(p0, p1, text) as s) = s

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

let isNil = function [] -> true | _::_ -> false

В этом коде определяется функция, принимающая универсальный список и возвращающая значение true, если список пуст, и значение false в противном случае.Использование таких приемов может сделать код менее читабельным.

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

let sum [a; b; c;] = a + b + c

Шаблоны с неполным совпадением рекомендуется использовать только для создания прототипов и для других временных задач.При компиляции такого кода будет выдано предупреждение.Такие шаблоны не могут охватывать все возможные варианты входных данных, а потому непригодны для создания API-интерфейсов компонентов.

Именованные аргументы

Аргументы методов могут указываться по положению (в виде разделенного запятыми списка аргументов) или передаваться методу явно, по имени (имя аргумента, затем знак "равно" и значение аргумента).Если аргументы указываются по имени, их порядок может отличаться от заданного при объявлении метода.

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

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

В следующем примере кода показано использование именованных аргументов.

type SpeedingTicket() =
    member this.GetMPHOver(speed: int, limit: int) = speed - limit

let CalculateFine (ticket : SpeedingTicket) =
    let delta = ticket.GetMPHOver(limit = 55, speed = 70)
    if delta < 20 then 50.0 else 100.0

let ticket1 : SpeedingTicket = SpeedingTicket()
printfn "%f" (CalculateFine ticket1)

Вызову конструктор класса можно задать значения свойств класса с помощью синтаксиса аналогично параметрам именованных аргументов.Этот синтаксис показан в следующем примере.

 type Account() =
    let mutable balance = 0.0
    let mutable number = 0
    let mutable firstName = ""
    let mutable lastName = ""
    member this.AccountNumber
       with get() = number
       and set(value) = number <- value
    member this.FirstName
       with get() = firstName
       and set(value) = firstName <- value
    member this.LastName
       with get() = lastName
       and set(value) = lastName <- value
    member this.Balance
       with get() = balance
       and set(value) = balance <- value
    member this.Deposit(amount: float) = this.Balance <- this.Balance + amount
    member this.Withdraw(amount: float) = this.Balance <- this.Balance - amount


let account1 = new Account(AccountNumber=8782108, 
                           FirstName="Darren", LastName="Parker",
                           Balance=1543.33)

Дополнительные сведения см. в разделе Конструкторы (F#).

Необязательные параметры

Для метода можно задать необязательный параметр, указав вопросительный знак перед именем этого параметра.Необязательные параметры интерпретируются как тип параметра в языке F#, поэтому их можно запрашивать как обычные типы параметра, используя выражение match с ключевыми словами Some и None.Необязательные параметры можно использовать только для членов, но не для функций, созданных с помощью привязки let.

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

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

type DuplexType =
    | Full
    | Half

type Connection(?rate0 : int, ?duplex0 : DuplexType, ?parity0 : bool) =
    let duplex = defaultArg duplex0 Full
    let parity = defaultArg parity0 false
    let mutable rate = match rate0 with
                        | Some rate1 -> rate1
                        | None -> match duplex with
                                  | Full -> 9600
                                  | Half -> 4800
    do printfn "Baud Rate: %d Duplex: %A Parity: %b" rate duplex parity

let conn1 = Connection(duplex0 = Full)
let conn2 = Connection(duplex0 = Half)
let conn3 = Connection(300, Half, true)

Выходные данные выглядят следующим образом.

Baud Rate: 9600 Duplex: Full Parity: false
Baud Rate: 4800 Duplex: Half Parity: false
Baud Rate: 300 Duplex: Half Parity: true

Передача по ссылке

Язык F# поддерживает ключевое слово byref, указывающее о том, что параметр передается по ссылке.Это означает, что любые изменения значения сохраняются после выполнения функции.Значения, указанные в параметре byref, должны быть изменяемыми.Также можно передавать ссылочные ячейки соответствующего типа.

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

В следующих примерах показано использование ключевого слова byref.Обратите внимание, что при использовании в качестве параметра ссылочной ячейки необходимо создать ссылочную ячейку как именованное значение и использовать его в качестве параметра, а не просто добавить оператор ref, как показано в первом вызове метода Increment в следующем коде.Поскольку при создании ссылочной ячейки копируется базовое значение, первый вызов увеличивает только временное значение.

type Incrementor(z) =
    member this.Increment(i : int byref) =
       i <- i + z

let incrementor = new Incrementor(1)
let mutable x = 10
// Not recommended: Does not actually increment the variable.
incrementor.Increment(ref x)
// Prints 10.
printfn "%d" x  

let mutable y = 10
incrementor.Increment(&y)
// Prints 11.
printfn "%d" y 

let refInt = ref 10
incrementor.Increment(refInt)
// Prints 11.
printfn "%d" !refInt  

В качестве возвращаемого значения можно использовать кортеж для хранения любых параметров out в методах библиотеки .NET.Также можно работать с параметром out как с параметром byref.В следующем примере кода демонстрируются оба эти способа.

// TryParse has a second parameter that is an out parameter
// of type System.DateTime.
let (b, dt) = System.DateTime.TryParse("12-20-04 12:21:00")

printfn "%b %A" b dt

// The same call, using an address of operator.
let mutable dt2 = System.DateTime.Now
let b2 = System.DateTime.TryParse("12-20-04 12:21:00", &dt2)

printfn "%b %A" b2 dt2

Массивы параметров

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

В языке F# массивы параметров можно определять только в методах.Их нельзя использовать в отдельных функциях, а также в функциях, определенных в модулях.

Определение массива параметра задается с помощью атрибута ParamArray.Атрибут ParamArray можно применить только к последнему параметру.

В следующем коде демонстрируется вызов метода .NET, принимающего массив параметров, и определение типа на языке F#, содержащего метод, принимающий массив параметров.

open System

type X() =
    member this.F([<ParamArray>] args: Object[]) =
        for arg in args do
            printfn "%A" arg

[<EntryPoint>]
let main _ =
    // call a .NET method that takes a parameter array, passing values of various types
    Console.WriteLine("a {0} {1} {2} {3} {4}", 1, 10.0, "Hello world", 1u, true)

    let xobj = new X()
    // call an F# method that takes a parameter array, passing values of various types
    xobj.F("a", 1, 10.0, "Hello world", 1u, true)
    0

При выполнении в проекте выходные данные приведенного выше кода выглядят следующим образом.

a 1 10 Hello world 1 True
"a"
1
10.0
"Hello world"
1u
true

См. также

Другие ресурсы

Члены (F#)