2010-10-29 4 views
25

Я новичок в Haskell, и с небольшим количеством проблем выясняю, как шаблон соответствует ByteString. Версия моей функции [Char] выглядит следующим образом:Haskell Bytestrings: Как сопоставить шаблон?

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 а не ByteString. Subbing в BS.empty кажется правильным, но дает «Квалифицированное имя в позиции привязки: BS.пути». Оставляя нам попробовать

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

это дает «ошибка разбора в шаблоне» для (x cons empty). Я не знаю, что еще я могу сделать здесь.

В качестве побочного примечания, что я пытаюсь сделать с этой функцией, необходимо отфильтровать определенный символ UTF16 из некоторого текста. Если есть чистый способ добиться этого, я бы хотел его услышать, но эта ошибка совпадения с образцом кажется чем-то, что действительно должен понять новичок-хакеллер.

+0

Я не уверен, но, может быть, охранники, а не образцы? –

+1

Вы не можете отфильтровать символ UTF-16. Возможно, вы имели в виду «отфильтровать символ текста, который закодирован в UTF-16». – gawi

ответ

21

Вы можете использовать view patterns для таких вещей

{-# 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

bytestrings делают код Haskell таким уродливым; вся элегантность прелюдии String, кажется, уходит :( – mntk123

+1

@ mntk123 Строки Haskell являются списками ссылок на символы и довольно неэффективными. Они все еще существуют для обратной совместимости. Bytestring, а также пакет Text предлагают гораздо более мощные решения для одной и той же проблемы. – Jaseem

10

Устройства используют конструкторы данных. http://book.realworldhaskell.org/read/defining-types-streamlining-functions.html

Ваш empty является просто обязательным для первого параметра, это могло быть x, и это ничего не изменит.

Вы не можете ссылаться на нормальную функцию в своем шаблоне, поэтому (x cons empty) не является законным. Примечание: Я думаю, что (cons x empty) действительно то, что вы имели в виду, но это также незаконно.

ByteString весьма отличается от String. String является псевдонимом [Char], поэтому это реальный список, и оператор : может использоваться в шаблонах.

ByteString is Data.ByteString.Internal.PS !(GHC.ForeignPtr.ForeignPtr GHC.Word.Word8) !Int !Int (то есть указатель на собственный символ * + смещение + длина). Поскольку конструктор данных 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) 
+0

Не знал этого бита о шаблонах и конструкторах данных. Поскольку, как отмечено ниже, ByteString не экспортирует свои конструкторы, это имеет смысл сейчас. Спасибо всем, кто ответил. – LOS

6

Для этого я бы сопоставление с образцом на результат uncons :: ByteString -> Maybe (Word8, ByteString).

Сравнение шаблонов в Haskell работает только с конструкторами, объявленными «данными» или «новым типом». Тип ByteString не экспортирует свои конструкторы, которые не могут совпадать с шаблоном.

2

Просто адресовать сообщение об ошибке вы получили и что это значит:

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).

7

Последняя версия 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)) 

Двигаясь дальше вы можете абстрактное это работать на любом классе типа (это будет выглядеть лучше когда/если мы получим associated pattern synonyms). Определения модели остаются теми же:

{-# 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)) 

Вы можете увидеть больше на моем post на синонимах рисунков.

Смежные вопросы