Haskell Bytestrings: Как сопоставить шаблон?
Я новичок Haskell и немного разобрался в том, как сопоставить шаблон с ByteString
. Версия моей функции [Char]
выглядит так:
dropAB :: String -> String
dropAB [] = []
dropAB (x:[]) = x:[]
dropAB (x:y:xs) = if x=='a' && y=='b'
then dropAB xs
else x:(dropAB $ y:xs)
Как и ожидалось, это отфильтровывает все вхождения "ab" из строки. Однако у меня возникают проблемы с попыткой применить это к ByteString
.
Наивная версия
dropR :: BS.ByteString -> BS.ByteString
dropR [] = []
dropR (x:[]) = [x]
<...>
дает
Couldn't match expected type `BS.ByteString'
against inferred type `[a]'
In the pattern: []
In the definition of `dropR': dropR [] = []
[]
явно является виновником, как и для регулярного String
не a ByteString
. Subbing в BS.empty
кажется правильным, но дает "Квалифицированное имя в позиции привязки: BS.empty". Оставив нас попробовать
dropR :: BS.ByteString -> BS.ByteString
dropR empty = empty
dropR (x cons empty) = x cons empty
<...>
это дает "ошибку разбора в шаблоне" для (x cons empty)
. Я не знаю, что еще я могу сделать здесь.
В качестве побочного примечания, что я пытаюсь сделать с этой функцией, нужно отфильтровать определенный символ UTF16 из некоторого текста. Если есть чистый способ добиться этого, я бы с удовольствием это услышал, но эта ошибка совпадения с образцом кажется чем-то, что действительно должен понять новичок-хакеллер.
Ответы
Ответ 1
Вы можете использовать просматривать шаблоны для таких вещей
{-# 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
В шаблонах используются конструкторы данных. http://book.realworldhaskell.org/read/defining-types-streamlining-functions.html
Ваш empty
является просто привязкой для первого параметра, он мог бы быть x
, и он ничего не изменит.
Вы не можете ссылаться на нормальную функцию в вашем шаблоне, поэтому (x cons empty)
не является законным. Примечание. Я полагаю, что (cons x empty)
действительно то, что вы имели в виду, но это также незаконно.
ByteString
сильно отличается от String
. String
является псевдонимом [Char]
, поэтому в шаблонах можно использовать реальный список и оператор :
.
ByteString Data.ByteString.Internal.PS !(GHC.ForeignPtr.ForeignPtr GHC.Word.Word8) !Int !Int
(т.е. указатель на нативную длину char * + смещение +). Поскольку конструктор данных ByteString скрыт, вы должны использовать функции для доступа к данным, а не шаблонам.
Здесь решение (конечно, не самое лучшее) для вашей проблемы с фильтром UTF-16 с помощью пакета 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)
Ответ 3
В последней версии GHC (7.8) есть функция, называемая синонимами шаблонов, которые можно добавить к примеру 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))
Идем дальше, вы можете абстрагировать это, чтобы работать над любым типом класса (это будет выглядеть лучше, если/если мы получим связанные синонимы шаблонов). Определения шаблонов остаются неизменными:
{-# 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 = (:)
и в этом случае dropR
может работать как на [Word8]
, так и на 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))
И черт возьми:
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))
Вы можете увидеть больше на
Ответ 4
Для этого я бы совпадал с шаблоном результата uncons :: ByteString -> Maybe (Word8, ByteString)
.
Совпадение шаблонов в Haskell работает только с конструкторами, объявленными "данными" или "newtype". Тип ByteString не экспортирует свои конструкторы, которые невозможно сопоставить с образцом.
Ответ 5
Просто чтобы ответить на полученное сообщение об ошибке и что это значит:
Couldn't match expected type `BS.ByteString'
against inferred type `[a]'
In the pattern: []
In the definition of `dropR': dropR [] = []
Итак, компилятор ожидал, что ваша функция будет иметь тип: BS.ByteString -> BS.ByteString
, потому что вы дали ему этот тип в своей подписи. Тем не менее он вывел (посмотрев на тело вашей функции), что функция действительно имеет тип [a] -> [a]
. Там есть несоответствие, поэтому компилятор жалуется.
Проблема в том, что вы думаете (:) и [] как синтаксический сахар, когда они на самом деле являются просто конструкторами для типа списка (который ОЧЕНЬ отличается от ByteString).