Criteri di ricerca [F#]
I modelli sono regole per la trasformazione dei dati di input. Vengono utilizzati nel linguaggio F# per confrontare i dati con una o più strutture logiche, scomporre i dati in parti costituenti o estrarre informazioni dai dati in diversi modi.
Note
I modelli vengono utilizzati in numerosi costrutti di linguaggio, ad esempio l'espressione match. I modelli vengono utilizzati quando si elaborano argomenti per le funzioni in associazioni let, in espressioni lambda e nei gestori di eccezioni associati all'espressione try...with. Per ulteriori informazioni, vedere Espressioni match (F#), Associazioni let (F#), Espressioni lambda: parola chiave fun (F#) e Eccezioni: espressione try...with (F#).
Nell'espressione match, ad esempio, l'elemento pattern è quello che segue il simbolo di barra verticale.
match expression with
| pattern [ quando condition ] -> result-expression
...
Ogni modello funge da regola per una determinata trasformazione dell'input. Nell'espressione match ogni modello viene esaminato a turno per verificare se i dati di input sono compatibili con il modello. Se viene individuata una corrispondenza, l'espressione risultante viene eseguita. In caso contrario, viene verificata la regola del modello successiva. La parte when condition facoltativa è illustrata in Espressioni match (F#).
Nella tabella che segue sono illustrati i modelli supportati. In fase di esecuzione, l'input viene verificato rispetto a ognuno dei modelli seguenti nell'ordine con cui sono elencati nella tabella e i modelli vengono applicati in modo ricorsivo, dal primo all'ultimo in base alla loro posizione nel codice e da sinistra verso destra per i modelli su ogni riga.
Nome |
Descrizione |
Esempio |
---|---|---|
Modello costante |
Qualsiasi valore letterale stringa, numerico o carattere, costante di enumerazione o identificatore letterale definito |
1.0, "test", 30, Color.Red |
Modello identificatore |
Valore case di un'unione discriminata, etichetta di eccezione o case di modello attivo |
Some(x) Failure(msg) |
Modello variabile |
identifier |
a |
Modello as |
pattern as identifier |
(a, b) as tuple1 |
Modello OR |
pattern1 | pattern2 |
([h] | [h; _]) |
Modello AND |
pattern1 & pattern2 |
(a, b) & (_, "test") |
Modello costruttore |
identifier :: list-identifier |
h :: t |
Modello elenco |
[ pattern_1; ... ; pattern_n ] |
[ a; b; c ] |
Modello matrice |
[| pattern_1; ..; pattern_n ] |
[| a; b; c |] |
Modello con parentesi |
( pattern ) |
( a ) |
Modello tupla |
( pattern_1, ... , pattern_n ) |
( a, b ) |
Modello record |
{ identifier1 = pattern_1; ... ; identifier_n = pattern_n } |
{ Name = name; } |
Modello carattere jolly |
_ |
_ |
Modello con annotazione del tipo |
pattern : type |
a : int |
Modello di test del tipo |
:? type [ as identifier ] |
:? System.DateTime as dt |
Modello Null |
null |
null |
Modelli costanti
I modelli costanti sono valori letterali numerici, di carattere, di stringa e costanti di enumerazione (includono il nome del tipo di enumerazione). Un'espressione match che include solo modelli costanti può essere confrontata con un'istruzione case in altri linguaggi. L'input viene confrontato con il valore letterale e il modello consente di verificare se i valori sono uguali. Il tipo del valore letterale deve essere compatibile con il tipo dell'input.
Nell'esempio seguente viene illustrato l'utilizzo di modelli letterali e vengono inoltre utilizzati un modello variabile e un modello OR.
[<Literal>]
let Three = 3
let filter123 x =
match x with
// The following line contains literal patterns combined with an OR pattern.
| 1 | 2 | Three -> printfn "Found 1, 2, or 3!"
// The following line contains a variable pattern.
| var1 -> printfn "%d" var1
for x in 1..10 do filter123 x
Un altro esempio di modello letterale è costituito da un modello basato su costanti di enumerazione. Quando si utilizzano costanti di enumerazione, è necessario specificare il nome del tipo di enumerazione.
type Color =
| Red = 0
| Green = 1
| Blue = 2
let printColorName (color:Color) =
match color with
| Color.Red -> printfn "Red"
| Color.Green -> printfn "Green"
| Color.Blue -> printfn "Blue"
| _ -> ()
printColorName Color.Red
printColorName Color.Green
printColorName Color.Blue
Modelli identificatori
Se il modello è una stringa di caratteri che forma un identificatore valido, il formato dell'identificatore determina il modo in cui viene cercata la corrispondenza per il modello. Se l'identificatore è costituito da più di un singolo carattere e inizia con un carattere maiuscolo, il compilatore tenta di trovare una corrispondenza per il modello identificatore. L'identificatore per questo modello potrebbe essere un valore contrassegnato con l'attributo Literal, un case di unione discriminata, un identificatore di eccezione o un case di modello attivo. Se non viene trovato alcun identificatore corrispondente, la ricerca di corrispondenza ha esito negativo e viene confrontata con l'input la regola del modello successiva, ovvero il modello variabile.
I modelli di unione discriminata possono essere case denominati semplici oppure possono disporre di un valore o di una tupla contenente più valori. Se è presente un valore, è necessario specificare un identificatore per tale valore. Nel caso di una tupla, è necessario fornire un modello tupla con un identificatore per ogni elemento della tupla o un identificatore con un nome di campo per uno o più campi unione denominati. Per alcuni esempi, vedere gli esempi di codice in questa sezione.
Il tipo option è un'unione discriminata che dispone di due case, Some e None. Un case (Some) ha un valore, mentre l'altro (None) è semplicemente un case denominato. Some necessita pertanto di una variabile per il valore associato al case Some, mentre None deve essere visualizzato da solo. Nel codice seguente alla variabile var1 viene associato il valore ottenuto dalla corrispondenza con il case Some.
let printOption (data : int option) =
match data with
| Some var1 -> printfn "%d" var1
| None -> ()
Nell'esempio seguente l'unione discriminata PersonName contiene una combinazione di stringhe e caratteri che rappresentano i formati possibili per i nomi. I case dell'unione discriminata sono FirstOnly, LastOnly e FirstLast.
type PersonName =
| FirstOnly of string
| LastOnly of string
| FirstLast of string * string
let constructQuery personName =
match personName with
| FirstOnly(firstName) -> printf "May I call you %s?" firstName
| LastOnly(lastName) -> printf "Are you Mr. or Ms. %s?" lastName
| FirstLast(firstName, lastName) -> printf "Are you %s %s?" firstName lastName
Per le unioni discriminate con campi denominati, utilizzare il segno di uguale (=) per estrarre il valore di un campo denominato. Ad esempio, si consideri un'unione discriminata con una dichiarazione simile alla seguente.
type Shape =
| Rectangle of height : float * width : float
| Circle of radius : float
È possibile utilizzare i campi denominati in un'espressione di criteri di ricerca come illustrato di seguito.
let matchShape shape =
match shape with
| Rectangle(height = h) -> printfn "Rectangle with length %f" h
| Circle(r) -> printfn "Circle with radius %f" r
L'utilizzo del campo denominato è facoltativo, pertanto nell'esempio precedente, sia Circle(r) sia Circle(radius = r) hanno lo stesso effetto.
Quando si specificano più campi, utilizzare il punto e virgola (;) come separatore.
match shape with
| Rectangle(height = h; width = w) -> printfn "Rectangle with height %f and width %f" h w
| _ -> ()
I modelli attivi consentono di definire una corrispondenza di modelli personalizzata più complessa. Per ulteriori informazioni sui modelli attivi, vedere Modelli attivi (F#).
Il caso in cui l'identificatore è un'eccezione viene utilizzato nella corrispondenza di modelli nel contesto dei gestori di eccezioni. Per informazioni sulla corrispondenza dei modelli nella gestione delle eccezioni, vedere Eccezioni: espressione try...with (F#).
Modelli variabili
Il modello variabile assegna il valore di cui viene cercata la corrispondenza a un nome di variabile, che è quindi disponibile per l'utilizzo nell'espressione di esecuzione a destra del simbolo ->. Un modello variabile da solo consente di trovare la corrispondenza con qualsiasi input, ma i modelli variabili sono spesso inclusi in altri modelli, consentendo pertanto di creare strutture più complesse come tuple e matrici da scomporre in variabili.
Nell'esempio seguente viene illustrato un modello variabile all'interno di un modello tupla.
let function1 x =
match x with
| (var1, var2) when var1 > var2 -> printfn "%d is greater than %d" var1 var2
| (var1, var2) when var1 < var2 -> printfn "%d is less than %d" var1 var2
| (var1, var2) -> printfn "%d equals %d" var1 var2
function1 (1,2)
function1 (2, 1)
function1 (0, 0)
Modello as
Il modello as è un modello a cui è aggiunta la clausola as. La clausola as consente di associare il valore di cui è stata cercata la corrispondenza con un nome che può essere utilizzato nell'espressione di esecuzione di un'espressione match o, nel caso in cui il modello venga utilizzato nell'associazione let, il nome viene aggiunto come associazione all'ambito locale.
Nell'esempio seguente viene utilizzato un modello as.
let (var1, var2) as tuple1 = (1, 2)
printfn "%d %d %A" var1 var2 tuple1
Modello OR
Il modello OR viene utilizzato quando i dati di input possono corrispondere a più modelli e si desidera eseguire lo stesso codice come risultato. I tipi su entrambi i lati del modello OR devono essere compatibili.
Nell'esempio seguente viene illustrato il modello OR.
let detectZeroOR point =
match point with
| (0, 0) | (0, _) | (_, 0) -> printfn "Zero found."
| _ -> printfn "Both nonzero."
detectZeroOR (0, 0)
detectZeroOR (1, 0)
detectZeroOR (0, 10)
detectZeroOR (10, 15)
Modello AND
Il modello AND richiede che l'input corrisponda a due modelli. I tipi su entrambi i lati del modello AND devono essere compatibili.
L'esempio è analogo a detectZeroTuple, illustrato nella sezione Modello tupla più avanti nel presente argomento, ma in questo caso sia var1 che var2 vengono ottenuti come valori utilizzando il modello AND.
let detectZeroAND point =
match point with
| (0, 0) -> printfn "Both values zero."
| (var1, var2) & (0, _) -> printfn "First value is 0 in (%d, %d)" var1 var2
| (var1, var2) & (_, 0) -> printfn "Second value is 0 in (%d, %d)" var1 var2
| _ -> printfn "Both nonzero."
detectZeroAND (0, 0)
detectZeroAND (1, 0)
detectZeroAND (0, 10)
detectZeroAND (10, 15)
Modello costruttore
Il modello costruttore viene utilizzato per scomporre un elenco nel primo elemento, head, e in un elenco che contiene gli elementi rimanenti, tail.
let list1 = [ 1; 2; 3; 4 ]
// This example uses a cons pattern and a list pattern.
let rec printList l =
match l with
| head :: tail -> printf "%d " head; printList tail
| [] -> printfn ""
printList list1
Modello elenco
Il modello elenco consente di scomporre gli elenchi in diversi elementi. Il modello elenco consente di trovare la corrispondenza solo di elenchi con un numero specifico di elementi.
// This example uses a list pattern.
let listLength list =
match list with
| [] -> 0
| [ _ ] -> 1
| [ _; _ ] -> 2
| [ _; _; _ ] -> 3
| _ -> List.length list
printfn "%d" (listLength [ 1 ])
printfn "%d" (listLength [ 1; 1 ])
printfn "%d" (listLength [ 1; 1; 1; ])
printfn "%d" (listLength [ ] )
Modello matrice
Il modello matrice assomiglia al modello elenco e può essere utilizzato per scomporre matrici di una lunghezza specifica.
// This example uses array patterns.
let vectorLength vec =
match vec with
| [| var1 |] -> var1
| [| var1; var2 |] -> sqrt (var1*var1 + var2*var2)
| [| var1; var2; var3 |] -> sqrt (var1*var1 + var2*var2 + var3*var3)
| _ -> failwith "vectorLength called with an unsupported array size of %d." (vec.Length)
printfn "%f" (vectorLength [| 1. |])
printfn "%f" (vectorLength [| 1.; 1. |])
printfn "%f" (vectorLength [| 1.; 1.; 1.; |])
printfn "%f" (vectorLength [| |] )
Modello con parentesi
È possibile raggruppare le parentesi attorno ai modelli per ottenere l'associazione desiderata. Nell'esempio seguente le parentesi vengono utilizzate per controllare l'associazione tra un modello AND e un modello costruttore.
let countValues list value =
let rec checkList list acc =
match list with
| (elem1 & head) :: tail when elem1 = value -> checkList tail (acc + 1)
| head :: tail -> checkList tail acc
| [] -> acc
checkList list 0
let result = countValues [ for x in -10..10 -> x*x - 4 ] 0
printfn "%d" result
Modello tupla
Il modello tupla consente di trovare la corrispondenza di input in formato di tupla e di scomporre la tupla nei relativi elementi costituenti utilizzando variabili di corrispondenza dei modelli per ogni posizione nella tupla.
Nell'esempio seguente viene illustrato il modello tupla e vengono inoltre utilizzati modelli letterali, modelli variabili e un modello carattere jolly.
let detectZeroTuple point =
match point with
| (0, 0) -> printfn "Both values zero."
| (0, var2) -> printfn "First value is 0 in (0, %d)" var2
| (var1, 0) -> printfn "Second value is 0 in (%d, 0)" var1
| _ -> printfn "Both nonzero."
detectZeroTuple (0, 0)
detectZeroTuple (1, 0)
detectZeroTuple (0, 10)
detectZeroTuple (10, 15)
Modello record
Il modello record è utilizzato per scomporre record per estrarre i valori di campi. Il modello non deve fare riferimento a tutti i campi del record. Eventuali campi omessi non partecipano alla corrispondenza e non vengono estratti.
// This example uses a record pattern.
type MyRecord = { Name: string; ID: int }
let IsMatchByName record1 (name: string) =
match record1 with
| { MyRecord.Name = nameFound; MyRecord.ID = _; } when nameFound = name -> true
| _ -> false
let recordX = { Name = "Parker"; ID = 10 }
let isMatched1 = IsMatchByName recordX "Parker"
let isMatched2 = IsMatchByName recordX "Hartono"
Modello carattere jolly
Il modello jolly è rappresentato dal carattere di sottolineatura (_) e corrisponde a qualsiasi input, come il modello variabile, con l'eccezione che l'input viene rimosso anziché essere assegnato a una variabile. Il modello carattere jolly viene spesso utilizzato all'interno di altri modelli come segnaposto per valori non necessari nell'espressione a destra del simbolo ->, nonché anche alla fine di un elenco di modelli per trovare la corrispondenza con qualsiasi input senza corrispondenza. Il modello carattere jolly è illustrato in numerosi esempi di codice in questo argomento. Per un esempio, vedere il codice riportato in precedenza.
Modelli con annotazioni del tipo
I modelli possono includere annotazioni del tipo. Queste annotazioni si comportano come altre annotazioni del tipo e determinano l'inferenza in modo analogo. Le annotazioni del tipo nei modelli devono essere racchiuse tra parentesi. Nel codice seguente viene illustrato un modello con un'annotazione del tipo.
let detect1 x =
match x with
| 1 -> printfn "Found a 1!"
| (var1 : int) -> printfn "%d" var1
detect1 0
detect1 1
Modello di test del tipo
Il modello di test del tipo è utilizzato per trovare la corrispondenza tra l'input e un tipo. Se il tipo di input corrisponde a quello specificato nel modello, oppure è un tipo derivato di tale tipo, la corrispondenza ha esito positivo.
Nell'esempio seguente viene illustrato il modello di test del tipo.
open System.Windows.Forms
let RegisterControl(control:Control) =
match control with
| :? Button as button -> button.Text <- "Registered."
| :? CheckBox as checkbox -> checkbox.Text <- "Registered."
| _ -> ()
Modello Null
Il modello Null consente di trovare la corrispondenza con il valore Null visualizzato quando si utilizzano tipi che consentono un valore Null. I modelli Null vengono spesso utilizzati in caso di interazione con il codice .NET Framework. Il valore restituito di un'API .NET può ad esempio essere l'input di un'espressione match. È possibile controllare il flusso del programma in base al fatto che il valore restituito sia Null, nonché in base ad altre caratteristiche del valore restituito. È possibile utilizzare il modello Null per impedire la propagazione di valori Null nella parte restante del programma.
Nell'esempio seguente vengono utilizzati il modello Null e il modello variabile.
let ReadFromFile (reader : System.IO.StreamReader) =
match reader.ReadLine() with
| null -> printfn "\n"; false
| line -> printfn "%s" line; true
let fs = System.IO.File.Open("..\..\Program.fs", System.IO.FileMode.Open)
let sr = new System.IO.StreamReader(fs)
while ReadFromFile(sr) = true do ()
sr.Close()