Funkcje jako wartości pierwszej klasy (F#)
Cechą definiującą programowanie funkcyjne jest podniesienie funkcji do stanu pierwszej klasy.Powinna istnieć możliwość zrobienia z funkcją wszystkiego tego, co można zrobić z wartościami innych typów wbudowanych, oraz zrobienia tego przy porównywalnym stopniu nakładu pracy.
Typowe miary stanu pierwszej klasy obejmują:
Czy można powiązać identyfikator z wartością?Czy oznacza to, że można nadać mu nazwę?
Czy można przechowywać wartość w strukturze danych, takiej jak lista?
Czy można przekazać wartość jako argument w wywołaniu funkcji?
Czy można zwrócić wartość jako wartość wywołania funkcji?
Ostatnie dwie miary definiują tak zwane operacje wyższego rzędu lub funkcje wyższego rzędu.Funkcje wyższego rzędu akceptują funkcje jako argumenty i zwracają funkcje jako wartości wywołań funkcji.Operacje te obsługują takie filary programowania funkcyjnego jak mapowanie i składanie funkcji.
Nadanie nazwy wartości
Jeżeli funkcja jest wartością pierwszej klasy, musi istnieć możliwość nadania jej nazwy, tak samo jak można nazywać liczby całkowite, ciągi oraz inne typy wbudowane.W literaturze dotyczącej programowania funkcyjnego odnosi się to do wiązania identyfikatora z wartością.Język F# używa wyrażeń let, aby powiązać nazwy z wartościami: let <identifier> = <value>.Poniższy kod pokazuje dwa przykłady.
// Integer and string.
let num = 10
let str = "F#"
Można nazwać funkcję równie łatwo.Poniższy przykład definiuje funkcję o nazwie squareIt, dokonując wiązania identyfikatora squareIt z wyrażeniem lambdafun n -> n * nFunkcja squareIt ma jeden parametr n i zwraca kwadrat tego parametru.
let squareIt = fun n -> n * n
Język F# dostarcza następującą zwięzłą składnię, pozwalającą uzyskać ten sam wynik wpisując mniej.
let squareIt2 n = n * n
Następujące przykłady używają głównie pierwszego stylu let <function-name> = <lambda-expression> do wyróżnienia podobieństw pomiędzy deklaracjami funkcji, a deklaracjami innych typów wartości.Jednakże wszystkie nazwane funkcje mogą być również zapisane przy użyciu zwięzłej składni.Niektóre przykłady są zapisane na dwa sposoby.
Przechowywanie wartości w strukturze danych
Wartość pierwszej klasy może być przechowywana w strukturze danych.Poniższy kod pokazuje przykłady, które przechowują wartości na listach i w krotkach.
// 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 )
Aby zweryfikować, że nazwa funkcji przechowywana w spójnej kolekcji jest w rzeczywistości szacowana jako funkcja, w poniższym przykładzie użyto operatorów fst i snd do wyodrębnienia pierwszego i drugiego elementu ze spójnej kolekcji funAndArgTuple.Pierwszy element w krotce to squareIt, a drugi element to num.Identyfikator num jest w poprzednim przykładzie powiązany z liczbą całkowitą 10, czyli prawidłowym argumentem dla funkcji squareIt.Drugie wyrażenie stosuje pierwszy element ze spójnej kolekcji do drugiego elementu ze spójnej kolekcji: 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))
Identyfikator squareIt i wyrażenie lambda fun n -> n * n mogą być stosowane zamiennie, podobnie, jak identyfikator num i liczba całkowita 10.
// 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))
Przekazywanie wartości jako argument
Jeśli wartość ma w języku stan pierwszej klasy, można ją przekazać do funkcji jako argument.Na przykład często przekazuje się liczby całkowite i ciągi jako argumenty.Poniższy kod pokazuje liczby całkowite i ciągi przekazywane jako argumenty w języku F#.
// 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)
Jeśli funkcje mają stan pierwszej klasy, musi istnieć możliwość przekazania ich jako argumenty w taki sam sposób.Należy pamiętać, że jest to pierwsza cecha funkcji wyższego rzędu.
W poniższym przykładzie funkcja applyIt ma dwa parametry, op i arg.Jeśli wysyłana jest funkcja, która ma jeden parametr dla op i odpowiedni argument funkcji arg, funkcja zwraca wynik zastosowania op do arg.W poniższym przykładzie zarówno argument funkcji, jak i argument liczby całkowitej są wysyłane w taki sam sposób, przy użyciu ich nazw.
// 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)
Możliwość wysłania funkcji jako argumentu do innej funkcji leży u podstaw wspólnych abstrakcji w językach programowania funkcyjnego, takich jak operacje mapowania lub filtrowania.Operacja mapowania jest na przykład funkcją wyższego rzędu, która przechwytuje obliczenia współdzielone przez funkcje, które przechodzą przez listę i wykonują działania na każdym elemencie, a następnie zwraca listę wyników.Można zwiększyć każdy element na liście liczb całkowitych lub podnieść do kwadratu każdy element lub zmienić każdy element na liście ciągów na wielkie litery.Podatna na błędy część obliczeń to proces cykliczny, który przechodzi krok po kroku przez listę i tworzy listę wyników do zwrócenia.Część ta jest przechwytywana w funkcji mapowania.Wszystko, co trzeba zapisać dla określonej aplikacji to funkcja, która ma być zastosowana do każdego elementu listy osobno (dodawanie, podnoszenie do kwadratu, zmiana wielkości liter).Funkcja ta jest wysyłana jako argument do funkcji mapowania, tak jak squareIt była wysłana do applyIt w poprzednim przykładzie.
Język F# dostarcza metody mapowania dla większości typów kolekcji, łącznie z listami, tablicami i zbiorami.W następujących przykładach użyto list.Składnia to 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
Aby uzyskać więcej informacji, zobacz Listy (F#).
Zwracanie wartości z wywołania funkcji
W końcu, jeśli funkcja ma w języku stan pierwszej klasy, musi istnieć możliwość zwrócenia wartości wywołania funkcji, tak jak zwracane są inne typy, takie jak liczby całkowite i ciągi.
Następujące wywołania funkcji zwracają i wyświetlają liczby całkowite.
// Function doubleIt is defined in a previous example.
//let doubleIt = fun n -> 2 * n
System.Console.WriteLine(doubleIt 3)
System.Console.WriteLine(squareIt 4)
Następujące wywołanie funkcji zwraca ciąg.
// str is defined in a previous section.
//let str = "F#"
let lowercase = str.ToLower()
Następujące wywołanie funkcji zadeklarowane w tekście, zwraca wartość logiczną.Wyświetlana jest wartość True.
System.Console.WriteLine((fun n -> n % 2 = 1) 15)
Możliwość zwrotu funkcji jako wartości wywołania funkcji jest drugą cechą funkcji wyższego rzędu.W poniższym przykładzie checkFor jest zdefiniowane jako funkcja, która pobiera jeden argument item i zwraca nową funkcję jako jej wartość.Zwrócona funkcja przyjmuje listę jako argument lst i wyszukuje item w lst.Jeśli item jest obecny, funkcja zwraca true.Jeśli item nie jest obecny, funkcja zwraca false.Jak w poprzedniej sekcji, poniższy kod używa dostarczonej funkcji listy List.exists, aby przeszukać listę.
let checkFor item =
let functionToReturn = fun lst ->
List.exists (fun a -> a = item) lst
functionToReturn
Poniższy kod wykorzystuje checkFor , aby utworzyć nową funkcję, która pobiera jeden argument (listę) i wyszukuje na liście liczby 7.
// 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)
W poniższym przykładzie użyto stanu pierwszej klasy funkcji języka F# do zadeklarowania funkcji compose zwracającej złożenie dwóch argumentów funkcji.
// 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
[!UWAGA]
Aby uzyskać jeszcze krótszą wersję, zobacz następującą sekcję "Funkcje rozwinięte."
Poniższy kod wysyła dwie funkcje jako argumenty do compose. Obie funkcje przyjmują po jednym argumencie tego samego typu.Wartość zwracana jest nową funkcja, która jest złożeniem dwóch argumentów funkcji.
// 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)
[!UWAGA]
Język F# dostarcza dwa operatory << i >> do złożenia funkcji.Na przykład let squareAndDouble2 = doubleIt << squareIt jest równoważne z let squareAndDouble = compose doubleIt squareIt w poprzednim przykładzie.
W poniższym przykładzie zwracania funkcji jako wartości wywołania funkcji, tworzona jest prosta gra zgadywanka.Aby utworzyć grę, wywołaj makeGame z wartością, która ma być odgadnięta, wysyłaną do target.Wartość zwrócona przez funkcję makeGame jest funkcją, która przyjmuje jeden argument (zgadywaną wartość) i sprawdza, czy wynik jest poprawny.
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
Poniższy kod wywołuje makeGame wysyłając wartość 7 do target.Identyfikator playGame jest wiązany ze zwracanym wyrażeniem lambda.Dlatego playGame jest funkcją przyjmującą jako jeden argument wartość dla guess.
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!
Funkcje rozwinięte
Wiele przykładów z poprzedniej sekcji może być zapisanych bardziej zwięźle, wykorzystując niejawne rozwijanie w deklaracjach funkcji języka F#.Rozwijanie jest procesem przekształcającym funkcję posiadającą więcej niż jeden parametr do serii osadzonych funkcji, z których każda ma jeden parametr.W języku F#, funkcje posiadające więcej niż jeden parametr są z założenia rozwijane.Na przykład compose z poprzedniej sekcji można zapisać jak pokazano w następującym zwięzłym stylu, z trzema parametrami.
let compose4 op1 op2 n = op1 (op2 n)
Jednakże wynikiem jest funkcja z jednym parametrem, która zwraca funkcję z jednym parametrem, która z kolei zwraca inną funkcję z jednym parametrem, jak pokazano w compose4curried.
let compose4curried =
fun op1 ->
fun op2 ->
fun n -> op1 (op2 n)
Można uzyskać dostęp do tej funkcji na kilka sposobów.Każdy z poniższych przykładów zwraca i wyświetla 18.Można zamienić compose4 na compose4curried w dowolnym z przykładów.
// 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)
Aby zweryfikować, że funkcja nadal działa jak poprzednio, spróbuj wykorzystać ponownie pierwotne przypadki testowe.
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)
[!UWAGA]
Można ograniczyć rozwijanie, umieszczając parametry w krotkach.Aby uzyskać więcej informacji, zobacz "Wzory parametrów" w Parametry i argumenty (F#).
W poniższym przykładzie użyto niejawnego rozwijania w celu zapisania krótszej wersji makeGame.Szczegóły na temat tego, jak makeGame konstruuje i zwraca funkcję game są mniej jawne w tym formacie, lecz można zweryfikować przy użyciu pierwotnych przypadków testowych, że wynik jest taki sam.
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'
Aby uzyskać więcej informacji dotyczących rozwijania, zobacz "Częściowa aplikacja argumentów" w Funkcje (F#).
Identyfikatory i definicje funkcji są zamienne
Nazwa zmiennej num w poprzednich przykładach jest oceniana jako liczba całkowita 10 i nie jest zaskoczeniem, że tam gdzie poprawne jest num, liczba 10 jest również poprawna.To samo dotyczy identyfikatorów funkcji i ich wartości: gdziekolwiek można użyć nazwy funkcji, może tam być użyte wyrażenie lambda, z którym jest powiązana.
W poniższym przykładzie zdefiniowano funkcję typu Boolean o nazwie isNegative, a następnie nazwa funkcji i jej definicja są używane zamiennie.Kolejne trzy przykłady zwracają i wyświetlają False.
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)
Aby pójść krok dalej, należy zastąpić wartość, z którą jest powiązana applyIt przez applyIt.
System.Console.WriteLine((fun op arg -> op arg) (fun n -> n < 0) 10)
Funkcje są wartościami pierwszej klasy w języku F#
Przykłady w poprzednich sekcjach przedstawiają, że funkcje w języku F# spełniają kryteria bycia wartościami pierwszej klasy w języku F#:
Można powiązać identyfikator z definicją funkcji.
let squareIt = fun n -> n * n
Można przechowywać funkcję w strukturze danych.
let funTuple2 = ( BMICalculator, fun n -> n * n )
Można przekazać funkcję jako argument.
let increments = List.map (fun n -> n + 1) [ 1; 2; 3; 4; 5; 6; 7 ]
Można zwrócić funkcję jako wartość wywołania funkcji.
let checkFor item = let functionToReturn = fun lst -> List.exists (fun a -> a = item) lst functionToReturn
Aby uzyskać więcej informacji dotyczących języka F#, zobacz Materiały referencyjne dotyczące języka F#.
Przykład
Opis
Poniższy kod zawiera wszystkie przykłady z tego tematu.
Kod
// ** 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
Zobacz też
Informacje
Wyrażenia lambda: fun — Słowo kluczowe (F#)