syntax - ¿Cómo uso la coincidencia de patrones en las definiciones ''let''?
f# pattern-matching (4)
AFAIK, no hay forma de que en F # se declaren enlaces múltiples con el mismo nombre y diferentes firmas de coincidencia de patrones. Creo que la construcción más cercana a lo que estás buscando es una expresión de reglas de funciones.
Tome este ejemplo para el automóvil
let car = function
| hd::tl -> hd
| [] -> failwith "empty list"
Me acabo de dar cuenta de que F # me permite usar las vinculaciones de let con literales y otros patrones de la siguiente manera:
let fib 0 = 1
let exists item [] = false
let car (hd :: tl) = hd
let cdr (hd :: tl) = tl
F # interpreta correctamente estas funciones como un tipo de coincidencia de patrones, porque me da las siguientes advertencias:
Advertencia 1 El patrón incompleto coincide con esta expresión. Por ejemplo, el valor ''1'' no coincidirá
Advertencia 2 El patrón incompleto coincide con esta expresión. Por ejemplo, el valor ''[_]'' no coincidirá
etc.
Estas funciones funcionan como se esperaba, pero quiero definir una función en este estilo con coincidencias de patrones completas, sin embargo, no puedo encontrar nada sobre esta sintaxis de coincidencia de patrones alternativa en el manual de F #.
Sé que puedo usar let whatever = function ...
y let whatever x = match x with ...
para obtener los resultados que quiero, pero acabo de descubrir otra sintaxis para la coincidencia de patrones y me molestará para siempre si no descubro cómo usarlo
¿Cómo escribo funciones usando la sintaxis de coincidencia de patrones alternativa que se muestra arriba?
JaredPar tiene razón, F # no tiene la forma sintáctica que tiene Haskell aquí.
La forma F # es principalmente útil para romper uniones discriminadas de un solo caso o para definir funciones con coincidencias incompletas (como el ejemplo de "automóvil" que falla en la lista vacía). Es simplemente una consecuencia del hecho de que prácticamente todos los enlaces de nombres en el lenguaje se realizan a través de patrones; esta forma sintáctica (que define una función que usa patrones en argumentos) a menudo no es demasiado útil en la práctica, por la razón exacta que usted describió.
Creo que Haskell hizo varias cosas mejor que ML cuando se trata de formas sintácticas, pero las raíces de F # están en ML. El beneficio es que un buen subconjunto de compilaciones cruzadas F # con OCaml (que ayudó a iniciar el lenguaje F # y la comunidad de usuarios); el inconveniente es que F # está "atascado" con algunos bits de sintaxis fea / limitada.
¿Cómo escribo funciones usando la sintaxis de coincidencia de patrones alternativa que se muestra arriba?
Como lo has hecho, pero solo cuando un patrón es exhaustivo. Los ejemplos obvios donde esto es útil incluyen tuplas y registros, así como uniones de un solo caso y patrones activos.
Utiliza esta función de idioma siempre que lo haga:
let f(a, b) = ...
Entonces esto se generaliza a:
let f(a, (b, c)) = ...
Incluso puede usar esto para elegir entre un valor predeterminado o Some value
:
let def(x, None | _, Some x) = x
Por cierto, el estilo que está sugiriendo se usó en SML antes de que Haskell y SML sean ML, por lo que obviamente esto no tiene nada que ver con Haskell vs ML. Realmente prefiero el estilo OCaml / F # porque es menos repetitivo:
fun descriptiveFunctionName [] = true
fun descriptiveFunctionName (_::_) = false
vs
let descriptiveFunctionName = function
| [] -> true
| _::_ -> false
Esto es mejor para el código no académico donde hay un valor real al usar identificadores autodocumentados.
Aparentemente, la coincidencia de patrones de F # es mucho más poderosa de lo que usamos en el desarrollo común.
Primero, puedes vincular varios valores a la vez. Por lo general, lo harás con List.partition
:
let data = [7;0;0;0;1;0;1;1;1;1;0;0;1;1;0]
let ones, others = data |> List.partition ((=) 1) // bind two values
Como nota al margen, puede vincular varios identificadores al mismo valor:
let (a & b) = 42 // a = 42; b = 42
Comencemos con un simple let
vinculante en aras de la simplicidad.
let hd::tl = data
advertencia FS0025 : el patrón incompleto coincide con esta expresión. Por ejemplo, el valor ''[]'' puede indicar un caso no cubierto por el patrón (s).
Para mitigar esto, tenemos que agregar otro caso para la lista vacía:
let (hd::tl) | [] = data
error FS0018 : los dos lados de este patrón ''o'' vinculan diferentes conjuntos de variables
Y eso es verdad; en caso de lista vacía, hd
y tl
permanecen sin consolidar. Es fácil enlazar tl
con la misma lista vacía:
let (hd::tl) | ([] as tl) = data
Sin embargo, el error de error FS0018 no desaparece. De hecho, también tenemos que proporcionar un valor "predeterminado" para hd
.
El siguiente truco sucio hará esto.
let (hd::tl, _) | ([] as tl , hd) = data, 42
La línea de arriba vinculará hd
a la cabecera de los data
, en caso de que la lista no esté vacía, o con el valor adicional provisto en el segundo valor de la tuple
.
Tenga en cuenta que no he encontrado la manera de insertar 42
en la construcción let
.
Finalmente, lo mismo para la función del car
:
let car ((hd::tl, _) | ([] as tl, hd)) = hd
let foo = car(data, 42) // foo = 7
let bar = car([], 42) // bar = 42