Riesci a nidificare una corrispondenza del modello "doppio contro"?
-
05-07-2019 - |
Domanda
Voglio rafforzare un modello per abbinare solo i numeri che passano una funzione di convalida aggiuntiva.
let (|IsValid|_|) n = ...
let (|Nil|One|Two|) (l : int list) =
match l with
| a :: b :: t -> Two(a + b)
| a :: t -> One(a)
| _ -> Nil
Il caso "One" è semplice:
| IsValid(a) :: t -> One(a)
Il caso "Due" non è ovvio per me. Deve convalidare la somma dei numeri. Posso farlo senza usare una protezione quando?
...
Modifica: potrei usare un when-guard (con una funzione isValid di ritorno bool) come questo:
| a :: b :: t when isValid a + b -> Two(a + b)
Questo è meno elegante del semplice abbinamento di un motivo; peggio, a + b viene applicato due volte.
Nota anche che questa è una versione semplificata del mio codice attuale (non sto provando ad abbinare semplicemente con diverse lunghezze di elenco per esempio) - la domanda riguarda la corrispondenza nidificata sul modello a doppio contro.
Soluzione
La mia soluzione: aggiungi un " helper " riconoscitore con un valore di ritorno progettato per essere utilizzato nel modello principale:
let (|MatchTwo|_|) = function
| a :: b :: t -> Some(a + b :: t)
| _ -> None
Usalo in questo modo:
let (|Nil|One|Two|) (l : int list) =
match l with
| MatchTwo(IsValid(a) :: t) -> Two(a)
| IsValid(a) :: t -> One(a)
| _ -> Nil
Altri suggerimenti
Quando lo fai:
| a :: b :: t -> ...
Non stai necessariamente abbinando due elementi in un elenco. È meglio usare []
anziché t
per abbinare esattamente due elementi - t
può essere un elenco di più elementi.
| a :: b :: [] -> Two (a+b)
Questo assicurerà che abbini due e solo due elementi: il controllo degli errori è gratuito! Suggerisco di farlo anche se ci si aspetta che la funzione accetti solo un elenco di 0, 1 o 2 elementi. Così,
EDIT:
let (|MatchTwo|_|) = function
| a :: b :: t -> Some(a + b :: t)
| _ -> None
let (|Nil|One|Two|) (l : int list) = match l with
| MatchTwo(IsValid(a) :: t) -> Two(a)
| IsValid(a) :: t -> One(a)
| _ -> Nil
Sì, usa quando
. Questo è un casino. Il pattern matching è proprio questo, l'applicazione delle funzioni all'interno del match non ha davvero senso. Ma tieni conto di ciò che ho menzionato prima. Sulla base del tuo esempio:
match l with
| a :: b :: t when isValid (a+b) -> Two (a+b)
| a :: t when isValid (a) -> One a
| _ -> Nil
Il secondo modello corrisponderà a elenchi di lunghezza maggiore di uno se isValid è falso sul primo modello - sii avvisato. Sii il più specifico possibile nei tuoi schemi, se intendi abbinare un elemento, fallo.
Se qualsiasi operazione che usi per combinare aeb (in questo caso +) è computazionalmente costosa, dovrai eliminare quando
e utilizzare un'istruzione let prima di testare isValid e restituire tipo di variante.