Pregunta

Acabo de notar que F # me permite usar enlaces 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 una especie de coincidencia de patrones, porque me da las siguientes advertencias:

  

Advertencia 1 Coincidencias de patrones incompletos   En esta expresión. Por ejemplo, el   el valor '1' no será   emparejado

     

Advertencia 2 Coincidencias de patrones incompletos   En esta expresión. Por ejemplo, el   el valor '[_]' no será   emparejado

     

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 alternativa de coincidencia de patrones 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 yo ' acabo de descubrir otra sintaxis para la coincidencia de patrones y me molestará para siempre si no descubro cómo usarla.

¿Cómo escribo funciones usando la sintaxis alternativa de coincidencia de patrones que se muestra arriba?

¿Fue útil?

Solución

JaredPar tiene razón, F # no tiene la forma sintáctica que Haskell tiene aquí.

El formulario F # es principalmente útil para romper uniones discriminadas abiertas de un solo caso o para definir funciones con coincidencias incompletas (como el ejemplo de su 'automóvil' que falla en la lista vacía). Es simplemente una consecuencia del hecho de que prácticamente todo el enlace de nombres en el lenguaje se realiza a través de patrones; esta forma sintáctica (que define una función usando patrones en argumentos) a menudo no es demasiado útil en la práctica, por la razón exacta que 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 F # compila con OCaml (que ayudó a arrancar el lenguaje F # y la comunidad de usuarios); El inconveniente es que F # está 'atascado' con algunos bits de sintaxis fea / limitada.

Otros consejos

AFAIK, no hay forma en F # de declarar múltiples enlaces let 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 función.

Tome este ejemplo para automóvil

let car = function
    | hd::tl -> hd
    | [] -> failwith "empty list"

Aparentemente, la coincidencia de patrones de F # es mucho más poderosa de lo que usamos en el desarrollo común.

Primero, puede vincular varios valores a la vez. Por lo general, lo hará 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 enlace simple let por simplicidad.

let hd::tl = data
  

advertencia FS0025 : coincidencias de patrones incompletos en 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' enlazan diferentes conjuntos de variables

Y eso es cierto; en caso de lista vacía, hd y tl permanecen sin consolidar. Es fácil vincular tl con la misma lista vacía:

let (hd::tl) | ([] as tl) = data

Sin embargo, el error error FS0018 no desaparece. De hecho, también tenemos que proporcionar algunos '' por defecto '' valor para hd .
El siguiente truco sucio hará esto.

let (hd::tl, _) | ([] as tl , hd) = data, 42

La línea de arriba unirá hd a la cabeza de data , en caso de que la lista no esté vacía, o con el extra valor proporcionado en el segundo valor de la tupla .

Nota No he encontrado la manera de incrustar 42 en la construcción let .

Finalmente, lo mismo para la función car :

let car ((hd::tl, _) | ([] as tl, hd)) = hd
let foo = car(data, 42) // foo = 7
let bar = car([], 42)   // bar = 42
  

¿Cómo escribo funciones usando la sintaxis alternativa de coincidencia de patrones que se muestra arriba?

Como lo has hecho pero solo cuando un patrón es exhaustivo. Los ejemplos obvios en los que esto es útil incluyen tuplas y registros, así como uniones de casos únicos y patrones activos.

Utiliza esta función de idioma cada vez que lo haces:

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 Algún valor :

let def(x, None | _, Some x) = x

Por cierto, el estilo que sugieres se usó en SML antes de Haskell y SML es un ML, por lo que esto obviamente no tiene nada que ver con Haskell vs ML. De hecho, 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 existe un valor real mediante el uso de identificadores autodocumentados.

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