200 likes | 278 Views
Compilaci ó n de Pattern Matching. Un ejemplo: zipWith f [] ys = [] zipWith f (x:xs) [] = [] zipWith f (x:xs) (y:ys) = f x y : zipWith f xs ys Semántica de pattern matching Se asume: (1) testea cada ecuación por un match top to bottom
E N D
Compilación de Pattern Matching Un ejemplo: zipWith f [] ys = [] zipWith f (x:xs) [] = [] zipWith f (x:xs) (y:ys) = f x y : zipWith f xs ys Semántica de pattern matching Se asume: (1) testea cada ecuación por un match top to bottom (2) Para cada ecuación, testea cada pattern por un match de izquierda a derecha, si falla termina con esa ecuación y testea la siguiente. Traduccion en expresiones case simples: una secuencia eficiente de tests, testeando cada valor sólo una vez
Expresiones case simples zipWith = \ f xs' ys -> case xs' of Nil -> Nil Cons x xs -> case ys' of Nil -> Nil Cons y ys -> Cons (f x y) (zipWith f xs ys) case e of C1 v11 ... v1k1 -> e1 ... Cn vn1 ... vnkn -> en v -> e . Sub-patterns son simple variables (vij's). . default pattern v -> e es opcional.
Traducción f p11 ...p1n = e1 ... f pm1 ...p mn = em f = \ u1 ...un -> ( (( \ (p11’ ,..., p1n’) -> e1’) u1...un ) ... (( \ (pm1’ ,..., pmn’) -> em’) u1...un ) ERROR ) Nuestro objetivo entonces es transformar una expresión de esta forma en una expresión equivalente definida en función de expresiones case.
La función match match [ u 1 , ..., u n ] [([ p11 , ..., p1n ] , [e1]) , ([ pm1 , ..., pmn ] , [em]) ] [ error "no match in f"] match us qs e, donde us: una lista de variables a ser testeadas por un match, qs: una lista de ecuaciones, e: expresion por defecto, si ninguna ecuacion en qs machea entonces e es el valor retornado
Un ejemplo de corrida: demo demo f [] ys = aa f ys demo f (x:xs) [] = bb f x xs demo f (x:xs) (y:ys) = cc f x xs y ys demo = \ u1 u2 u3 -> match [ u1 , u2 , u 3] [( [f , Nil , ys ] , [ aa f ys ]) , ( [f , Cons x xs , Nil ] , [ bb f x xs]) , ( [f , Cons x xs ,Cons y ys ] ,[cc f x xs y ys ]) ] [ error "no match in demo"]
La regla de la Variable demo = \ u1 u2 u3 -> match [ u1 , u2 , u3] [( [f , Nil , ys ] ,[ aa f ys ]) , ( [f , Cons x xs ,Nil ] ,[ bb f x xs]) , ( [f , Cons x xs ,Cons y ys ] ,[cc f x xs y ys ]) ] [ error "no match in demo"] Todos los primer patterns son variables demo = \ u1 u2 u3 -> match [u2 , u3] [ ( Nil , ys ] , [ aa f ys ]) , ( Cons x xs , Nil ] , [ bb f x xs]) , ( Cons x xs , Cons y ys ] , [cc f x xs y ys ]) ] [ error "no match in demo"] Nota: Las variables no necesitan ser las mismas
Regla 2: la regla Constructor Todos los primer patterns son constructores ejemplo: en un data type que incluye constructores A, B y C. match ([ u ] : us ) [([ A psa1 ]: ps'a1, ea1 ), ([ B psb1 ]: ps'b1, eb1 ), ([ A psa2 ]: ps'a2, ea2 ), ([ B psb2 ]: ps'b2, eb2 ), ([ C psc1 ]: ps'c1, ec1 ), ([ A psa3 ]: ps'a3, ea3 ) ] d =>
Regla 2 (cont.) case u of A us'a -> match ( us'a ++ us ) [ ( psa1 ++ ps'a1 , ea1 ), ( psa2 ++ ps'a2 , ea2 ), ( psa3 ++ ps'a3 , ea3 )] d B us'b -> match ( us'b ++ us ) [ ( psb1 ++ ps'b1 , eb1 ), ( psb2 ++ ps'b2 , eb2 )] d C us'c -> match ( us'c ++ us ) [ ( psc1 ++ ps'c1 , ec1 )] d u' -> d
Una nota sobre el orden de ecuaciones match ([ u ] : us ) [([ A psa1 ]: ps'a1, ea1 ), ([ B psb1 ]: ps'b1, eb1 ), ([ A psa2 ]: ps'a2, ea2 ), ([ B psb2 ]: ps'b2, eb2 ), ([ C psc1 ]: ps'c1, ec1 ), ([ A psa3 ]: ps'a3, ea3 ) ] d => match ([ u ] : us ) [([ A psa1 ]: ps'a1, ea1 ), ([ A psa2 ]: ps'a2, ea2 ), ([ A psa3 ]: ps'a3, ea3 ) ([ B psb1 ]: ps'b1, eb1 ), ([ B psb2 ]: ps'b2, eb2 ), ([ C psc1 ]: ps'c1, ec1 ), ] d Es siempre seguro permutar ecuaciones adyacentes con diferentes constructores. Entonces, en general se podría ordenar la lista con clave el constructor usando un sort estable.
Usando la regla 2 Los primeros patterns son constructores demo = \ u1 u2 u3 -> case u2 of Nil -> match [u3] [( [ ys ] , [ aa u1 ys]) ] [ error "no match in demo"] Cons u4 u5 -> match [u4, u5, u3] [ ( [ x xs , Nil ] , [ bb u1 x xs]) , ( [ x xs , Cons y ys ] , [ cc u1 x xs y ys ]) ] [ error "no match in demo"] v -> [ error "no match in demo"]
Regla 2 (cont.) demo = \ u1 u2 u3 -> case u2 of Nil -> match [u3] [( [ ys ] , [ aa u1 ys]) ] [ error "no match in demo"] Cons u4 u5 -> match [u4, u5, u3] [ ( [ x xs , Nil ] , [ bb u1 x xs]) , ( [ x xs , Cons y ys ] , [ cc u1 x xs y ys ]) ] [ error "no match in demo"] v -> [ error "no match in demo"] Aplicamos la regla de variable 3 veces
Regla 2 (cont.) demo = \ u1 u2 u3 -> case u2 of Nil -> match [ ] [( [ ] , [ aa u1 u3]) ] [ error "no match in demo"] Cons u4 u5 -> match [ u3 ] [ ( [ Nil ] , [ bb u1 u4 u5]) , ( [ Cons y ys ] , [ cc u1 u4 u5 y ys ]) ] [ error "no match in demo"] v -> [ error "no match in demo"] Regla 3: la regla Vacía match [] [([] , e1 )] ... [([] , em )] d => e1 … … em… d
demo (cont.) demo = \ u1 u2 u3 -> case u2 of Nil -> aa u1 u3 Cons u4 u5 -> case u3 of Nil -> match [ ] [([ ] , [ bb u1 u4 u5])] [ error "no match in demo"] Cons u6 u7 -> match [ ] [([ ] , [ cc u1 u4 u5 u6 u7])] [ error "no match in demo"] v -> [ error "no match in demo"] v -> [ error "no match in demo"]
... y finalmente demo = \ u1 u2 u3 -> case u2 of Nil -> aa u1 u3 Cons u4 u5 -> case u3 of Nil -> bb u1 u4 u5 Cons u6 u7 -> cc u1 u4 u5 u6 u7 v -> [ error "no match in demo"] v -> [ error "no match in demo"] Si todos los constructores de un data type están presentes en el case, entonces el caso por defecto puede ser eliminado.
demo’: otra versión de demo demo' f [] ys = aa f ys demo' f xs [] = bb f x xs demo' f (x:xs) (y:ys) = cc f x xs y ys => demo' = \ u1 u2 u3 -> match [ u1 , u2 , u3] [ ( [ f , Nil , ys ] , [ aa f ys ]) , ( [ f , xs , Nil ] , [ bb f x xs]) , ( [ f , Cons x xs , Cons y ys ] , [cc f x xs y ys ]) ] [ error "no match in demo"]
demo’ (2) demo' = \ u1 u2 u3 -> match [ u2 , u3] [ ( [ Nil , ys ] , [ aa u1 ys ]) , ( [ xs , Nil ] , [ bb u1 x xs]) , ( [ Cons x xs ,Cons y ys ] , [cc u1 x xs y ys ]) ] [ error "no match in demo"] Los primer patterns no son enteramente variables ni enteramente constructores.
Regla 4: la regla Mixta match us qs d donde los primer patterns en qs no son enteramente variables ni enteramente constructores. Efectuamos una partición deqsen k listas qs = qs1 ++ qs2 ++ ... ++ qsk tal que los primer patterns de cada qsison todos variables o todos constructores. Entoncesmatch us qs d= match us qs1 (match us qs2 (...(match us qsk d)...)) Nota: la regla mixta siempre puede ser aplicada, pero su uso es sólo necesario en el caso de primer patterns mezclados.
demo’ (cont.) demo' = \ u1 u2 u3 -> match [ u2 , u3] [ ( [ Nil , ys ] , [ aa u1 ys ]) ] ( match [ u2 , u3] [ ( [ xs , Nil ] , [ bb u1 x xs])] ( match [ u2 , u3] [ ( [ Cons x xs ,Cons y ys ] , [cc u1 x xs y ys ]) ] [ error "no match in demo"] )) Deficiencias: La regla mixta fácilmente causa que la misma variable sea examinada más de una vez. Esto puedeser optimizado en una pasada posterior.
Completitud • Ahora es posible reducir toda posible llamada a match a una expresión case. Podemos razonar haciendo un simple análisis de casos. • Dada una aplicación (match us qs e) : • si us es [] entonces aplicamos la regla Vacía • sino, entonces cada ecuación debe contener una lista no vacía de patrones, la que debe empezar o con una variable o con un constructor. • si todos son variables => se aplica la regla Variable • si todos son constructores => se aplica la regla Constructor • sino => se aplica la regla Mixta
Terminación data Pattern = Var Vars | Cons Const [Pattern] type Vars = String type Const = String type Eq = ([ Pattern ] , Exp) type Eqs = [Eq] sizeeqs :: Eqs -> Int Se puede demostrar fácilmente que para las cuatro reglas que definimos las llamadas recursivas a match son efectuadas sobre listas de ecuaciones de menor tamaño. Esto garantiza que el algoritmo termina.