Реализация класса типов чтения, где при синтаксическом анализе строк используется "$" - PullRequest
6 голосов
/ 16 сентября 2011

Я играл с Haskell около месяца.Для моего первого "настоящего" проекта на Haskell я пишу тегер частей речи.В рамках этого проекта у меня есть тип с именем Tag, который представляет тег части речи, реализованный следующим образом:

data Tag = CC | CD | DT | EX | FW | IN | JJ | JJR | JJS ...

Выше приведен длинный список стандартизированных тегов частей речикоторый я намеренно усек.Однако в этом стандартном наборе тегов есть два, заканчивающиеся знаком доллара ($): PRP $ и NNP $.Поскольку у меня не может быть конструкторов типов с $ в их имени, я решил переименовать их в PRPS и NNPS.

Это все хорошо, но я хотел бы читать теги из строк влексикон и преобразовать их в мой Tag тип.Попытка этого не удалась:

instance Read Tag where
    readsPrec _ input =
        (\inp -> [((NNPS), rest) | ("NNP$", rest) <- lex inp]) input

Лексер Haskell душит $.Любые идеи, как это осуществить?

Реализация Show была довольно простой.Было бы здорово, если бы была какая-то похожая стратегия для чтения.

instance Show Tag where
    showsPrec _ NNPS = showString "NNP$"
    showsPrec _ PRPS = showString "PRP$"
    showsPrec _ tag  = shows tag

Ответы [ 2 ]

5 голосов
/ 16 сентября 2011

Вы злоупотребляете Read здесь.

Show и Read предназначены для печати и анализа допустимых значений Haskell, для включения отладки и т. Д. Это не всегда идеально (например, есливы импортируете Data.Map квалифицированный и затем вызываете show для значения Map, вызов fromList не квалифицирован), но это допустимая начальная точка.

Если вы хотите распечатать или проанализироватьВаши значения должны соответствовать определенному формату, а затем использовать симпатичную библиотеку для печати для первого и реальную библиотеку для анализа (например, uu-parsinglib, polyparse, parsec и т. д.) для второго.Как правило, они имеют гораздо более приятную поддержку для анализа, чем ReadS (хотя ReadP в GHC не слишком плохо).

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

4 голосов
/ 16 сентября 2011

Тогда не используйте лексер Haskell.read функции используют ParSec, который вы можете найти отличное введение в книгу о реальном мире Haskell.

Вот код, который, кажется, работает,

import Text.Read
import Text.ParserCombinators.ReadP hiding (choice)
import Text.ParserCombinators.ReadPrec hiding (choice)

data Tag = CC | CD | DT | EX | FW | IN | JJ | JJR | JJS deriving (Show)

strValMap = map (\(x, y) -> lift $ string x >> return y)

instance Read Tag where
    readPrec = choice $ strValMap [
        ("CC", CC),
        ("CD", CD),
        ("JJ$", JJS)
        ]

просто запустить его

(read "JJ$") :: Tag

Код довольно понятен.Монада синтаксического анализатора string x соответствует x, и, если она выполнена успешно (исключение не выдается), возвращается y.Мы используем choice, чтобы выбрать среди всего этого.Он будет возвращен соответствующим образом, поэтому, если вы добавите конструктор CCC, то CC, частично совпадающий с "CCC", позже потерпит неудачу и вернется к CCC.Конечно, если вам это не нужно, используйте комбинатор <|>.

...