¿Puede anidar un & # 8220; dobles contras & # 8221; ¿coincidencia de patrones?

StackOverflow https://stackoverflow.com/questions/420832

  •  05-07-2019
  •  | 
  •  

Pregunta

Quiero fortalecer un patrón para que coincida solo con los números que pasan una función de validación adicional.

let (|IsValid|_|) n = ...

let (|Nil|One|Two|) (l : int list) =
    match l with 
    | a :: b :: t -> Two(a + b)
    | a :: t      -> One(a)
    | _           -> Nil

El caso 'Uno' es fácil:

    | IsValid(a) :: t -> One(a)

El caso 'Dos' no es obvio para mí. Necesita validar la suma de los números. ¿Puedo hacer esto sin usar un guardián cuando?

...

Editar: podría usar una protección cuándo (con una función isValid que devuelve bool) como esta:

    | a :: b :: t when isValid a + b -> Two(a + b)

Esto es menos elegante que simplemente hacer coincidir un patrón; peor, a + b se aplica dos veces.

También tenga en cuenta que esta es una versión simplificada de mi código real (no estoy tratando de hacer coincidir simplemente con diferentes longitudes de lista, por ejemplo): la pregunta es sobre la coincidencia anidada sobre el patrón de doble contra.

¿Fue útil?

Solución

Mi solución: agregar un " ayudante " reconocedor con un valor de retorno diseñado para ser utilizado en el patrón padre:

let (|MatchTwo|_|) = function
    | a :: b :: t -> Some(a + b :: t)
    | _ -> None

Úsalo así:

let (|Nil|One|Two|) (l : int list) =
    match l with 
    | MatchTwo(IsValid(a) :: t) -> Two(a)
    |          IsValid(a) :: t  -> One(a)
    | _                         -> Nil

Otros consejos

Cuando lo hagas:

| a :: b :: t -> ... 

No estás necesariamente haciendo coincidir dos elementos en una lista. Es mejor usar [] en lugar de t para hacer coincidir exactamente dos elementos: t puede ser una lista de más elementos.

 | a :: b :: [] -> Two (a+b)

Esto asegurará que coincida con dos y solo dos elementos: ¡comprobación de errores gratis! Sugiero hacer esto aunque espere que la función solo acepte una lista de 0, 1 o 2 elementos. Entonces,

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í, use cuando . Esto es un desastre. La coincidencia de patrones es solo eso, la aplicación de funciones dentro de la coincidencia realmente no tiene sentido. Pero ten en cuenta lo que he mencionado antes. Basado en su ejemplo:

match l with
| a :: b :: t when isValid (a+b) -> Two (a+b)
| a :: t when isValid (a) -> One a
| _ -> Nil

El segundo patrón coincidirá con las listas de longitud más largas que una si isValid es falso en el primer patrón: tenga cuidado. Sea lo más específico posible en sus patrones, si quiere hacer coincidir un elemento, hágalo.

Si cualquier operación que use para combinar ayb (en este caso +) es computacionalmente costosa, entonces tendrá que soltar when y usar una instrucción let antes de probar isValid y devolver el tipo de variante.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top