2011-11-03 16 views
19

Soy un novato en Haskell y espero que esta pregunta no sea tonta.¿La "Lista" se maneja especialmente en la coincidencia de patrones de Haskell?

he visto tanta ejemplo, que cuando estoy teniendo una lista, yo soy capaz de igualar y unir "elemento de composición" de la lista de variables individuales:

listSizeDesc :: [a] -> String 
listSizeDesc [] = "Emtpy" 
listSizeDesc (x:xs) = "Something inside" 

Sin embargo, traté de hacer algo como:

foo :: Int -> String 
foo 0 = "Zero" 
foo (n - 1) = "next number is " ++ show n 

No funciona.

Me parece que tanto (n-1) como (x: xs) describen cómo se "crea" el argumento y vincula el "componente" a un argumento. ¿La forma en que se combinó la Lista está especialmente diseñada para facilitar la recursión? Porque me parece que esta lógica de vinculación/vinculación de argumentos no se aplica a otras funciones, excepto (:).

+13

Definitivamente no es una pregunta estúpida. Este es mi entendimiento como un compañero novato: las listas no son "especiales". La coincidencia de patrones funciona con: porque: es un constructor de tipos para listas. Funciona tipo constructores pero no para funciones generales. Cómo se explica bastante bien este trabajo [aquí en WikiBooks] (http://en.wikibooks.org/wiki/Haskell/Pattern_matching) –

+1

No existe una pregunta tonta, siempre y cuando esté bien planteada. – hugomg

+1

Respuesta corta: No. –

Respuesta

14

La lista es "Suma tipo" con un constructor, algo así como:

data List a = 
    cons a (List a) 
    | nil 

Usted primer ejemplo es una comparación de patrones en un tipo de datos (con azúcar sintáctica para :).

Su segundo ejemplo es una coincidencia de patrón en enteros, que no son una definición de tipo de datos. En enteros, no hay un patrón que use su sintaxis.Puede escribir el ejemplo con:

foo :: Int -> String 
foo 0 = "Zero" 
foo n = "next number is " ++ show (n+1) 

En una nota si codifica enteros con tipos de datos como:

data Nat = Zero | Succ Nat deriving (Show) 

continuación, puede utilizar el ajuste de patrones, ya que queríamos inicialmente.

foo :: Nat -> String 
foo Zero = "Zero" 
foo [email protected](p) = "next number is " ++ show(n) 

Aquí el patrón Succ(p) desempeña el papel de n-1.

+0

¡Excelentes ejemplos! Todo tiene sentido para mí ahora: D Muchas gracias –

+2

Ese último ejemplo es al revés. Para que coincida con lo que quiere el OP, el patrón debería ser 'n' y la función debería mostrar' Succ n'. – jwodder

3
moo :: Int -> String 
moo 0 = "Zero" 
moo n = "next number is " ++ show (n + 1) 

n - 1 es una aplicación de función normal, no un patrón. Se solía hacer una excepción para + y este podría ser el modelo que está utilizando. Puede escribir algo como

goo :: Int -> String 
goo 0 = "Zero" 
goo (n+1) = "previous number is " ++ show n 

en hugs; todavía se puede hacer esto con la ghc, si se incluye el pragma

{-#LANGUAGE NPlusKPatterns#-} 
+5

Por cierto, esos 'NPlusKPatterns' están en desuso, ya que confunden a muchos programadores. ¡Use con cuidado! Además, AFAIK solo coinciden con números positivos. – fuz

+0

Sí, ese era mi punto, supongo que no expresado claramente. – applicative

18

El problema que estamos encontrando es que la coincidencia de patrones sólo funciona con constructores de datos. Un constructor de datos es, en esencia, muy simple; solo toma valores de datos y los agrupa en algún tipo de estructura. Por ejemplo, data Foo = Bar a b simplemente toma dos datos y los agrupa bajo la etiqueta Foo. La función (:) que usa en su primer ejemplo es más que solo una función; es un constructor de datos. Construye una nueva lista agregando el argumento de la izquierda al argumento de la derecha.

Ahora, la coincidencia de patrones simplemente está haciendo lo contrario de este proceso. Deconstruye un tipo de datos. Cuando escribe (x:xs) en su patrón, está extrayendo las dos piezas de datos que el constructor cosió originalmente. Por lo tanto, todas las coincidencias de patrones se extraen de los datos que un constructor unió previamente.

Hay una excepción: patrones n + k. En Haskell98, se te permitió usar patrones de la forma (n + k). Esta fue una especie de excepción arbitraria y fue eliminada recientemente. Si lo desea, puede usarlo si incluye el lenguaje pragma NPlusKPatterns.

tipo
+0

Gran respuesta. Aunque di el "Aceptar" a otra respuesta debido al ejemplo intuitivo dado, esta respuesta es indudablemente tan buena. Ojalá pueda aceptar 2 respuestas al mismo tiempo porque creo que ambas respuestas son buenas cumplidas entre sí. –

5

Ya hay algunas buenas respuestas, así que no me molestaré con la pregunta principal. Esto es no el mejor uso, pero lo que intenta hacer puede lograrse con view patterns.

{-# LANGUAGE ViewPatterns #-} 

foo :: Int -> String 
foo 0 = "Zero" 
foo (pred -> n) = "Next number is " ++ show n 
4

Sólo para ponerlo de forma más sencilla posible:
Una lista literalmente es una serie de concatenaciones. Un número podría ser equivalente a el resultado de una operación aritmética. La diferencia es que el resultado de a : b es simplemente a : b.


En más detalle:

Listas y (:) no son un caso especial. Vamos a hacer nuestra propia:

data List2 a = End    -- equivalent of "[]" 
      | Cat a (List2 a) -- non-infix ":" 
    deriving (Show) 

Así [1, 2, 3], que == (1 : (2 : (3 : []))), se escribiría:

a = Cat 1 (Cat 2 (Cat 3 End)) 

Al igual que el patrón de coincidencia (x:xs), podemos patrón-partido Lista2:

newTail End = End 
newTail (Cat _ x) = x 

Pruébelo:

*Main> tail [1,2,3] 
[2,3] 
*Main> newTail a 
Cat 2 (Cat 3 End) 
Cuestiones relacionadas