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


Параметры и аргументы (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

С помощью шаблона 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#)

Журнал изменений

Дата

Журнал

Причина

Апрель 2011

Добавлен подраздел, поясняющий, как задавать значения свойств в конструкторах.

Исправление ошибки содержимого.