Funzioni come valori di prima classe (F#)

Una caratteristica distintiva dei linguaggi di programmazione funzionale è l'elevazione delle funzioni allo stato di prima classe. Una funzione dovrebbe consentire di effettuare qualsiasi operazione eseguibile con i valori degli altri tipi incorporati con la stessa facilità.

Le misure tipiche dello stato di prima classe includono quanto segue:

  • È possibile associare un identificatore al valore? In altre parole, è possibile dargli un nome?

  • È possibile archiviare il valore in una struttura di dati, ad esempio un elenco?

  • È possibile passare il valore come argomento in una chiamata di funzione?

  • È possibile restituire il valore come valore di una chiamata di funzione?

Le ultime due misure definiscono quelle note come operazioni di ordine superiore o funzioni di ordine superiore. Le funzioni di ordine superiore accettano funzioni come argomenti e restituiscono funzioni come valori di chiamate di funzione. Queste operazioni supportano fondamenti della programmazione funzionale quali il mapping di funzioni e la composizione di funzioni.

Dare un nome al valore

Se una funzione è un valore di prima classe, deve essere possibile denominarla, proprio come si possono denominare interi, stringhe e altri tipi incorporati. Nella letteratura della programmazione funzionale, questa operazione è nota come associazione di un identificatore a un valore. F# utilizza espressioni let per associare nomi ai valori: let <identifier> = <value>. Nel codice che segue sono illustrati due esempi.

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

È possibile denominare una funzione altrettanto facilmente. Nell'esempio seguente viene definita una funzione denominata squareIt mediante associazione dell'identificatore squareIt all'espressione lambda fun n -> n * n. La funzione squareIt dispone di un parametro, n, e restituisce il quadrato di tale parametro.

let squareIt = fun n -> n * n

F# fornisce la seguente sintassi più concisa per ottenere lo stesso risultato con minore digitazione.

let squareIt2 n = n * n

Negli esempi che seguono viene per lo più utilizzato il primo stile, let <function-name> = <lambda-expression>, per enfatizzare le somiglianze tra la dichiarazione di funzioni e la dichiarazione di altri tipi di valori. Tuttavia, tutte le funzioni denominate possono essere scritte anche con la sintassi concisa. Alcuni degli esempi sono scritti in entrambi i modi.

Archiviare il valore in una struttura di dati

Un valore di prima classe può essere archiviato in una struttura di dati. Nel codice seguente vengono illustrati esempi di archiviazione di valori in elenchi e tuple.

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

Per verificare che un nome di funzione archiviato in una tupla restituisca di fatto una funzione, nell'esempio che segue vengono utilizzati gli operatori fst e snd per estrarre il primo e il secondo elemento dalla tupla funAndArgTuple. Il primo elemento nella tupla è squareIt e il secondo elemento è num. In un esempio precedente, l'identificatore num è associato all'intero 10, un argomento valido per la funzione squareIt. La seconda espressione applica il primo elemento nella tupla al secondo elemento nella tupla: 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))

Analogamente, come l'identificatore num e l'intero 10 possono essere utilizzati in modo intercambiabile, lo stesso vale per l'identificatore squareIt e l'espressione lambda fun n -> n * n.

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

Passare il valore come argomento

Se un valore dispone dello stato di prima classe in un linguaggio, è possibile passarlo come argomento a una funzione. Ad esempio, è pratica comune passare interi e stringhe come argomenti. Nel codice seguente vengono illustrati interi e stringhe passati come argomenti in 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)

Se le funzioni dispongono dello stato di prima classe, deve essere comunque possibile passarle come argomenti. Questa è la prima caratteristica delle funzioni di ordine superiore.

Nell'esempio riportato di seguito la funzione applyIt possiede due parametri, op e arg. Se si invia una funzione che dispone di un parametro per op e un argomento appropriato per la funzione a arg, la funzione restituisce il risultato dell'applicazione di op ad arg. Nell'esempio seguente, sia l'argomento della funzione che l'argomento integer vengono inviati nello stesso modo, tramite i rispettivi nomi.

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

La possibilità di inviare una funzione come argomento a un'altra funzione sta alla base di astrazioni comuni nei linguaggi di programmazione funzionale, quali le operazioni di mapping o di filtraggio. Un'operazione di mapping, ad esempio, è una funzione di ordine superiore che acquisisce il calcolo condiviso dalle funzioni che scorrono un elenco, eseguono un'azione su ogni elemento e restituiscono un elenco di risultati. È possibile incrementare ogni elemento in un elenco di interi, o elevare al quadrato ogni elemento, o ancora modificare in maiuscolo ogni elemento in un elenco di stringhe. La parte tendente all'errore del calcolo è il processo ricorsivo che scorre l'elenco e compila un elenco di risultati da restituire. Tale parte viene acquisita nella funzione di mapping. Tutto ciò che occorre scrivere per una particolare applicazione è la funzione che si desidera applicare a ogni singolo elemento dell'elenco (aggiunta, elevazione al quadrato, modifica della combinazione di maiuscole e minuscole). Tale funzione viene inviata come argomento alla funzione di mapping, proprio come squareIt viene inviato a applyIt nell'esempio precedente.

F# fornisce metodi di mapping per la maggior parte dei tipi di insieme, inclusi elenchi, matrici e set. Nell'esempio seguente vengono utilizzati gli elenchi. La sintassi è 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

Per ulteriori informazioni, vedere Elenchi (F#).

Restituire il valore da una chiamata di funzione

Infine, se una funzione dispone dello stato di prima classe in un linguaggio, deve essere possibile restituirla come valore di una chiamata di funzione, esattamente come si restituiscono altri tipi, ad esempio interi e stringhe.

Le chiamate di funzione seguenti restituiscono e visualizzano interi.

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

La chiamata di funzione seguente restituisce una stringa.

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

La chiamata di funzione seguente, dichiarata inline, restituisce un valore booleano. Il valore visualizzato è True.

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

La possibilità di restituire una funzione come valore di una chiamata di funzione è la seconda caratteristica delle funzioni di ordine superiore. Nell'esempio seguente, checkFor è definito come funzione che accetta un argomento, item, e ne restituisce come valore una nuova funzione. La funzione restituita accetta un elenco come argomento, lst, e cerca item in lst. Se item è presente, la funzione restituisce true. Se item non è presente, la funzione restituisce false. Come nella sezione precedente, nel codice che segue viene utilizzata una funzione dell'elenco specificata, List.exists, per eseguire ricerche nell'elenco.

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

Nel codice seguente viene utilizzato checkFor per creare una nuova funzione che accetta un unico argomento, un elenco, e cerca 7 nell'elenco.

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

Nell'esempio seguente viene utilizzato lo stato di prima classe delle funzioni in F# per dichiarare una funzione, compose, che restituisce una composizione di due argomenti della funzione.

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

Nota

Per una versione ancora più breve vedere la sezione seguente, "Funzioni sottoposte a currying".

Nel codice seguente, due funzioni vengono inviate come argomenti a compose; entrambe accettano un unico argomento dello stesso tipo. Il valore restituito è una nuova funzione equivalente a una composizione dei due argomenti della funzione.

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

Nota

F# fornisce due operatori, << e >>, che compongono funzioni. Ad esempio, let squareAndDouble2 = doubleIt << squareIt è equivalente a let squareAndDouble = compose doubleIt squareIt nell'esempio precedente.

Il seguente esempio di restituzione di una funzione come valore di una chiamata di funzione crea un semplice gioco di individuazione. Per creare un gioco, chiamare makeGame con il valore da individuare inviato per target. Il valore restituito dalla funzione makeGame è una funzione che accetta un unico argomento, la proposta, e segnala se questo è corretto o meno.

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

Nel codice seguente viene chiamata la funzione makeGame inviando il valore 7 per target. L'identificatore playGame è associato all'espressione lambda restituita. Pertanto, playGame è una funzione che accetta come unico argomento un valore per 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!

Funzioni sottoposte a currying

Molti degli esempi riportati nella sezione precedente possono essere scritti in maniera più concisa sfruttando il currying implicito nelle dichiarazioni di funzione in F#. Il currying è un processo che trasforma una funzione con più di un parametro in una serie di funzioni incorporate, ognuna delle quali dispone di un unico parametro. In F#, le funzioni con più di un parametro vengono intrinsecamente sottoposte a currying. Ad esempio, la funzione compose della sezione precedente può essere scritta con tre parametri, come illustrato nello stile conciso che segue.

let compose4 op1 op2 n = op1 (op2 n)

Tuttavia, il risultato è una funzione di un parametro che restituisce una funzione di un parametro che, a sua volta, restituisce un'altra funzione di un parametro, come illustrato in compose4curried.

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

È possibile accedere a questa funzione in diversi modi. In ognuno degli esempi che seguono viene restituito e visualizzato 18. In tutti gli esempi è possibile sostituire compose4 con compose4curried.

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

Per verificare che la funzione continui a funzionare come in precedenza, riprovare i test case originali.

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)

Nota

È possibile limitare il currying racchiudendo i parametri in tuple. Per ulteriori informazioni, vedere "Modelli di parametri" in Parametri e argomenti (F#).

Nell'esempio seguente viene utilizzato il currying implicito per scrivere una versione più breve di makeGame. I dettagli del modo in cui makeGame costruisce e restituisce la funzione game sono meno espliciti in questo formato, ma è possibile verificare che il risultato sia lo stesso utilizzando i test case originali.

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'

Per ulteriori informazioni sul currying, vedere "Applicazione parziale di argomenti" in Funzioni (F#).

Identificatore e definizione di funzione sono intercambiabili

Il nome di variabile num negli esempi precedenti restituisce l'intero 10. Appare scontato che, dove è valido num, sia valido anche 10. Lo stesso vale per gli identificatori di funzione e i rispettivi valori: ovunque sia possibile utilizzare il nome della funzione, si può utilizzare l'espressione lambda alla quale è associato il nome.

Nell'esempio seguente viene definita una funzione Boolean chiamata isNegative, quindi vengono utilizzati in modo intercambiabile il nome e la definizione della funzione. Nei tre esempi successivi viene sempre restituito e visualizzato 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) 

Per approfondire, sostituire applyIt con il valore al quale è associato applyIt.

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

Le funzioni sono valori di prima classe in F#

Gli esempi riportati nelle sezioni precedenti dimostrano che le funzioni in F# soddisfano i criteri necessari per essere considerate valori di prima classe:

  • È possibile associare un identificatore a una definizione di funzione.

    let squareIt = fun n -> n * n
    
  • È possibile archiviare una funzione in una struttura di dati.

    let funTuple2 = ( BMICalculator, fun n -> n * n )
    
  • È possibile passare una funzione come argomento.

    let increments = List.map (fun n -> n + 1) [ 1; 2; 3; 4; 5; 6; 7 ]
    
  • È possibile restituire una funzione come valore di una chiamata di funzione.

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

Per ulteriori informazioni su F#, vedere Novità di Visual F# 2010 o Riferimenti per il linguaggio F#.

Esempio

Descrizione

Nel codice seguente sono inclusi tutti gli esempi di questo argomento.

Codice


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

Vedere anche

Riferimenti

Tuple (F#)

Funzioni (F#)

Associazioni let (F#)

Espressioni lambda: parola chiave fun (F#)

Altre risorse

Elenchi (F#)