tutorial simbolos regulares probar patrones online expresiones ejemplos busqueda function haskell pattern-matching compiler-warnings clause

function - simbolos - Por qué[x, y,_] no está permitido en la coincidencia de patrones



expresiones regulares tutorial (4)

Aquí está mi código:

tell :: (Show a) => [a] -> String tell [] = "The list is empty" tell (x:[]) = "The list has one element: " ++ show x tell (x:y:[]) = "The list has two elements: " ++ show x ++ " and " ++ show y tell (x:y:_) = "Other"

en la última línea, ¿por qué no puedo cambiar (x:y:_) a [x, y, _] ?

¿Y cuál es el significado global de _ ?


en la última línea por qué no puedo cambiar (x: y: _) a [x, y, _]

Tienen diferentes significados:

  • [x, y, _] refiere a una lista con tres elementos; el primer elemento está vinculado a x , el segundo está vinculado a y , y el tercer elemento no está vinculado a ningún nombre.
  • Por el contrario, (x:y:_) refiere a una lista con al menos dos elementos, ya que : es un constructor utilizado para unir el primer elemento de una lista con el resto de la lista; en el patrón anterior, el primer elemento está vinculado a x , el segundo está vinculado a y , y el resto de la lista (que puede estar vacía o no) no está vinculada a ningún nombre.

¿Y cuál es el significado global de _?

Realmente no deberías hacer dos preguntas en una publicación, pero es relevante aquí, así que: _ es solo un marcador de posición cuando quieres hacer coincidir un patrón en particular, pero no darle un nombre. Entonces, en los ejemplos anteriores:

  • [x,y,z] enlazará cada elemento de una lista de tres elementos con los nombres x , y y z respectivamente. Cuando reemplaza z con _ , aún coincide con una lista de tres elementos, pero no le da un nombre al último elemento.
  • (x:y:z) vinculará los primeros dos elementos de una lista de al menos dos elementos a los nombres x e y , luego vinculará el resto de la lista a z . Cuando reemplaza z con _ , aún coincide con una lista de al menos dos elementos, pero no le da un nombre al resto de la lista.

En los patrones, _ significa "hacer coincidir cualquier cosa aquí, no me importa".

Como los guiones bajos son caracteres válidos en los identificadores, casi puede tratar _ como equivalente a cualquier otro nombre de variable que simplemente no utilice en el lado derecho. (La única diferencia es cuando escribe múltiples _ en el mismo patrón; en ese caso, cada _ se trata como algo que coincide por separado , mientras que el uso de una variable normal varias veces en un patrón da un error sobre definiciones en conflicto para la variable)

Como solo usa un solo _ en cada patrón, podemos ver que hará lo mismo si solo inventamos una nueva variable y la usamos.

Entonces tell (x:y:_) = ... es lo mismo que tell (x:y:z) = ...

OTOH tell [x, y, _] = ... es lo mismo que tell [x, y, z] = ... Esto es completamente diferente a lo anterior. En x:y:z , el argumento izquierdo de cada : es un elemento de la lista y el argumento derecho es una lista que contiene el resto de los elementos 1 . Pero en [x, y, z] , cada uno de x , y y z son elementos de la lista .

La sintaxis de la lista de corchetes [a, b, c, d, ...] no le permite consultar la cola de la lista . Cada una de las expresiones separadas por comas son elementos contenidos en la lista, y la lista tiene exactamente el número de elementos que escribió. No puede usar la sintaxis de la lista de corchetes para escribir un patrón que coincida con una lista con un número arbitrario de elementos.

Entonces, el patrón (x:y:_) (igual que (x:y:z) ) coincide con una lista con al menos 2 elementos , y el _ coincide con los elementos restantes cero o más de la lista después de los primeros 2.

El patrón [x, y, _] coincide con una lista con exactamente 3 elementos , y el _ coincide con el tercer elemento.

1 El operador : es asociativo correcto, entonces x:y:z significa x:(y:z) ; y son argumentos a la izquierda para diferentes : y, por lo tanto, elementos, mientras que z es un argumento correcto para : y por lo tanto una lista.

Para responder a su pregunta sobre el "significado global de _ ", en realidad no tiene un significado global. Significa diferentes cosas en diferentes contextos, lo que puede ser un poco confuso. En general, todos se relacionan con la idea de "no sé" o "no me importa", pero en realidad no están conectados.

La discusión anterior fue sobre el significado de _ en los patrones .

En las expresiones , puede usar _ como marcador de posición para indicar "Todavía no sé qué pasa aquí". Si compila código que contiene _ como expresión, GHC le dará un error, pero tratará de averiguar el tipo de lo que podría ir allí y le dará alguna información al respecto en el mensaje de error. Con mucha frecuencia comienzo a escribir una función escribiendo una "forma" básica llena de _ , y luego dejo que el compilador me diga qué tipo de cosas necesito para completar los huecos.

Por ejemplo, compilando esto:

mymap :: (a -> b) -> [a] -> [b] mymap f [] = [] mymap f (x:xs) = _ : _

Produce esto:

/tmp/foo.hs:4:18: error: • Found hole: _ :: b Where: ‘b’ is a rigid type variable bound by the type signature for: mymap :: forall a b. (a -> b) -> [a] -> [b] at /tmp/foo.hs:2:1-31 • In the first argument of ‘(:)’, namely ‘_’ In the expression: _ : _ In an equation for ‘mymap’: mymap f (x : xs) = _ : _ • Relevant bindings include xs :: [a] (bound at /tmp/foo.hs:4:12) x :: a (bound at /tmp/foo.hs:4:10) f :: a -> b (bound at /tmp/foo.hs:4:7) mymap :: (a -> b) -> [a] -> [b] (bound at /tmp/foo.hs:3:1) | 4 | mymap f (x:xs) = _ : _ | ^ /tmp/foo.hs:4:22: error: • Found hole: _ :: [b] Where: ‘b’ is a rigid type variable bound by the type signature for: mymap :: forall a b. (a -> b) -> [a] -> [b] at /tmp/foo.hs:2:1-31 • In the second argument of ‘(:)’, namely ‘_’ In the expression: _ : _ In an equation for ‘mymap’: mymap f (x : xs) = _ : _ • Relevant bindings include xs :: [a] (bound at /tmp/foo.hs:4:12) x :: a (bound at /tmp/foo.hs:4:10) f :: a -> b (bound at /tmp/foo.hs:4:7) mymap :: (a -> b) -> [a] -> [b] (bound at /tmp/foo.hs:3:1) Valid hole fits include mempty :: forall a. Monoid a => a with mempty @[b] (imported from ‘Prelude’ at /tmp/foo.hs:1:1 (and originally defined in ‘GHC.Base’)) | 4 | mymap f (x:xs) = _ : _ | ^

Otro uso de _ es en firmas de tipo . Aquí significa "No sé qué es esto, dime". Como el compilador puede inferir tipos de todos modos la mayor parte del tiempo, simplemente le dirá en el mensaje de error qué debe usar para completar el espacio en blanco.

Por ejemplo, compilando esto:

foo :: Int -> _ foo x = Just x

Produce:

/tmp/foo.hs:2:15: error: • Found type wildcard ‘_’ standing for ‘Maybe Int’ To use the inferred type, enable PartialTypeSignatures • In the type signature: foo :: Int -> _ | 2 | foo :: Int -> _ | ^

(Incluso puede usar la extensión de lenguaje PartialTypeSignatures para permitir que GHC continúe y use el tipo que infiere para completar los espacios en blanco, en lugar de tratarlo como un error)


Puede cambiar (x : y : _) a [x, y, _] , pero no significará lo mismo. [x, y, _] es equivalente a (x : y : _ : []) , es decir, una lista de exactamente tres elementos (los dos primeros están vinculados a x e y ).

Del mismo modo, x : y es una lista cuya cabeza (primer elemento) es x cuya cola (elementos restantes) es y , pero [x, y] es una lista de exactamente dos elementos.

No estoy seguro de lo que quiere decir con "significado global", pero _ es un patrón comodín que coincide con cualquier valor, pero no lo vincula a un nombre.


Resumen rápido:

data [a] = [] | a : [a]

Entonces, para hacer una coincidencia de patrones que expliquen por sí mismos:

head (x:_) = x tail (_:xs) = xs secondElem (_:x:_) = x wrapInList x = [x] wrapInList x = x : [] wrapTwoInlist x y = [x, y] wraptwoInlist x y = x : y : [] unWrapFromList [x] = x unWrapTwoFromList (x:y:_) = (x,y) unWrapFromTwoSizeList [x,y] = (x,y) unWrapFromThreeSizeList [x,y,_] = (x,y) unWrapFromIDontKnowTheSizeList (x:y:_) = (x,y)

Los dos últimos son la principal diferencia entre [x,y,_] y (x:y:_) , el primero es una lista de tres elementos, el otro no se puede distinguir.