Haskell cadenas de bytes: Cómo modelar el partido?
-
27-09-2019 - |
Pregunta
Soy un novato Haskell, y teniendo un poco de problemas para encontrar la manera de patrón coincide con un ByteString
. La versión [Char]
de mis miradas de función como:
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, que excluye todo ocurrencias de "ab" de una cadena. Sin embargo, no tengo problemas tratando de aplicar esto a un ByteString
.
La versión ingenua
dropR :: BS.ByteString -> BS.ByteString
dropR [] = []
dropR (x:[]) = [x]
<...>
rendimientos
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 String
no regular de un ByteString
. Reemplazando en BS.empty
parece que lo correcto, pero da "Nombre calificado en la posición de encuadernado: 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 estoy tratando de hacer con esta función es filtrar un carácter específico UTF16 de algún texto. Si hay un camino limpio para lograr eso, me encantaría escucharlo, pero este patrón de error a juego parece como algo que un novato haskeller debe entender realmente.
Solución
Se puede usar vista patrones para este tipo de 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)
Otros consejos
La última versión de GHC (7.8) tiene una característica llamada sinónimos patrón que puede ser añadido 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))
El ir más allá, puede abstracta que esto funcione en cualquier tipo de clase (esto se verá mejor cuando / si conseguimos sinónimos patrón asociado ). Las definiciones de patrones permanecen igual:
{-# 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 posterior en sinónimos de patrones.
patrones de uso de constructores de datos. http://book.realworldhaskell.org/read/defining-types-streamlining -functions.html
Su empty
es sólo un enlace para el primer parámetro, que podría haber sido x
y no cambiaría nada.
No se puede hacer referencia a una función normal en su patrón de modo (x cons empty)
no es legal. Nota: supongo (cons x empty)
es realmente lo que quería 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 utilizar en los patrones.
ByteString es Data.ByteString.Internal.PS !(GHC.ForeignPtr.ForeignPtr GHC.Word.Word8) !Int !Int
(es decir, un puntero a un char nativo * + offset + longitud). Desde el constructor de datos de ByteString se oculta, debe utilizar funciones para acceder a los datos, no patrones.
A continuación, 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)
Para esto, el ajuste de patrones haría en el resultado de uncons :: ByteString -> Maybe (Word8, ByteString)
.
La concordancia de patrones en Haskell sólo funciona en constructores declaradas con 'datos' o 'newtype.' El tipo ByteString no exporta sus constructores no se puede coincidencia de patrón.
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í que el compilador espera su función a ser de tipo: BS.ByteString -> BS.ByteString
ya que le dio ese tipo en su firma. Sin embargo, inferido (mirando el cuerpo de su función) que la función es en realidad el tipo de [a] -> [a]
. Hay una falta de coincidencia no lo que el compilador se queja.
El problema es que usted está pensando (:) y [] como el azúcar sintáctica, cuando en realidad son sólo los constructores para el tipo de lista (que es muy diferente de ByteString).