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.

È stato utile?

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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top