2010-10-29 16 views
25

Soy un novato Haskell, y tengo algunos problemas para encontrar la forma de un patrón que coincida con ByteString. La versión [Char] de mi función será similar a:Bytestrings Haskell: ¿Cómo se empareja el patrón?

dropAB :: String -> String 
dropAB []  = [] 
dropAB (x:[]) = x:[] 
dropAB (x:y:xs) = if x=='a' && y=='b' 
        then dropAB xs 
        else x:(dropAB $ y:xs) 

Como era de esperar, este filtra todas las ocurrencias de "ab" de una cadena. Sin embargo, tengo problemas para intentar aplicar esto a un ByteString.

La versión ingenua

dropR :: BS.ByteString -> BS.ByteString 
dropR []   = [] 
dropR (x:[])  = [x] 
<...> 

produce

Couldn't match expected type `BS.ByteString' 
     against inferred type `[a]' 
In the pattern: [] 
In the definition of `dropR': dropR [] = [] 

[] es claramente el culpable, como lo es para un habitual String no un ByteString. Subbing en BS.empty parece lo correcto, pero da "Nombre calificado en la posición de enlace: BS.empty". dejándonos a tratar

dropR :: BS.ByteString -> BS.ByteString 
dropR empty    = empty   
dropR (x cons empty)  = x cons empty 
<...> 

esto da "Error de análisis en el patrón" para (x cons empty). Realmente no sé qué más puedo hacer aquí.

Como nota al margen, lo que intento hacer con esta función es filtrar un carácter UTF16 específico de algún texto. Si hay una manera limpia de lograr eso, me encantaría escucharlo, pero este error de coincidencia de patrones parece algo que un novato debe entender realmente.

+0

No estoy seguro, pero tal vez guardias en lugar de coincidencia de patrones? –

+1

No se puede filtrar un carácter UTF-16. Quizás quisiste decir "filtrar un carácter de un texto codificado en UTF-16". – gawi

Respuesta

21

Puede utilizar view patterns para tales cosas

{-# LANGUAGE ViewPatterns #-}  
import Data.ByteString (ByteString, cons, uncons, singleton, empty) 
import Data.ByteString.Internal (c2w) 

dropR :: ByteString -> ByteString 
dropR (uncons -> Nothing) = empty 
dropR (uncons -> Just (x,uncons -> Nothing)) = singleton x 
dropR (uncons -> Just (x,uncons -> Just(y,xs))) = 
    if x == c2w 'a' && y == c2w 'b' 
    then dropR xs 
    else cons x (dropR $ cons y xs) 
+2

cadenas de bytes hacen que el código de Haskell se vea tan feo; toda la elegancia de Prelude String parece desaparecer :( – mntk123

+1

@ mntk123 Las cadenas de Haskell son listas de enlaces de caracteres y bastante ineficientes. Todavía existen para la compatibilidad con versiones anteriores. Bytestring y Text ofrecen soluciones mucho más potentes para el mismo problema. – Jaseem

10

Los patrones usan constructores de datos. http://book.realworldhaskell.org/read/defining-types-streamlining-functions.html

Su empty es solo un enlace para el primer parámetro, podría haber sido x y no cambiaría nada.

No se puede hacer referencia a una función normal en su patrón por lo que (x cons empty) no es legal. Nota: Supongo que (cons x empty) es realmente lo que usted quiso decir, pero esto también es ilegal.

ByteString es bastante diferente de String. String es un alias de [Char], por lo que es una lista real y el operador : se puede usar en patrones.

ByteString es Data.ByteString.Internal.PS !(GHC.ForeignPtr.ForeignPtr GHC.Word.Word8) !Int !Int (es decir, un puntero a un char * nativo + desplazamiento + longitud). Como el constructor de datos de ByteString está oculto, debe usar funciones para acceder a los datos, no a los patrones.


Aquí una solución (sin duda no es el mejor) a su problema filtro de UTF-16, utilizando el paquete text:

module Test where 

import Data.ByteString as BS 
import Data.Text as T 
import Data.Text.IO as TIO 
import Data.Text.Encoding 

removeAll :: Char -> Text -> Text 
removeAll c t = T.filter (/= c) t 

main = do 
    bytes <- BS.readFile "test.txt" 
    TIO.putStr $ removeAll 'c' (decodeUtf16LE bytes) 
+0

No sabía nada sobre patrones y constructores de datos. Como, como se indica a continuación, ByteString no exporta sus constructores, esto tiene sentido ahora. Gracias a todos los que respondieron. – LOS

6

Para esto, el ajuste de patrones haría en el resultado de uncons :: ByteString -> Maybe (Word8, ByteString).

La coincidencia de patrones en Haskell solo funciona en constructores declarados con 'data' o 'newtype.' El tipo ByteString no exporta sus constructores, no puede coincidir con el patrón.

2

Sólo para dirigir el mensaje de error recibido y lo que significa:

Couldn't match expected type `BS.ByteString' 
     against inferred type `[a]' 
In the pattern: [] 
In the definition of `dropR': dropR [] = [] 

Así el compilador espera su función para ser de tipo : BS.ByteString -> BS.ByteString porque le dio ese tipo en su firma. Sin embargo, infiere (mirando el cuerpo de su función) que la función es en realidad del tipo [a] -> [a]. Hay una falta de coincidencia allí, por lo que el compilador se queja.

El problema es que está pensando en (:) y [] como azúcar sintáctica, cuando en realidad son solo los constructores para el tipo de lista (que es MUY diferente de ByteString).

7

La última versión de GHC (7.8) tiene una característica llamada sinónimos de patrones que se pueden añadir al ejemplo de gawi:

{-# LANGUAGE ViewPatterns, PatternSynonyms #-} 

import Data.ByteString (ByteString, cons, uncons, singleton, empty) 
import Data.ByteString.Internal (c2w) 

infixr 5 :< 

pattern b :< bs <- (uncons -> Just (b, bs)) 
pattern Empty <- (uncons -> Nothing) 

dropR :: ByteString -> ByteString 
dropR Empty   = empty 
dropR (x :< Empty) = singleton x 
dropR (x :< y :< xs) 
    | x == c2w 'a' && y == c2w 'b' = dropR xs 
    | otherwise     = cons x (dropR (cons y xs)) 

Yendo más allá, puede abstracta que esto funcione en cualquier tipo de clase (esto se verá más bonito cuando/si obtenemos associated pattern synonyms). Las definiciones de patrones siguen siendo los mismos:

{-# LANGUAGE ViewPatterns, PatternSynonyms, TypeFamilies #-} 

import qualified Data.ByteString as BS 
import Data.ByteString (ByteString, singleton) 
import Data.ByteString.Internal (c2w) 
import Data.Word 

class ListLike l where 
    type Elem l 

    empty :: l 
    uncons :: l -> Maybe (Elem l, l) 
    cons :: Elem l -> l -> l 

instance ListLike ByteString where 
    type Elem ByteString = Word8 

    empty = BS.empty 
    uncons = BS.uncons 
    cons = BS.cons 

instance ListLike [a] where 
    type Elem [a] = a 

    empty   = [] 
    uncons []  = Nothing 
    uncons (x:xs) = Just (x, xs) 
    cons   = (:) 

en cuyo caso dropR puede trabajar en ambos [Word8] y ByteString:

-- dropR :: [Word8] -> [Word8] 
-- dropR :: ByteString -> ByteString 
dropR :: (ListLike l, Elem l ~ Word8) => l -> l 
dropR Empty   = empty 
dropR (x :< Empty) = cons x empty 
dropR (x :< y :< xs) 
    | x == c2w 'a' && y == c2w 'b' = dropR xs 
    | otherwise     = cons x (dropR (cons y xs)) 

Y para el placer de hacerlo:

import Data.ByteString.Internal (w2c) 

infixr 5 :•  
pattern b :• bs <- (w2c -> b) :< bs 

dropR :: (ListLike l, Elem l ~ Word8) => l -> l 
dropR Empty    = empty 
dropR (x :< Empty)  = cons x empty 
dropR ('a' :• 'b' :• xs) = dropR xs 
dropR (x :< y :< xs) = cons x (dropR (cons y xs)) 

Se puede ver más en mi post en sinónimos de patrones.

Cuestiones relacionadas