Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Funktionen sind die grundlegende Programmausführungseinheit in jeder Programmiersprache. Wie in anderen Sprachen weist eine F#-Funktion einen Namen auf, kann Parameter aufweisen und Argumente annehmen und einen Textkörper aufweisen. F# unterstützt auch funktionsbezogene Programmierkonstrukte wie das Behandeln von Funktionen als Werte, die Verwendung von nicht benannten Funktionen in Ausdrücken, die Zusammensetzung von Funktionen, um neue Funktionen, geschweifte Funktionen zu bilden, und die implizite Definition von Funktionen mithilfe der teilweisen Anwendung von Funktionsargumenten.
Sie definieren Funktionen mithilfe des let Schlüsselworts oder, wenn die Funktion rekursiv ist, die let rec Schlüsselwortkombination.
Syntax
// 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
Bemerkungen
Der Funktionsname ist ein Bezeichner, der die Funktion darstellt. Die Parameterliste besteht aus aufeinander folgenden Parametern, die durch Leerzeichen getrennt sind. Sie können einen expliziten Typ für jeden Parameter angeben, wie im Abschnitt "Parameter" beschrieben. Wenn Sie keinen bestimmten Argumenttyp angeben, versucht der Compiler, den Typ vom Funktionstext abzuleiten. Der Funktionstext besteht aus einem Ausdruck. Der Ausdruck, aus dem der Funktionstext besteht, ist in der Regel ein zusammengesetzter Ausdruck, der aus einer Reihe von Ausdrücken besteht, die in einem endgültigen Ausdruck enden, der den Rückgabewert darstellt. Der Rückgabetyp ist ein Doppelpunkt gefolgt von einem Typ und optional. Wenn Sie den Typ des Rückgabewerts nicht explizit angeben, bestimmt der Compiler den Rückgabetyp aus dem endgültigen Ausdruck.
Eine einfache Funktionsdefinition sieht wie folgt aus:
let f x = x + 1
Im vorherigen Beispiel ist der Funktionsname ist f, das Argument x, das den Typ int verweist, der Funktionsrumpf ist x + 1, und der Rückgabewert ist vom Typ int.
Funktionen können markiert inlinewerden. Informationen zu inlineden Inlinefunktionen finden Sie unter "Inlinefunktionen".
Geltungsbereich
Auf einer anderen Ebene als Modulbereich ist es kein Fehler, einen Wert oder Funktionsnamen wiederzuverwenden. Wenn Sie einen Namen wiederverwenden, schattiert der name, der später deklariert wurde, den zuvor deklarierten Namen. Im Bereich der obersten Ebene in einem Modul müssen namen jedoch eindeutig sein. Der folgende Code erzeugt beispielsweise einen Fehler, wenn er im Modulbereich angezeigt wird, aber nicht, wenn er in einer Funktion angezeigt wird:
let list1 = [ 1; 2; 3 ]
// Error: duplicate definition.
let list1 = []
let function1 () =
let list1 = [ 1; 2; 3 ]
let list1 = []
list1
Der folgende Code ist jedoch auf jeder Ebene zulässig:
let list1 = [ 1; 2; 3 ]
let sumPlus x =
// OK: inner list1 hides the outer list1.
let list1 = [ 1; 5; 10 ]
x + List.sum list1
Die Parameter
Namen von Parametern werden nach dem Funktionsnamen aufgelistet. Sie können einen Typ für einen Parameter angeben, wie im folgenden Beispiel gezeigt:
let f (x: int) = x + 1
Wenn Sie einen Typ angeben, folgt er dem Namen des Parameters und wird durch einen Doppelpunkt vom Namen getrennt. Wenn Sie den Typ für den Parameter weglassen, wird der Parametertyp vom Compiler abgeleitet. In der folgenden Funktionsdefinition wird das Argument x beispielsweise als Typ int abgeleitet, da 1 vom Typ intist.
let f x = x + 1
Der Compiler versucht jedoch, die Funktion so generisch wie möglich zu machen. Beachten Sie beispielsweise den folgenden Code:
let f x = (x, x)
Die Funktion erstellt ein Tupel aus einem Argument eines beliebigen Typs. Da der Typ nicht angegeben ist, kann die Funktion mit jedem Argumenttyp verwendet werden. Weitere Informationen finden Sie unter "Automatische Generalisierung".
Funktionskörper
Ein Funktionstext kann Definitionen lokaler Variablen und Funktionen enthalten. Solche Variablen und Funktionen befinden sich im Textkörper der aktuellen Funktion, aber nicht außerhalb der Funktion. Sie müssen einen Einzug verwenden, um anzugeben, dass sich eine Definition in einem Funktionstext befindet, wie im folgenden Beispiel gezeigt:
let cylinderVolume radius length =
// Define a local value pi.
let pi = 3.14159
length * pi * radius * radius
Weitere Informationen finden Sie unter Codeformatierungsrichtlinien und ausführliche Syntax.
Rückgabewerte
Der Compiler verwendet den endgültigen Ausdruck in einem Funktionstext, um den Rückgabewert und den Typ zu bestimmen. Der Compiler leitet möglicherweise den Typ des endgültigen Ausdrucks aus vorherigen Ausdrücken ab. In der Im vorherigen Abschnitt gezeigten Funktion cylinderVolumewird der Typ des pi zu verwendenden Literals 3.14159floatbestimmt. Der Compiler verwendet den Typ des pi Ausdrucks, um den Typ des zu bestimmenden Ausdrucks length * pi * radius * radius zu floatbestimmen. Daher lautet floatder Gesamtrücklauftyp der Funktion .
Um den Rückgabetyp explizit anzugeben, schreiben Sie den Code wie folgt:
let cylinderVolume radius length : float =
// Define a local value pi.
let pi = 3.14159
length * pi * radius * radius
Wie der Code oben geschrieben wird, wendet der Compiler float auf die gesamte Funktion an. Wenn Sie es auch auf die Parametertypen anwenden möchten, verwenden Sie den folgenden Code:
let cylinderVolume (radius: float) (length: float) : float
Aufrufen einer Funktion
Sie rufen Funktionen auf, indem Sie den Funktionsnamen gefolgt von einem Leerzeichen und dann durch Leerzeichen getrennte Argumente angeben. Um beispielsweise den FunktionszylinderVolume aufzurufen und das Ergebnis dem Wert "vol" zuzuweisen, schreiben Sie den folgenden Code:
let vol = cylinderVolume 2.0 3.0
Teilweise Anwendung von Argumenten
Wenn Sie weniger als die angegebene Anzahl von Argumenten angeben, erstellen Sie eine neue Funktion, die die verbleibenden Argumente erwartet. Diese Methode für die Behandlung von Argumenten wird als Zahlenverarbeitung bezeichnet und ist ein Merkmal funktionaler Programmiersprachen wie F#. Angenommen, Sie arbeiten mit zwei Rohrgrößen: einer hat einen Radius von 2,0 und der andere hat einen Radius von 3,0. Sie können Funktionen erstellen, die das Pipevolumen wie folgt bestimmen:
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
Anschließend würden Sie das letzte Argument bei Bedarf für verschiedene Rohrlängen der beiden unterschiedlichen Größen angeben:
let length1 = 30.0
let length2 = 40.0
let smallPipeVol1 = smallPipeVolume length1
let smallPipeVol2 = smallPipeVolume length2
let bigPipeVol1 = bigPipeVolume length1
let bigPipeVol2 = bigPipeVolume length2
Rekursive Funktionen
Rekursive Funktionen sind Funktionen , die sich selbst aufrufen. Sie erfordern, dass Sie das Rec-Schlüsselwort nach dem Let-Schlüsselwort angeben. Rufen Sie die rekursive Funktion innerhalb des Textkörpers der Funktion genauso auf, wie Sie einen Funktionsaufruf aufrufen würden. Die folgende rekursive Funktion berechnet die n-Fibonacci-Zahl. Die Fibonacci-Nummernsequenz ist seit der Antike bekannt und ist eine Sequenz, in der jede aufeinander folgende Zahl die Summe der vorherigen beiden Zahlen in der Sequenz ist.
let rec fib n =
if n < 2 then 1 else fib (n - 1) + fib (n - 2)
Einige rekursive Funktionen können den Programmstapel überlaufen oder ineffizient ausgeführt werden, wenn Sie sie nicht mit Sorgfalt und mit dem Bewusstsein spezieller Techniken schreiben, z. B. die Verwendung von Tail-Rekursion, Akkumulatoren und Fortsetzungen.
Funktionswerte
In F# werden alle Funktionen als Werte betrachtet; tatsächlich werden sie als Funktionswerte bezeichnet. Da Funktionen Werte sind, können sie als Argumente für andere Funktionen oder in anderen Kontexten verwendet werden, in denen Werte verwendet werden. Nachfolgend sehen Sie ein Beispiel für eine Funktion, die einen Funktionswert als Argument verwendet:
let apply1 (transform: int -> int) y = transform y
Sie geben den Typ eines Funktionswerts mithilfe des -> Tokens an. Auf der linken Seite dieses Tokens ist der Typ des Arguments, und auf der rechten Seite ist der Rückgabewert. Im vorherigen Beispiel ist eine Funktion, apply1 die eine Funktion transform als Argument verwendet, wobei transform es sich um eine Funktion handelt, die eine ganze Zahl akzeptiert und eine weitere ganze Zahl zurückgibt. Der folgende Code zeigt die Verwendung apply1:
let increment x = x + 1
let result1 = apply1 increment 100
Der Wert von result 101, nachdem der vorherige Code ausgeführt wird.
Mehrere Argumente werden durch aufeinander folgende -> Token getrennt, wie im folgenden Beispiel gezeigt:
let apply2 (f: int -> int -> int) x y = f x y
let mul x y = x * y
let result2 = apply2 mul 10 20
Das Ergebnis ist 200.
Lambda-Ausdrücke
Ein Lambda-Ausdruck ist eine unbenannte Funktion. In den vorherigen Beispielen können Sie anstelle der Definition benannter Funktionen inkrementierung und mul Lambda-Ausdrücke wie folgt verwenden:
let result3 = apply1 (fun x -> x + 1) 100
let result4 = apply2 (fun x y -> x * y) 10 20
Sie definieren Lambda-Ausdrücke mithilfe des fun Schlüsselworts. Ein Lambda-Ausdruck ähnelt einer Funktionsdefinition, mit der Ausnahme, dass anstelle des = Tokens das -> Token verwendet wird, um die Argumentliste vom Funktionstext zu trennen. Wie in einer regulären Funktionsdefinition können die Argumenttypen explizit abgeleitet oder angegeben werden, und der Rückgabetyp des Lambda-Ausdrucks wird vom Typ des letzten Ausdrucks im Textkörper abgeleitet. Weitere Informationen finden Sie unter Lambda-Ausdrücke: Das fun Schlüsselwort.
Rohrleitungen
Der Pipeoperator |> wird bei der Verarbeitung von Daten in F# häufig verwendet. Mit diesem Operator können Sie "Pipelines" von Funktionen flexibel einrichten. Pipelining ermöglicht die Verkettung von Funktionsaufrufen als aufeinander folgende Vorgänge:
let result = 100 |> function1 |> function2
Im folgenden Beispiel wird erläutert, wie Sie diese Operatoren verwenden können, um eine einfache funktionale Pipeline zu erstellen:
/// 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
Das Ergebnis ist [2; 10; 26]. Im vorherigen Beispiel werden Listenverarbeitungsfunktionen verwendet, die zeigen, wie Funktionen zum Verarbeiten von Daten beim Erstellen von Pipelines verwendet werden können. Der Pipelineoperator selbst wird in der F#-Kernbibliothek wie folgt definiert:
let (|>) x f = f x
Funktionskomposition
Funktionen in F# können aus anderen Funktionen zusammengesetzt werden. Die Zusammensetzung von zwei Funktionen Funktion1 und Funktion2 ist eine weitere Funktion, die die Anwendung von Funktion1 gefolgt von der Anwendung von Funktion2 darstellt:
let function1 x = x + 1
let function2 x = x * 2
let h = function1 >> function2
let result5 = h 100
Das Ergebnis ist 202.
Der Kompositionsoperator akzeptiert zwei Funktionen und gibt eine Funktion zurück. Im Gegensatz dazu akzeptiert der Pipelineoperator >>|> einen Wert und eine Funktion und gibt einen Wert zurück. Das folgende Codebeispiel zeigt den Unterschied zwischen den Pipeline- und Kompositionsoperatoren, indem die Unterschiede in den Funktionssignaturen und der Verwendung gezeigt werden.
// 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
Überladungsfunktionen
Sie können Methoden eines Typs, aber keine Funktionen überladen. Weitere Informationen finden Sie unter "Methoden".