Informationen
Das angeforderte Thema wird unten angezeigt. Es ist jedoch nicht in dieser Bibliothek vorhanden.
Dieser Artikel wurde noch nicht bewertet - Dieses Thema bewerten.

Funktionen (F#)

Funktionen sind in jeder Programmiersprache die grundlegende Einheit der Programmausführung. Wie Funktionen anderer Sprachen weist eine F#-Funktion einen Namen auf, sie kann über Parameter verfügen und Argumente akzeptieren, und sie verfügt über einen Funktionsrumpf. F# unterstützt auch Konstrukte der funktionalen Programmierung. Hierzu zählen beispielsweise das Behandeln von Funktionen als Werte, das Verwenden unbenannter Funktionen in Ausdrücken, das Zusammensetzen von Funktionen zum Bilden neuer Funktionen, Curry-Funktionen sowie die implizite Definition von Funktionen durch die partielle Anwendung von Funktionsargumenten.

Funktionen werden mit dem let-Schlüsselwort oder, wenn die Funktion rekursiv ist, mit dem let rec Schlüsselwort definiert.

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

Der function-name ist ein Bezeichner, der die Funktion darstellt. Die parameter-list 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 aus dem Funktionsrumpf abzuleiten. Der function-body besteht aus einem Ausdruck. Der Ausdruck, der den Funktionsrumpf bildet, ist in der Regel ein zusammengesetzter Ausdruck, der aus einer Reihe von Ausdrücken besteht, die mit einem abschließenden Ausdruck enden, bei dem es sich um den Rückgabewert handelt. Der return-type ist ein Doppelpunkt, gefolgt von einem Typ, und er ist optional. Wenn Sie den Typ des Rückgabewerts nicht explizit angeben, bestimmt der Compiler den Rückgabetyp anhand des abschließenden Ausdrucks.

Eine einfache Funktionsdefinition lautet etwa folgendermaßen:

let f x = x + 1

Im vorherigen Beispiel ist f der Funktionsname, x ist das Argument mit dem Typ int, x + 1 ist der Funktionsrumpf, und der Rückgabewert ist vom Typ int.

Der Inlinespezifizierer informiert den Compiler, dass es sich um eine kleine Funktion handelt und dass der Code für die Funktion in den Rumpf des Aufrufers integriert werden kann.

Die Wiederverwendung des Namens für einen Wert oder eine Funktion ist auf jeder Gültigkeitsbereichsebene außer dem Gültigkeitsbereich des Moduls zulässig. Wenn Sie einen Namen wiederverwenden, führt der später deklarierte Name Shadowing für den früher deklarierten Namen aus. Auf der obersten Gültigkeitsbereichsebene in einem Modul müssen Namen jedoch eindeutig sein. Beispielsweise generiert der folgende Code einen Fehler, wenn er im Modulgültigkeitsbereich vorhanden ist, jedoch nicht, wenn er sich in einer Funktion befindet:


let list1 = [ 1; 2; 3]
// Error: duplicate definition.
let list1 = []  
let function1 =
   let list1 = [1; 2; 3]
   let list1 = []
   list1


Jedoch ist der folgende Code auf jeder Gültigkeitsbereichsebene 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


Namen von Parametern werden nach dem Funktionsnamen aufgeführt. 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 auf den Namen des Parameters, und er wird durch einen Doppelpunkt vom Namen getrennt. Wenn Sie den Typ für den Parameter weglassen, wird der Parametertyp vom Compiler abgeleitet. Beispielsweise wird in der folgenden Funktionsdefinition das Argument x als Argument vom Typ int abgeleitet, weil 1 vom Typ int ist.

let f x = x + 1

Der Compiler versucht jedoch, die Funktion so generisch wie möglich festzulegen. Beachten Sie z. B. folgenden Code:

let f x = (x, x)

Die Funktion erstellt ein Tupel aus einem Argument beliebigen Typs. Da der Typ nicht angegeben ist, kann die Funktion mit jedem Argumenttyp verwendet werden. Weitere Informationen finden Sie unter Automatische Verallgemeinerung (F#).

Ein Funktionsrumpf kann Definitionen von lokalen Variablen und Funktionen enthalten. Der Gültigkeitsbereich dieser Variablen und Funktionen ist auf den Rumpf der aktuellen Funktion begrenzt. Wenn Sie die Option für die einfache Syntax aktiviert haben, müssen Sie mithilfe des Einzugs angeben, dass sich eine Definition in einem Funktionsrumpf 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 Richtlinien für das Formatieren von Code (F#) und Ausführliche Syntax (F#).

Der Compiler bestimmt den Rückgabewert und -typ mithilfe des abschließenden Ausdrucks in einem Funktionsrumpf. Der Compiler kann den Typ des abschließenden Ausdrucks von vorherigen Ausdrücken ableiten. In der im vorherigen Abschnitt gezeigten Funktion cylinderVolume wird der Typ von pi anhand des Typs des Literals 3.14159 als float bestimmt. Der Compiler bestimmt den Typ des Ausdrucks h * pi * r * r anhand des Typs von pi als float. Daher ist der allgemeine Rückgabetyp der Funktion float.

Wenn Sie den Rückgabewert explizit angeben möchten, schreiben Sie folgenden Code:



let cylinderVolume radius length : float =
   // Define a local value pi.
   let pi = 3.14159
   length * pi * radius * radius


Wenn obiger Code geschrieben wurde, wendet der Compiler float auf die gesamte Funktion an. Wenn der Typ auch auf die Parametertypen angewendet werden soll, verwenden Sie folgenden Code:

let cylinderVolume (radius : float) (length : float) : float

Sie rufen Funktionen auf, indem Sie den Funktionsnamen, danach ein Leerzeichen und anschließend ggf. durch Leerzeichen getrennte Argumente angeben. Beispielsweise schreiben Sie folgenden Code, um die Funktion cylinderVolume aufzurufen und dem Wert vol das Ergebnis zuzuweisen:

let vol = cylinderVolume 2.0 3.0

Wenn Sie eine geringere Anzahl von Argumenten als angegeben bereitstellen, erstellen Sie eine neue Funktion, die die restlichen Argumente erwartet. Dieses Verfahren zum Behandeln von Argumenten ist eine Besonderheit funktionaler Programmiersprachen wie F#, die als Currying bezeichnet wird. Angenommen, Sie arbeiten mit zwei Rohrgrößen: ein Radius von 2.0 und ein Radius von 3.0. Sie können Funktionen erstellen, die das Rohrvolumen 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 stellen Sie je nach Bedarf das zusätzliche Argument für verschiedene Rohrlängen der beiden Rohrgrößen bereit:


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 sind Funktionen, die sich selbst aufrufen. Sie erfordern die Angabe des rec-Schlüsselworts nach dem let-Schlüsselwort. Rufen Sie die rekursive Funktion im Rumpf der Funktion so auf, wie Sie jede andere Funktion aufrufen. Die folgende rekursive Funktion berechnet die n-te Fibonacci-Zahl. Die Fibonacci-Zahlenfolge ist seit dem Altertum bekannt. Jede Zahl in dieser Folge ist die Summe der vorherigen zwei Zahlen in der Folge.


let rec fib n = if n < 2 then 1 else fib (n - 1) + fib (n - 2)


Einige rekursive Funktionen können einen Überlauf des Programmstapels verursachen oder ineffizient ausgeführt werden, wenn Sie sie ohne die erforderliche Sorgfalt und ohne Kenntnis spezieller Verfahren, z. B. die Verwendung von Akkumulatoren und Fortsetzungen, schreiben.

In F# gelten alle Funktionen als Werte. Funktionen werden sogar als Funktionswerte bezeichnet. Da Funktionen Werte sind, können sie als Argumente für andere Funktionen oder in anderen Kontexten, in denen Werte verwendet werden, eingesetzt werden. Es folgt ein Beispiel für eine Funktion, die einen Funktionswert als Argument akzeptiert:


let apply1 (transform : int -> int ) y = transform y


Sie geben den Typ eines Funktionswerts mit dem ->-Token an. Auf der linken Seite dieses Tokens befindet sich der Typ des Arguments und auf der rechten Seite der Rückgabewert. Im vorherigen Beispiel ist apply1 eine Funktion, die die Funktion transform als Argument akzeptiert, wobei transform eine Funktion ist, die eine ganze Zahl akzeptiert und eine andere ganze Zahl zurückgibt. Im folgenden Code wird die Verwendung von apply1 veranschaulicht:


let increment x = x + 1

let result1 = apply1 increment 100


Nach der Ausführung des vorangehenden Codes ist der Wert von result 101.

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.

Ein Lambda-Ausdruck ist eine unbenannte Funktion. In den vorherigen Beispielen können Sie Lambda-Ausdrücke wie folgt verwenden, statt die benannten Funktionen increment und mul zu definieren:


let result3 = apply1 (fun x -> x + 1) 100

let result4 = apply2 (fun x y -> x * y ) 10 20


Sie definieren Lambda-Ausdrücke mit dem fun-Schlüsselwort. Ein Lambda-Ausdruck ähnelt einer Funktionsdefinition, mit dem Unterschied, dass statt des =-Tokens das ->-Token verwendet wird, um die Argumentliste vom Funktionsrumpf zu trennen. Wie in einer regulären Funktionsdefinition können die Argumenttypen abgeleitet oder explizit angegeben werden, und der Rückgabetyp des Lambda-Ausdrucks wird vom Typ des letzten Ausdrucks im Rumpf abgeleitet. Weitere Informationen finden Sie unter Lambda-Ausdrücke: Das fun-Schlüsselwort (F#).

Funktionen in F# können aus anderen Funktionen zusammengesetzt sein. Die Zusammensetzung der beiden Funktionen function1 und function2 ist eine weitere Funktion, die die Anwendung von function1 und die anschließende Anwendung von function2 darstellt:


let function1 x = x + 1
let function2 x = x * 2
let h = function1 >> function2
let result5 = h 100


Das Ergebnis ist 202.

Pipelinefunktionen ermöglichen die Verkettung von Funktionsaufrufen als aufeinander folgende Vorgänge. Pipelinefunktionen werden wie folgt ausgeführt:

let result = 100 |> function1 |> function2

Das Ergebnis ist erneut 202.

Die Kompositionsoperatoren nehmen zwei Funktionen und geben eine Funktion zurück; hingegen von nehmen die Pipelineoperatoren eine Funktion und ein Argument und geben einen Wert zurück. Im folgenden Codebeispiel wird der Unterschied zwischen der Pipeline und den Kompositionsoperatoren an, indem es die Unterschiede in den Funktionssignaturen und beim anzeigt.

// 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
// ( <| ) : ('T -> 'U) -> 'T -> 'U
let Pipeline1 x = addOne <| timesTwo x

// Backward pipeline operator
// ( |> ) : 'T1 -> ('T1 -> 'U) -> 'U
let Pipeline2 x = addOne x |> timesTwo

// Result is 5
let result3 = Pipeline1 2

// Result is 6
let result4 = Pipeline2 2


Sie können Methoden eines Typs jedoch nicht Funktionen überladen. Weitere Informationen finden Sie unter Methoden (F#).

Fanden Sie dies hilfreich?
(1500 verbleibende Zeichen)
Vielen Dank für Ihr Feedback.

Community-Beiträge

Anzeigen:
© 2014 Microsoft. Alle Rechte vorbehalten.