Parametry a argumenty

Toto téma popisuje podporu jazyka pro definování parametrů a předávání argumentů funkcím, metodám a vlastnostem. Obsahuje informace o tom, jak předávat odkazy a jak definovat a používat metody, které mohou mít proměnlivý počet argumentů.

Parametry a argumenty

Parametr termínu slouží k popisu názvů hodnot, u které se očekává, že se mají zadávat. Argument termínu se používá pro hodnoty zadané pro každý parametr.

Parametry je možné zadat ve formě řazené kolekce členů nebo ve složené podobě nebo v některé kombinaci těchto dvou. Argumenty můžete předat pomocí explicitního názvu parametru. Parametry metod lze zadat jako volitelné a zadat výchozí hodnotu.

Vzory parametrů

Parametry poskytované funkcím a metodám jsou obecně vzory oddělené mezerami. To znamená, že v zásadě lze některý ze vzorů popsaných ve výrazech shody použít v seznamu parametrů pro funkci nebo člena.

Metody obvykle používají formu řazené kolekce členů předávacích argumentů. Tím dosáhnete jasnějšího výsledku z pohledu jiných jazyků .NET, protože formulář řazené kolekce členů odpovídá způsobu předávání argumentů v metodách .NET.

Složený formulář se nejčastěji používá s funkcemi vytvořenými pomocí let vazeb.

Následující pseudokód ukazuje příklady řazené kolekce členů a složených argumentů.

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

Kombinované formuláře jsou možné, pokud jsou některé argumenty v řazených kolekcích členů a některé ne.

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

Další vzory lze použít také v seznamech parametrů, ale pokud vzor parametrů neodpovídá všem možným vstupům, může být v době běhu neúplná shoda. Výjimka MatchFailureException se vygeneruje, pokud hodnota argumentu neodpovídá vzorům zadaným v seznamu parametrů. Kompilátor vydá upozornění, když vzor parametrů umožňuje nekompletní shody. Nejméně jeden jiný vzor je běžně užitečný pro seznamy parametrů a to je zástupný znak. Vzor se zástupnými důkazy v seznamu parametrů použijete, když chcete jednoduše ignorovat všechny zadané argumenty. Následující kód znázorňuje použití vzoru se zástupnými znaky v seznamu argumentů.

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

Vzorec se zástupnými znaky může být užitečný vždy, když nepotřebujete předané argumenty, například v hlavním vstupním bodě programu, pokud nemáte zájem o argumenty příkazového řádku, které jsou obvykle zadány jako pole řetězců, jako v následujícím kódu.

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

Jiné vzory, které se někdy používají v argumentech, jsou as vzor a vzory identifikátorů spojené s diskriminovanými sjednoceními a aktivními vzory. Model sjednocovacího sjednocení s jedním případem můžete použít následujícím způsobem.

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

Výstup je následující.

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

Aktivní vzory můžou být užitečné například jako parametry, například při transformaci argumentu do požadovaného formátu, jako v následujícím příkladu:

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

Vzor můžete použít as k uložení odpovídající hodnoty jako místní hodnoty, jak je znázorněno na následujícím řádku kódu.

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

Dalším vzorem, který se používá příležitostně, je funkce, která ponechá poslední argument nepojmenovaný tak, že jako tělo funkce poskytne výraz lambda, který okamžitě provede shodu vzoru u implicitního argumentu. Příkladem je následující řádek kódu.

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

Tento kód definuje funkci, která vezme obecný seznam a vrátí true , pokud je seznam prázdný, a false v opačném případě. Použití těchto technik může ztížit čtení kódu.

Vzory, které zahrnují neúplné shody, jsou někdy užitečné, například pokud víte, že seznamy v programu mají pouze tři prvky, můžete použít vzor podobný následujícímu v seznamu parametrů.

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

Použití vzorů, které mají neúplné shody, je nejvhodnější pro rychlé vytváření prototypů a další dočasná použití. Kompilátor vydá pro takový kód upozornění. Tyto vzory nemohou pokrýt obecný případ všech možných vstupů, a proto nejsou vhodné pro rozhraní API součástí.

Pojmenované argumenty

Argumenty pro metody lze zadat podle pozice v seznamu argumentů oddělených čárkami, nebo mohou být předány metodě explicitně zadáním názvu, následovaného znaménkem rovná se a hodnotou, která se má předat. Pokud je zadaný zadáním názvu, může se zobrazit v jiném pořadí, než který se použil v deklaraci.

Pojmenované argumenty můžou kód lépe čitelný a lépe přizpůsobitelný určitým typům změn v rozhraní API, jako je například změna pořadí parametrů metody.

Pojmenované argumenty jsou povoleny pouze pro metody, ne pro letvýrazy lambda, funkce, hodnoty funkce nebo výrazy lambda.

Následující příklad kódu ukazuje použití pojmenovaných argumentů.

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)

Při volání konstruktoru třídy můžete nastavit hodnoty vlastností třídy pomocí syntaxe podobné syntaxi pojmenovaných argumentů. Následující příklad ukazuje tuto syntaxi.

 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)

Další informace naleznete v tématu Konstruktory (F#).

Stejná technika, která má volat vlastnosti setters, platí také pro jakoukoli metodu vrácení objektu (například metody továrny):

type Widget() =
    member val Width = 1 with get,set
    member val Height = 1 with get,set

type WidgetFactory =
    static member MakeNewWidget() =
         new Widget()
    static member AdjustWidget(w: Widget) =
         w
let w = WidgetFactory.MakeNewWidget(Width=10)
w.Width // = 10
w.Height // = 1
WidgetFactory.AdjustWidget(w, Height=10)
w.Height // = 10

Všimněte si, že tito členové mohou provádět libovolnou práci, syntaxe je v podstatě krátká ruka pro volání setter vlastností před vrácením konečné hodnoty.

Volitelné parametry

Volitelný parametr pro metodu můžete zadat pomocí otazníku před názvem parametru. Volitelné parametry jsou interpretovány jako typ možnosti jazyka F#, takže je můžete dotazovat běžným způsobem, jakým jsou typy možností dotazovány, pomocí výrazu match s Some a None. Volitelné parametry jsou povoleny pouze pro členy, ne pro funkce vytvořené pomocí let vazeb.

Existující volitelné hodnoty můžete předat metodě podle názvu parametru, například ?arg=None nebo ?arg=Some(3)?arg=arg. To může být užitečné při vytváření metody, která předává volitelné argumenty jiné metodě.

Můžete také použít funkci defaultArg, která nastaví výchozí hodnotu volitelného argumentu. Funkce defaultArg přebírá volitelný parametr jako první argument a výchozí hodnotu jako druhou.

Následující příklad ukazuje použití volitelných parametrů.

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)
let conn4 = Connection(?duplex0 = None)
let conn5 = Connection(?duplex0 = Some(Full))

let optionalDuplexValue : option<DuplexType> = Some(Half)
let conn6 = Connection(?duplex0 = optionalDuplexValue)

Výstup je následující.

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

Pro účely zprostředkovatele komunikace jazyka C# a Visual Basic můžete použít atributy [<Optional; DefaultParameterValue<(...)>] v jazyce F#, aby volající viděli argument jako nepovinný. To je ekvivalentem definování argumentu jako volitelného v jazyce C# jako v MyMethod(int i = 3)jazyce .

open System
open System.Runtime.InteropServices
type C =
    static member Foo([<Optional; DefaultParameterValue("Hello world")>] message) =
        printfn $"{message}"

Nový objekt můžete také zadat jako výchozí hodnotu parametru. Člen může mít například Foo volitelný CancellationToken vstup jako vstup:

open System.Threading
open System.Runtime.InteropServices
type C =
    static member Foo([<Optional; DefaultParameterValue(CancellationToken())>] ct: CancellationToken) =
        printfn $"{ct}"

Hodnota zadaná jako argument DefaultParameterValue musí odpovídat typu parametru. Například následující možnosti nejsou povoleny:

type C =
    static member Wrong([<Optional; DefaultParameterValue("string")>] i:int) = ()

V tomto případě kompilátor vygeneruje upozornění a bude ignorovat oba atributy úplně. Všimněte si, že výchozí hodnota null musí být typu anotována, protože jinak kompilátor odvodí nesprávný typ, tj. [<Optional; DefaultParameterValue(null:obj)>] o:obj.

Předávání podle odkazu

Předání hodnoty jazyka F# odkazem zahrnuje odkazy na odkazy, což jsou spravované typy ukazatelů. Pokyny pro typ, který se má použít, jsou následující:

  • Použijte inref<'T> , pokud potřebujete jen přečíst ukazatel.
  • Použijte outref<'T> , pokud potřebujete na ukazatel psát pouze.
  • Použijte byref<'T> , pokud potřebujete číst z ukazatele i zapisovat do ukazatele.
let example1 (x: inref<int>) = printfn $"It's %d{x}"

let example2 (x: outref<int>) = x <- x + 1

let example3 (x: byref<int>) =
    printfn $"It's %d{x}"
    x <- x + 1

let test () =
    // No need to make it mutable, since it's read-only
    let x = 1
    example1 &x

    // Needs to be mutable, since we write to it
    let mutable y = 2
    example2 &y
    example3 &y // Now 'y' is 3

Vzhledem k tomu, že parametr je ukazatel a hodnota je proměnlivá, všechny změny hodnoty se zachovají po spuštění funkce.

Řazenou kolekci členů můžete použít jako návratovou hodnotu k uložení libovolných out parametrů v metodách knihovny .NET. Případně můžete s parametrem zacházet out jako s parametrem byref . Následující příklad kódu ukazuje oba způsoby.

// 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

Pole parametrů

Někdy je nutné definovat funkci, která přebírá libovolný počet parametrů heterogenního typu. Není praktické vytvořit všechny možné přetížené metody, které by zohlednily všechny typy, které by bylo možné použít. Implementace .NET poskytují podporu těchto metod prostřednictvím funkce pole parametrů. Metodu, která přebírá pole parametrů v podpisu, lze poskytnout libovolný počet parametrů. Parametry jsou vloženy do pole. Typ prvků pole určuje typy parametrů, které lze předat funkci. Pokud definujete pole parametrů jako System.Object typ elementu, klientský kód může předat hodnoty libovolného typu.

V jazyce F# lze pole parametrů definovat pouze v metodách. Nelze je použít v samostatných funkcích nebo funkcích definovaných v modulech.

Pomocí atributu ParamArray definujete pole parametrů. Atribut ParamArray lze použít pouze u posledního parametru.

Následující kód znázorňuje volání metody .NET, která přebírá pole parametrů a definici typu v jazyce F#, která má metodu, která přebírá pole parametrů.

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

Při spuštění v projektu je výstup předchozího kódu následující:

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

Viz také