Funktionen als erstrangige Werte (F#)

Eine wichtige Eigenschaft funktionaler Programmiersprachen ist die Behandlung von Funktionen als erstrangige Werte. Sie sollten mit einer Funktion ohne Mehraufwand alle Vorgänge ausführen können, die Sie auch mit den Werten der anderen integrierten Typen ausführen können.

Zur Feststellung der Erstrangigkeit gehören folgende Kriterien:

  • Kann ein Bezeichner an den Wert gebunden werden? In anderen Worten: Können Sie den Wert einen Namen zuweisen?

  • Kann der Wert in einer Datenstruktur gespeichert werden, z. B. in einer Liste?

  • Kann der Wert in einem Funktionsaufruf als Argument übergeben werden?

  • Kann der Wert als Wert eines Funktionsaufrufs zurückgegeben werden?

Mit den letzten zwei Kriterien werden sogenannte Operationen höherer Ordnung bzw. Funktionen höherer Ordnung definiert. Funktionen höherer Ordnung unterstützen Funktionen als Argumente und geben Funktionen als Werte von Funktionsaufrufen zurück. Diese Operationen bilden die Grundlage für die Hauptstützen der funktionalen Programmierung, wie z. B. Zuordnungsfunktionen und Funktionszusammensetzung.

Zuweisen eines Namens zu einem Wert

Einem erstrangigen Funktionswert muss wie ganzen Zahlen, Zeichenfolgen und anderen integrierten Typen ein Name zugewiesen werden können. Bei der funktionalen Programmierung wird dies als Bindung eines Bezeichners an einen Wert ausgedrückt. In F# werden let-Ausdrücke zur Bindung von Namen an Werte verwendet: let <identifier> = <value>. Der folgende Code enthält zwei Beispiele.

// Integer and string.
let num = 10
let str = "F#"

Das Zuweisen eines Namens zu einer Funktion ist ebenso einfach. Im folgenden Beispiel wird eine Funktion mit dem Namen squareIt definiert, indem der Bezeichner squareIt an den lambda-Ausdruck fun n -> n * n gebunden wird. Die Funktion squareIt verfügt über einen Parameter n und gibt das Quadrat dieses Parameters zurück.

let squareIt = fun n -> n * n

Durch die folgende kürzere Syntax in F# erzielen Sie das gleiche Ergebnis mit weniger Aufwand.

let squareIt2 n = n * n

In den folgenden Beispielen wird meist das erste Format (let <function-name> = <lambda-expression>) verwendet, um die Ähnlichkeiten zwischen der Deklaration von Funktionen und der Deklaration anderer Werttypen hervorzuheben. Sie können jedoch alle benannten Funktionen mit der kürzeren Syntax schreiben. In einigen Beispielen werden beide Formate verwendet.

Speichern des Werts in einer Datenstruktur

Sie können erstrangige Werte in einer Datenstruktur speichern. Der folgende Code enthält Beispiele, in denen Werte in Listen und Tupeln gespeichert werden.

// Lists.

// Storing integers and strings.
let integerList = [ 1; 2; 3; 4; 5; 6; 7 ]
let stringList = [ "one"; "two"; "three" ]

// You cannot mix types in a list. The following declaration causes a 
// type-mismatch compiler error.
//let failedList = [ 5; "six" ]

// In F#, functions can be stored in a list, as long as the functions 
// have the same signature.

// Function doubleIt has the same signature as squareIt, declared previously.
//let squareIt = fun n -> n * n
let doubleIt = fun n -> 2 * n

// Functions squareIt and doubleIt can be stored together in a list.
let funList = [ squareIt; doubleIt ]

// Function squareIt cannot be stored in a list together with a function
// that has a different signature, such as the following body mass 
// index (BMI) calculator.
let BMICalculator = fun ht wt -> 
                    (float wt / float (squareIt ht)) * 703.0

// The following expression causes a type-mismatch compiler error.
//let failedFunList = [ squareIt; BMICalculator ]


// Tuples.

// Integers and strings.
let integerTuple = ( 1, -7 )
let stringTuple = ( "one", "two", "three" )

// A tuple does not require its elements to be of the same type.
let mixedTuple = ( 1, "two", 3.3 )

// Similarly, function elements in tuples can have different signatures.
let funTuple = ( squareIt, BMICalculator )

// Functions can be mixed with integers, strings, and other types in
// a tuple. Identifier num was declared previously.
//let num = 10
let moreMixedTuple = ( num, "two", 3.3, squareIt )

Um zu überprüfen, ob ein in einem Tupel gespeicherter Funktionsname tatsächlich eine Funktion ergibt, werden im folgenden Beispiel mit dem fst-Operator und dem snd-Operator das erste und das zweite Element des Tupels funAndArgTuple extrahiert. Das erste Element im Tupel ist squareIt, das zweite Element ist num. Der Bezeichner num wurde in einem vorangehenden Beispiel an die ganze Zahl 10 gebunden, ein gültiges Argument für die squareIt-Funktion. Der zweite Ausdruck wendet das erste Element im Tupel auf das zweite Element im Tupel an: squareIt num.

// You can pull a function out of a tuple and apply it. Both squareIt and num
// were defined previously.
let funAndArgTuple = (squareIt, num)

// The following expression applies squareIt to num, returns 100, and 
// then displays 100.
System.Console.WriteLine((fst funAndArgTuple)(snd funAndArgTuple))

Ebenso wie der Bezeichner num und die ganze Zahl 10 sind auch der Bezeichner squareIt und der lambda-Ausdruck fun n -> n * n austauschbar.

// Make a list of values instead of identifiers.
let funAndArgTuple2 = ((fun n -> n * n), 10)

// The following expression applies a squaring function to 10, returns
// 100, and then displays 100.
System.Console.WriteLine((fst funAndArgTuple2)(snd funAndArgTuple2))

Übergeben des Werts als Argument

Werte mit erstrangigem Status in einer Sprache können als Argumente an eine Funktion übergeben werden. So werden z. B. ganze Zahlen und Zeichenfolgen häufig als Argumente übergeben. Im folgenden Code werden ganze Zahlen und Zeichenfolgen in F# als Argumente übergeben.

// An integer is passed to squareIt. Both squareIt and num are defined in 
// previous examples.
//let num = 10
//let squareIt = fun n -> n * n
System.Console.WriteLine(squareIt num)

// String.
// Function repeatString concatenates a string with itself.
let repeatString = fun s -> s + s

// A string is passed to repeatString. HelloHello is returned and displayed.
let greeting = "Hello"
System.Console.WriteLine(repeatString greeting)

Funktionen mit erstrangigem Status müssen sich ebenso als Argumente übergeben lassen. Hierbei handelt es sich um die erste Eigenschaft von Funktionen höherer Ordnung.

Die Funktion applyIt im folgenden Beispiel verfügt über die beiden Parameter op und arg. Wenn Sie eine Funktion mit einem Parameter für op und ein entsprechendes Funktionsargument für arg senden, gibt die Funktion ein Ergebnis zurück, das sich aus der Anwendung von op auf arg ergibt. Im folgenden Beispiel wird sowohl zum Senden des Funktionsarguments als auch zum Senden des ganzzahligen Arguments der jeweilige Name verwendet.

// Define the function, again using lambda expression syntax.
let applyIt = fun op arg -> op arg

// Send squareIt for the function, op, and num for the argument you want to 
// apply squareIt to, arg. Both squareIt and num are defined in previous 
// examples. The result returned and displayed is 100.
System.Console.WriteLine(applyIt squareIt num)

// The following expression shows the concise syntax for the previous function
// definition.
let applyIt2 op arg = op arg
// The following line also displays 100.
System.Console.WriteLine(applyIt2 squareIt num)

Die Fähigkeit, eine Funktion als Argument an eine andere Funktion zu übergeben, unterliegt in funktionalen Programmiersprachen allgemeinen Abstraktionen, wie z. B. Zuordnungs- und Filteroperationen. Um eine Zuordnungsoperation handelt es sich z. B. bei einer Funktion höherer Ordnung, mit der die Berechnung von Funktionen erfasst wird, die eine Liste durchlaufen, eine bestimmte Aktion für jedes Element ausführen und dann eine Liste mit den Ergebnissen zurückgeben. Auf diese Weise können Sie z. B. die einzelnen Element in einer Liste mit ganzen Zahlen um einen bestimmten Wert erhöhen oder quadrieren oder die Elemente in einer Liste mit Zeichenfolgen in Großschreibung ändern. Der fehleranfällige Teil dieser Berechnung ist der rekursive Prozess, mit dem die Liste durchlaufen und eine Liste der zurückzugebenden Ergebnisse erstellt wird. Dieser Teil wird von der Zuordnungsfunktion ausgeführt. Sie müssen für eine bestimmte Anwendung lediglich die Funktion schreiben, die auf jedes einzelne Element in der Liste angewendet werden soll (Addieren, Quadrieren, in Großschreibung ändern). Diese Funktion wird als Argument an die Zuordnungsfunktion gesendet, wie im vorangehenden Beispiel, in dem squareIt an applyIt gesendet wird.

F# stellt Zuordnungsmethoden für die meisten Auflistungstypen bereit, einschließlich Listen, Arrays und Sets. In den folgenden Beispielen werden Listen verwendet. Die Syntax lautet List.map <the function> <the list>.

// List integerList was defined previously:
//let integerList = [ 1; 2; 3; 4; 5; 6; 7 ]

// You can send the function argument by name, if an appropriate function
// is available. The following expression uses squareIt.
let squareAll = List.map squareIt integerList

// The following line displays [1; 4; 9; 16; 25; 36; 49]
printfn "%A" squareAll

// Or you can define the action to apply to each list element inline.
// For example, no function that tests for even integers has been defined,
// so the following expression defines the appropriate function inline.
// The function returns true if n is even; otherwise it returns false.
let evenOrNot = List.map (fun n -> n % 2 = 0) integerList

// The following line displays [false; true; false; true; false; true; false]
printfn "%A" evenOrNot

Weitere Informationen finden Sie unter Listen (F#).

Zurückgeben des Werts aus einem Funktionsaufruf

Schließlich müssen sich Funktion mit erstrangigem Status in einer Sprache als Wert aus einem Funktionsaufruf zurückgeben lassen, ebenso wie andere Typen, z. B. ganze Zahlen oder Zeichenfolgen.

Mit den folgenden Funktionsaufrufen werden ganze Zahlen zurückgegeben und angezeigt.

// Function doubleIt is defined in a previous example.
//let doubleIt = fun n -> 2 * n
System.Console.WriteLine(doubleIt 3)
System.Console.WriteLine(squareIt 4)

Mit dem folgenden Funktionsaufruf wird eine Zeichenfolge zurückgegeben.

// str is defined in a previous section.
//let str = "F#"
let lowercase = str.ToLower()

Mit dem folgenden inline deklarierten Funktionsaufruf wird ein boolescher Wert zurückgegeben. Der angezeigte Wert ist True.

System.Console.WriteLine((fun n -> n % 2 = 1) 15)

Die Fähigkeit, eine Funktion als Wert eines Funktionsaufrufs zurückzugeben, ist die zweite Eigenschaft von Funktionen höherer Ordnung. Im folgenden Beispiel wird checkFor als Funktion definiert, die ein Argument erfordert (item) und eine neue Funktion als Wert zurückgibt. Die zurückgegebene Funktion erfordert eine Liste als Argument (lst) und führt in lst eine Suche nach item aus. Wenn item gefunden wird, gibt die Funktion true zurück. Wenn item nicht vorhanden ist, wird false zurückgegeben. Wie im vorherigen Abschnitt wird im folgenden Code eine bereitgestellte Listenfunktion (List.exists) zum Suchen verwendet.

let checkFor item = 
    let functionToReturn = fun lst ->
                           List.exists (fun a -> a = item) lst
    functionToReturn

Im folgenden Code wird mit checkFor eine neue Funktion erstellt, die ein Listenargument erfordert und in der Liste nach der Zahl 7 sucht.

// integerList and stringList were defined earlier.
//let integerList = [ 1; 2; 3; 4; 5; 6; 7 ]
//let stringList = [ "one"; "two"; "three" ]

// The returned function is given the name checkFor7. 
let checkFor7 = checkFor 7

// The result displayed when checkFor7 is applied to integerList is True.
System.Console.WriteLine(checkFor7 integerList)

// The following code repeats the process for "seven" in stringList.
let checkForSeven = checkFor "seven"

// The result displayed is False.
System.Console.WriteLine(checkForSeven stringList)

Im folgenden Beispiel wird der erstrangige Funktionsstatus in F# verwendet, um die Funktion compose zu deklarieren, die eine Zusammensetzung von zwei Funktionsargumenten zurückgibt.

// Function compose takes two arguments. Each argument is a function 
// that takes one argument of the same type. The following declaration
// uses lambda expresson syntax.
let compose = 
    fun op1 op2 ->
        fun n ->
            op1 (op2 n)

// To clarify what you are returning, use a nested let expression:
let compose2 = 
    fun op1 op2 ->
        // Use a let expression to build the function that will be returned.
        let funToReturn = fun n ->
                            op1 (op2 n)
        // Then just return it.
        funToReturn

// Or, integrating the more concise syntax:
let compose3 op1 op2 =
    let funToReturn = fun n ->
                        op1 (op2 n)
    funToReturn

Tipp

Eine noch kürzere Version finden Sie im folgenden Abschnitt, "Funktionen mit Currying".

Mit dem folgenden Code werden zwei Funktionen als Argumente an compose gesendet, die beide ein einzelnes Argument desselben Typs erfordern. Der Rückgabewert ist eine neue Funktion, bei der es sich um eine Zusammensetzung der beiden Funktionsargumente handelt.

// Functions squareIt and doubleIt were defined in a previous example.
let doubleAndSquare = compose squareIt doubleIt
// The following expression doubles 3, squares 6, and returns and
// displays 36.
System.Console.WriteLine(doubleAndSquare 3)

let squareAndDouble = compose doubleIt squareIt
// The following expression squares 3, doubles 9, returns 18, and
// then displays 18.
System.Console.WriteLine(squareAndDouble 3)

Tipp

F# bietet zwei Operatoren zum Zusammensetzen von Funktionen: << und >>. let squareAndDouble2 = doubleIt << squareIt entspricht im vorangehenden Beispiel let squareAndDouble = compose doubleIt squareIt.

Im folgenden Beispiel zur Rückgabe einer Funktion als Wert eines Funktionsaufrufs wird ein einfaches Ratespiel erstellt. Rufen Sie zum Erstellen eines Spiels makeGame auf, und senden Sie den Wert, der erraten werden soll, an target. Der Rückgabewert der Funktion makeGame ist eine Funktion, die ein Argument erfordert (den zu erratenden Wert) und zurückgibt, ob richtig geraten wurde.

let makeGame target = 
    // Build a lambda expression that is the function that plays the game.
    let game = fun guess -> 
                   if guess = target then
                      System.Console.WriteLine("You win!")
                   else 
                      System.Console.WriteLine("Wrong. Try again.")
    // Now just return it.
    game

Im folgenden Code wird makeGame aufgerufen und der Wert 7 an target gesendet. Der Bezeichner playGame wird an den zurückgegebenen Lambda-Ausdruck gebunden. Somit ist playGame eine Funktion, die den Wert für guess als einzelnes Argument erfordert.

let playGame = makeGame 7
// Send in some guesses.
playGame 2
playGame 9
playGame 7

// Output:
// Wrong. Try again.
// Wrong. Try again.
// You win!

// The following game specifies a character instead of an integer for target. 
let alphaGame = makeGame 'q'
alphaGame 'c'
alphaGame 'r'
alphaGame 'j'
alphaGame 'q'

// Output:
// Wrong. Try again.
// Wrong. Try again.
// Wrong. Try again.
// You win!

Funktionen mit Currying

Sie können viele der Beispiele im vorherigen Abschnitt noch kürzer fassen, indem Sie die als Currying bezeichnete Methode für Funktionsdeklarationen in F# nutzen. Beim sogenannten Currying wird eine Funktion mit mehreren Parametern in eine Reihe eingebetteter Funktionen mit jeweils einem einzelnen Parameter umgewandelt. In F# wird Currying grundsätzlich auf Funktionen mit mehreren Parametern angewendet. Beispiel: compose aus dem vorherigen Abschnitt kann in dem folgenden kurz gefassten Format mit drei Parametern geschrieben werden.

let compose4 op1 op2 n = op1 (op2 n)

Das Ergebnis ist eine Funktion mit einem Parameter, die eine Funktion mit einem Parameter zurückgibt, die wiederum eine Funktion mit einem Parameter zurückgibt (siehe compose4curried).

let compose4curried =
    fun op1 ->
        fun op2 ->
            fun n -> op1 (op2 n)

Für den Zugriff auf diese Funktion haben Sie verschiedene Möglichkeiten. In jedem der folgenden Beispiele wird 18 zurückgegeben und angezeigt. Sie können in jedem der Beispiele compose4 durch compose4curried ersetzen.

// Access one layer at a time.
System.Console.WriteLine(((compose4 doubleIt) squareIt) 3)

// Access as in the original compose examples, sending arguments for 
// op1 and op2, then applying the resulting function to a value.
System.Console.WriteLine((compose4 doubleIt squareIt) 3)

// Access by sending all three arguments at the same time.
System.Console.WriteLine(compose4 doubleIt squareIt 3)

Um zu überprüfen, ob die Funktion wie gehabt funktioniert, verwenden Sie die ursprünglichen Testsituationen erneut.

let doubleAndSquare4 = compose4 squareIt doubleIt
// The following expression returns and displays 36.
System.Console.WriteLine(doubleAndSquare4 3)

let squareAndDouble4 = compose4 doubleIt squareIt
// The following expression returns and displays 18.
System.Console.WriteLine(squareAndDouble4 3)

Tipp

Zur Einschränkung von Currying können Sie die Parameter in Tupeln einschließen. Weitere Informationen finden Sie unter "Parametermuster" in Parameter und Argumente (F#).

Im folgenden Beispiel wird mit implizitem Currying eine kürzere Version von makeGame geschrieben. Wie makeGame die game-Funktion erstellt und zurückgibt, ist in diesem Format weniger explizit, Sie können das Ergebnis aber mit den ursprünglichen Testsituationen überprüfen.

let makeGame2 target guess =
    if guess = target then
       System.Console.WriteLine("You win!")
    else 
       System.Console.WriteLine("Wrong. Try again.")

let playGame2 = makeGame2 7
playGame2 2
playGame2 9
playGame2 7

let alphaGame2 = makeGame2 'q'
alphaGame2 'c'
alphaGame2 'r'
alphaGame2 'j'
alphaGame2 'q'

Weitere Informationen zu Currying finden Sie im Abschnitt "Partielle Anwendung von Argumenten" unter Funktionen (F#).

Bezeichner und Funktionsdefinition sind austauschbar

Der Variablenname num in den vorherigen Beispielen ergibt die ganze Zahl 10, wenn also num gültig ist, hat 10 ebenso Gültigkeit. Dasselbe gilt für Funktionsbezeichner und ihre Werte: Wenn der Name der Funktion verwendet werden kann, kann auch der daran gebundene lambda-Ausdruck verwendet werden.

Im folgenden Beispiel wird eine Boolean-Funktion mit dem Namen isNegative definiert. Dann werden Funktionsname und Funktionsdefinition beliebig ausgetauscht. In den nächsten drei Beispielen wird immer False zurückgegeben und angezeigt.

let isNegative = fun n -> n < 0

// This example uses the names of the function argument and the integer
// argument. Identifier num is defined in a previous example.
//let num = 10
System.Console.WriteLine(applyIt isNegative num)

// This example substitutes the value that num is bound to for num, and the
// value that isNegative is bound to for isNegative.
System.Console.WriteLine(applyIt (fun n -> n < 0) 10) 

Gehen Sie noch einen Schritt weiter und ersetzen Sie applyIt durch den Wert, an den applyIt gebunden ist.

System.Console.WriteLine((fun op arg -> op arg) (fun n -> n < 0)  10)

Funktionen in F# sind erstrangige Werte

Die Beispiele in den vorherigen Abschnitten veranschaulichen, dass die Funktionen in F# die Kriterien für erstrangige Werte in F# erfüllen:

  • Sie können einen Bezeichner an eine Funktionsdefinition binden.

    let squareIt = fun n -> n * n
    
  • Sie können eine Funktion in einer Datenstruktur speichern.

    let funTuple2 = ( BMICalculator, fun n -> n * n )
    
  • Sie können eine Funktion als Argument übergeben.

    let increments = List.map (fun n -> n + 1) [ 1; 2; 3; 4; 5; 6; 7 ]
    
  • Sie können eine Funktion als Wert eines Funktionsaufrufs zurückgeben.

    let checkFor item = 
        let functionToReturn = fun lst ->
                               List.exists (fun a -> a = item) lst
        functionToReturn
    

Weitere Informationen zu F# finden Sie unter Neues in Visual F# 2010 und unter F#-Sprachreferenz.

Beispiel

Beschreibung

Der folgende Code enthält alle Beispiele aus diesem Thema.

Code


// ** GIVE THE VALUE A NAME **

// Integer and string.
let num = 10
let str = "F#"

let squareIt = fun n -> n * n

let squareIt2 n = n * n


// ** STORE THE VALUE IN A DATA STRUCTURE **

// Lists.

// Storing integers and strings.
let integerList = [ 1; 2; 3; 4; 5; 6; 7 ]
let stringList = [ "one"; "two"; "three" ]

// You cannot mix types in a list. The following declaration causes a 
// type-mismatch compiler error.
//let failedList = [ 5; "six" ]

// In F#, functions can be stored in a list, as long as the functions 
// have the same signature.

// Function doubleIt has the same signature as squareIt, declared previously.
//let squareIt = fun n -> n * n
let doubleIt = fun n -> 2 * n

// Functions squareIt and doubleIt can be stored together in a list.
let funList = [ squareIt; doubleIt ]

// Function squareIt cannot be stored in a list together with a function
// that has a different signature, such as the following body mass 
// index (BMI) calculator.
let BMICalculator = fun ht wt -> 
                    (float wt / float (squareIt ht)) * 703.0

// The following expression causes a type-mismatch compiler error.
//let failedFunList = [ squareIt; BMICalculator ]


// Tuples.

// Integers and strings.
let integerTuple = ( 1, -7 )
let stringTuple = ( "one", "two", "three" )

// A tuple does not require its elements to be of the same type.
let mixedTuple = ( 1, "two", 3.3 )

// Similarly, function elements in tuples can have different signatures.
let funTuple = ( squareIt, BMICalculator )

// Functions can be mixed with integers, strings, and other types in
// a tuple. Identifier num was declared previously.
//let num = 10
let moreMixedTuple = ( num, "two", 3.3, squareIt )

// You can pull a function out of a tuple and apply it. Both squareIt and num
// were defined previously.
let funAndArgTuple = (squareIt, num)

// The following expression applies squareIt to num, returns 100, and 
// then displays 100.
System.Console.WriteLine((fst funAndArgTuple)(snd funAndArgTuple))

// Make a list of values instead of identifiers.
let funAndArgTuple2 = ((fun n -> n * n), 10)

// The following expression applies a squaring function to 10, returns
// 100, and then displays 100.
System.Console.WriteLine((fst funAndArgTuple2)(snd funAndArgTuple2))


// ** PASS THE VALUE AS AN ARGUMENT **

// An integer is passed to squareIt. Both squareIt and num are defined in 
// previous examples.
//let num = 10
//let squareIt = fun n -> n * n
System.Console.WriteLine(squareIt num)

// String.
// Function repeatString concatenates a string with itself.
let repeatString = fun s -> s + s

// A string is passed to repeatString. HelloHello is returned and displayed.
let greeting = "Hello"
System.Console.WriteLine(repeatString greeting)

// Define the function, again using lambda expression syntax.
let applyIt = fun op arg -> op arg

// Send squareIt for the function, op, and num for the argument you want to 
// apply squareIt to, arg. Both squareIt and num are defined in previous 
// examples. The result returned and displayed is 100.
System.Console.WriteLine(applyIt squareIt num)

// The following expression shows the concise syntax for the previous function
// definition.
let applyIt2 op arg = op arg
// The following line also displays 100.
System.Console.WriteLine(applyIt2 squareIt num)

// List integerList was defined previously:
//let integerList = [ 1; 2; 3; 4; 5; 6; 7 ]

// You can send the function argument by name, if an appropriate function
// is available. The following expression uses squareIt.
let squareAll = List.map squareIt integerList

// The following line displays [1; 4; 9; 16; 25; 36; 49]
printfn "%A" squareAll

// Or you can define the action to apply to each list element inline.
// For example, no function that tests for even integers has been defined,
// so the following expression defines the appropriate function inline.
// The function returns true if n is even; otherwise it returns false.
let evenOrNot = List.map (fun n -> n % 2 = 0) integerList

// The following line displays [false; true; false; true; false; true; false]
printfn "%A" evenOrNot


// ** RETURN THE VALUE FROM A FUNCTION CALL **

// Function doubleIt is defined in a previous example.
//let doubleIt = fun n -> 2 * n
System.Console.WriteLine(doubleIt 3)
System.Console.WriteLine(squareIt 4)

// The following function call returns a string:
// str is defined in a previous section.
//let str = "F#"
let lowercase = str.ToLower()

System.Console.WriteLine((fun n -> n % 2 = 1) 15)

let checkFor item = 
    let functionToReturn = fun lst ->
                           List.exists (fun a -> a = item) lst
    functionToReturn

// integerList and stringList were defined earlier.
//let integerList = [ 1; 2; 3; 4; 5; 6; 7 ]
//let stringList = [ "one"; "two"; "three" ]

// The returned function is given the name checkFor7. 
let checkFor7 = checkFor 7

// The result displayed when checkFor7 is applied to integerList is True.
System.Console.WriteLine(checkFor7 integerList)

// The following code repeats the process for "seven" in stringList.
let checkForSeven = checkFor "seven"

// The result displayed is False.
System.Console.WriteLine(checkForSeven stringList)

// Function compose takes two arguments. Each argument is a function 
// that takes one argument of the same type. The following declaration
// uses lambda expresson syntax.
let compose = 
    fun op1 op2 ->
        fun n ->
            op1 (op2 n)

// To clarify what you are returning, use a nested let expression:
let compose2 = 
    fun op1 op2 ->
        // Use a let expression to build the function that will be returned.
        let funToReturn = fun n ->
                            op1 (op2 n)
        // Then just return it.
        funToReturn

// Or, integrating the more concise syntax:
let compose3 op1 op2 =
    let funToReturn = fun n ->
                        op1 (op2 n)
    funToReturn

// Functions squareIt and doubleIt were defined in a previous example.
let doubleAndSquare = compose squareIt doubleIt
// The following expression doubles 3, squares 6, and returns and
// displays 36.
System.Console.WriteLine(doubleAndSquare 3)

let squareAndDouble = compose doubleIt squareIt
// The following expression squares 3, doubles 9, returns 18, and
// then displays 18.
System.Console.WriteLine(squareAndDouble 3)

let makeGame target = 
    // Build a lambda expression that is the function that plays the game.
    let game = fun guess -> 
                   if guess = target then
                      System.Console.WriteLine("You win!")
                   else 
                      System.Console.WriteLine("Wrong. Try again.")
    // Now just return it.
    game

let playGame = makeGame 7
// Send in some guesses.
playGame 2
playGame 9
playGame 7

// Output:
// Wrong. Try again.
// Wrong. Try again.
// You win!

// The following game specifies a character instead of an integer for target. 
let alphaGame = makeGame 'q'
alphaGame 'c'
alphaGame 'r'
alphaGame 'j'
alphaGame 'q'

// Output:
// Wrong. Try again.
// Wrong. Try again.
// Wrong. Try again.
// You win!


// ** CURRIED FUNCTIONS **

let compose4 op1 op2 n = op1 (op2 n)

let compose4curried =
    fun op1 ->
        fun op2 ->
            fun n -> op1 (op2 n)

// Access one layer at a time.
System.Console.WriteLine(((compose4 doubleIt) squareIt) 3)

// Access as in the original compose examples, sending arguments for 
// op1 and op2, then applying the resulting function to a value.
System.Console.WriteLine((compose4 doubleIt squareIt) 3)

// Access by sending all three arguments at the same time.
System.Console.WriteLine(compose4 doubleIt squareIt 3)

let doubleAndSquare4 = compose4 squareIt doubleIt
// The following expression returns and displays 36.
System.Console.WriteLine(doubleAndSquare4 3)

let squareAndDouble4 = compose4 doubleIt squareIt
// The following expression returns and displays 18.
System.Console.WriteLine(squareAndDouble4 3)

let makeGame2 target guess =
    if guess = target then
       System.Console.WriteLine("You win!")
    else 
       System.Console.WriteLine("Wrong. Try again.")

let playGame2 = makeGame2 7
playGame2 2
playGame2 9
playGame2 7

let alphaGame2 = makeGame2 'q'
alphaGame2 'c'
alphaGame2 'r'
alphaGame2 'j'
alphaGame2 'q'


// ** IDENTIFIER AND FUNCTION DEFINITION ARE INTERCHANGEABLE **

let isNegative = fun n -> n < 0

// This example uses the names of the function argument and the integer
// argument. Identifier num is defined in a previous example.
//let num = 10
System.Console.WriteLine(applyIt isNegative num)

// This example substitutes the value that num is bound to for num, and the
// value that isNegative is bound to for isNegative.
System.Console.WriteLine(applyIt (fun n -> n < 0) 10) 

System.Console.WriteLine((fun op arg -> op arg) (fun n -> n < 0)  10)


// ** FUNCTIONS ARE FIRST-CLASS VALUES IN F# **

//let squareIt = fun n -> n * n

let funTuple2 = ( BMICalculator, fun n -> n * n )

let increments = List.map (fun n -> n + 1) [ 1; 2; 3; 4; 5; 6; 7 ]

//let checkFor item = 
//    let functionToReturn = fun lst ->
//                           List.exists (fun a -> a = item) lst
//    functionToReturn

Siehe auch

Referenz

Tupel (F#)

Funktionen (F#)

let-Bindungen (F#)

Lambda-Ausdrücke: Das fun-Schlüsselwort (F#)

Weitere Ressourcen

Listen (F#)