Parameter und Argumente (F#)
In diesem Thema wird die Sprachunterstützung für das Definieren von Parametern und das Übergeben von Argumenten an Funktionen, Methoden und Eigenschaften beschrieben.Es enthält Informationen zur Übergabe nach Verweis und zur Art und Weise, wie Methoden definiert und verwendet werden können, die eine variable Anzahl von Argumenten akzeptieren können.
Parameter und Argumente
Als Parameter werden die Namen für Werte bezeichnet, deren Bereitstellung erwartet wird.Als Argument werden die für die einzelnen Parameter bereitgestellten Werte bezeichnet.
Parameter können in Tupel- oder Curry-Form oder in einer Kombination der beiden Formen angegeben werden.Sie können Argumente mit einem expliziten Parameternamen übergeben.Parameter von Methoden können als optional angegeben werden, und Sie können ihnen einen Standardwert zuweisen.
Parametermuster
Für Funktionen und Methoden bereitgestellte Parameter sind in der Regel durch Leerzeichen getrennte Muster.Dies bedeutet, dass im Prinzip jedes in Vergleichsausdrücke (F#) beschriebene Muster in einer Parameterliste für eine Funktion oder einen Member verwendet werden kann.
Argumente von Methoden werden i. d.R. in Tupelform übergeben.Dies ergibt aus der Perspektive anderer .NET-Sprachen ein klareres Ergebnis, da die Tupelform mit dem Verfahren zum Übergeben von Argumenten in NET-Methoden übereinstimmt.
Die Curry-Form wird am häufigsten für Funktionen verwendet, die mit let-Bindungen erstellt werden.
Der folgende Pseudocode zeigt Beispiele für Tupel und Curry-Argumente.
// Tuple form.
member this.SomeMethod(param1, param2) = ...
// Curried form.
let function1 param1 param2 = ...
Kombinierte Formen sind möglich, wenn nur ein Teil der Argumente in Tupeln enthalten ist.
let function2 param1 (param2a, param2b) param3 = ...
In Parameterlisten können auch andere Muster verwendet werden. Wenn das Parametermuster jedoch nicht mit allen potenziellen Eingaben übereinstimmt, tritt zur Laufzeit möglicherweise eine unvollständige Übereinstimmung auf.Wenn der Wert eines Arguments nicht mit den in der Parameterliste angegebenen Mustern übereinstimmt, wird die Ausnahme MatchFailureException generiert.Die Compiler gibt eine Warnung aus, wenn ein Parametermuster unvollständige Übereinstimmungen ermöglicht.Für Parameterlisten ist in der Regel mindestens ein weiteres Muster hilfreich, und zwar das Platzhaltermuster.Sie verwenden das Platzhaltermuster in einer Parameterliste, wenn alle angegebenen Argumente ignoriert werden sollen.Im folgenden Code wird die Verwendung des Platzhaltermusters in einer Argumentliste veranschaulicht.
let makeList _ = [ for i in 1 .. 100 -> i * i ]
// The arguments 100 and 200 are ignored.
let list1 = makeList 100
let list2 = makeList 200
Das Platzhaltermuster kann hilfreich sein, wenn Sie die übergebenen Argumente nicht benötigen, z. B. im Haupteinstiegspunkt eines Programms, wobei die Befehlszeilenargumente, die normalerweise als Zeichenfolgenarray angegeben werden, für Sie ohne Belang sind, wie im folgenden Code.
[<EntryPoint>]
let main _ =
printfn "Entry point!"
0
Weitere Muster, die manchmal in Argumenten verwendet werden, sind das as-Muster sowie Unterscheidungs-Unions und aktiven Mustern zugeordnete Bezeichnermuster.Sie können das Unterscheidungs-Union-Muster mit nur einem Fall wie folgt verwenden.
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
Die Ausgabe lautet wie folgt.
Data begins at 0 and ends at 4 in string Et tu, Brute?
Et tu
Mit aktiven Mustern können als Parameter nützlich sein, z. B. wenn ein Argument in ein gewünschtes Format transformiert, wie im folgenden Beispiel gezeigt wird:
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
Sie können mithilfe des as-Musters einen übereinstimmenden Wert als lokalen Wert speichern, wie in der folgenden Codezeile gezeigt.
let GetSubstring2 (Slice(p0, p1, text) as s) = s
Ein weiteres Muster, das gelegentlich verwendet wird, ist eine Funktion, deren letztes Argument unbenannt bleibt, indem als Funktionsrumpf ein Lambda-Ausdruck bereitgestellt wird, der sofort einen Mustervergleich für das implizite Argument ausführt.Ein Beispiel hierfür ist die folgende Codezeile.
let isNil = function [] -> true | _::_ -> false
In diesem Code wird eine Funktion definiert, die eine generische Liste akzeptiert und true zurückgibt, wenn die Liste leer ist, und andernfalls false zurückgibt.Die Verwendung dieser Verfahren kann die Lesbarkeit von Code verringern.
Gelegentlich sind Muster mit unvollständigen Übereinstimmungen hilfreich. Wenn Sie z. B. wissen, dass die Listen im Programm nur drei Elemente enthalten, können Sie in einer Parameterliste ein Muster wie das folgende verwenden.
let sum [a; b; c;] = a + b + c
Die Verwendung von Mustern mit unvollständigen Übereinstimmungen sollte auf die schnelle Prototyperstellung und andere temporäre Verwendungen beschränkt werden.Der Compiler gibt eine Warnung für solchen Code aus.Diese Muster entsprechen nicht dem allgemeinen Fall (alle möglichen Eingaben), daher sind sie nicht für Komponenten-APIs geeignet.
Benannte Argumente
Argumente für Methoden können anhand der Position in einer durch Trennzeichen getrennten Argumentliste angegeben werden, oder sie können explizit durch Angabe des Namens, gefolgt von einem Gleichheitszeichen und dem zu übergebenden Wert, an eine Methode übergeben werden.Bei einer Angabe über den Namen kann die Reihenfolge von der in der Deklaration abweichen.
Benannte Argumente können die Lesbarkeit des Codes erhöhen und erleichtern die Anpassung von Code an bestimmte Typen von Änderungen in der API, z. B. die Neuanordnung von Methodenparametern.
Benannte Argumente sind nur für Methoden zulässig, nicht für Funktionen mit let-Bindung, Funktionswerte oder Lambda-Ausdrücke.
Im folgenden Codebeispiel wird die Verwendung benannter Argumente veranschaulicht.
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)
In einem Aufruf von einem Klassenkonstruktor, können Sie die Werte von Eigenschaften der Klasse setzen, indem Sie eine Syntax, die der von benannten Argumenten ähnelt.Im folgenden Beispiel wird diese Syntax veranschaulicht.
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)
Weitere Informationen finden Sie unter Konstruktoren (F#).
Optionale Parameter
Mit einem Fragezeichen vor dem Parameternamen können Sie einen optionalen Parameter für eine Methode angeben.Optionale Parameter werden als F#-Optionstyp interpretiert, deshalb können Sie sie mit dem regulären Verfahren zum Abfragen von Optionstypen abfragen, indem Sie einen match-Ausdruck mit Some und None verwenden.Optionale Parameter sind nur für Member zulässig, nicht für mit let-Bindungen erstellte Funktionen.
Sie können auch die Funktion defaultArg verwenden, die einen Standardwert eines optionalen Arguments festlegt.Die defaultArg-Funktion akzeptiert den optionalen Parameter als erstes Argument und den Standardwert als zweites Argument.
Das folgende Beispiel veranschaulicht die Verwendung optionaler Parameter.
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)
Die Ausgabe lautet wie folgt.
Baud Rate: 9600 Duplex: Full Parity: false
Baud Rate: 4800 Duplex: Half Parity: false
Baud Rate: 300 Duplex: Half Parity: true
Übergabe als Verweis
F# unterstützt das byref-Schlüsselwort, das angibt, dass ein Parameter als Verweis übergeben wird.Dies bedeutet, dass alle Änderungen des Werts nach der Ausführung der Funktion beibehalten werden.Für einen byref-Parameter bereitgestellte Werte müssen änderbar sein.Alternativ können Sie Referenzzellen vom entsprechenden Typ übergeben.
Die Übergabe als Verweis wurde in .NET-Sprachen entwickelt, um eine Funktion mehrere Werte zurückgeben zu lassen.In F# können Sie zu diesem Zweck ein Tupel zurückgeben oder eine änderbare Referenzzelle als Parameter verwenden.Der byref-Parameter wird hauptsächlich für die Interoperabilität mit .NET-Bibliotheken bereitgestellt.
Die folgenden Beispiele veranschaulichen die Verwendung des byref-Schlüsselworts.Wenn Sie eine Referenzzelle als Parameter verwenden, müssen Sie eine Referenzzelle als benannten Wert erstellen und diesen als Parameter verwenden, statt wie im folgenden Code im ersten Aufruf von Increment lediglich den ref-Operator hinzuzufügen.Da durch das Erstellen einer Referenzzelle eine Kopie des zugrunde liegenden Werts erstellt wird, wird mit dem ersten Aufruf nur ein temporärer Wert erhöht.
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
Sie können ein Tupel als Rückgabewert verwenden, um out-Parameter in .NET-Bibliotheksmethoden zu speichern.Alternativ können Sie den out-Parameter als byref-Parameter behandeln.Beide Verfahren werden im folgenden Codebeispiel veranschaulicht.
// 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
Parameterarrays
Gelegentlich ist es notwendig, eine Funktion zu definieren, die eine beliebige Anzahl von Parametern heterogenen Typs akzeptiert.Es ist nicht zweckmäßig, alle möglichen überladenen Methoden zu erstellen, um alle Typen zu berücksichtigen, die verwendet werden können.Die .NET-Plattform unterstützt solche Methoden mit der Parameterarrayfunktion.Eine Methode, die ein Parameterarray in ihrer Signatur akzeptiert, kann mit einer beliebigen Anzahl von Parametern bereitgestellt werden.Die Parameter werden in ein Array eingefügt.Der Typ der Arrayelemente bestimmt die Parametertypen, die an die Funktion übergeben werden können.Wenn Sie das Parameterarray mit Object als Elementtyp definieren, kann Clientcode Werte von jedem Typ übergeben.
In F# können Parameterarrays nur in Methoden definiert werden.Sie können nicht in eigenständigen Funktionen oder Funktionen verwendet werden, die in Modulen definiert sind.
Sie definieren ein Parameterarray mit dem ParamArray-Attribut.Das ParamArray-Attribut kann nur auf den letzten Parameter angewendet werden.
Im folgenden Code werden das Aufrufen einer .NET-Methode veranschaulicht, die ein Parameterarray akzeptiert, und die Definition eines Typs in F#, der über eine Methode verfügt, die ein Parameterarray akzeptiert.
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
Bei der Ausführung in einem Projekt ist die Ausgabe des vorherigen Codes wie folgt:
a 1 10 Hello world 1 True
"a"
1
10.0
"Hello world"
1u
true