Я давно задавался этим вопросом, и ваш вопрос побудил меня изучить его.
Вывод: Самый простой способ сделать это вручную:
instance Read Tree where
readsPrec _ str = [(parsecRead str,"")]
Но deriving
более безопасный вариант; вышеприведенное не работает с [Tree]
и другими типами данных. Насколько я понимаю, Show
и Read
не предназначены для ручной реализации; они должны быть производными и работать с синтаксически правильными выражениями Haskell.
Похоже, причина того, что Read
не так проста, как
class Read a where
read :: String -> a
заключается в том, что существует система комбинаторов синтаксических анализаторов, похожая на Parsec, но отличная от нее, разработанная как модульная, рекурсивная и так далее. Но поскольку мы уже используем другую библиотеку комбинаторов синтаксических анализаторов, Parsec, я думаю, что лучше возиться с другой системой как можно меньше.
В документации Prelude сказано, что минимальной полной реализацией Read
является readsPrec
или readPrec
. Последнее описывается как «Предлагаемая замена readsPrec с использованием синтаксических анализаторов нового стиля (только GHC)». Для меня это пахнет неприятностями, так что давайте приступим к реализации readsPrec
.
Тип
readsPrec :: Read a => Int -> ReadS a
type ReadS a = String -> [(a,String)]
а документация для ReadS
гласит: «Синтаксический анализатор для типа a
, представленный в виде функции, которая принимает String
и возвращает список возможных анализов в виде пар (a,String)
». Для меня не совсем очевидно, что такое «разбор», но взгляните на исходный код для read
в Text.Read
раскрывает:
read :: Read a => String -> a
read s = either errorWithoutStackTrace id (readEither s)
readEither :: Read a => String -> Either String a
readEither s =
-- minPrec is defined as 0 in Text.ParserCombinators.ReadPrec
case [ x | (x,"") <- readPrec_to_S read' minPrec s ] of
[x] -> Right x
[] -> Left "Prelude.read: no parse"
_ -> Left "Prelude.read: ambiguous parse"
where
read' = -- read' :: P.ReadPrec a
do x <- readPrec
lift P.skipSpaces -- P is Text.ParserCombinators.ReadP
return x
Я попытался расширить определения readPrec_to_S
и т. д., но понял, что это того не стоит. Я думаю, определение ясно дает понять, что мы должны вернуть [(x,"")]
как успешный синтаксический анализ.
Целочисленный аргумент readsPrec
кажется «контекстом приоритета». Я предполагаю, что его безопасно игнорировать, если мы просто хотим анализировать одно дерево за раз, но игнорирование этого вызовет проблемы позже, если мы попытаемся, например, проанализировать экземпляры [Tree]
. Я проигнорирую это, потому что я не думаю, что это стоит проблем.
Короче говоря, если у нас есть parsecRead :: String -> Tree
, как определено в посте, на который вы ссылаетесь (автор назвал его read'
)
instance Read Tree where
readsPrec _ str = [(parsecRead str,"")]
Если мы проверим, как это работает в программе (используя экземпляр Show
, предоставленный исходным запросчиком):
main = do
print (read "ABC(DE)F" == example)
print ([read "ABC(DE)F", read "ABC(DE)F"] :: [Tree])
print (read "[ABC(DE)F,ABC(DE)F]" :: [Tree])
мы получаем
True
[ABC(DE)F,ABC(DE)F]
Test.hs: Prelude.read: no parse
Сложность и отсутствие документации здесь на самом деле заставляет меня думать, что deriving (Read)
на самом деле единственный безопасный вариант, если только вы не хотите углубляться в детали уровней приоритета. Кажется, я где-то читал, что Show
и Read
на самом деле главным образом предназначены для производных и что строки должны быть синтаксически правильными выражениями Haskell (пожалуйста, поправьте меня, если Я не прав). Для более общего синтаксического анализа такие библиотеки, как Parsec
, вероятно, являются лучшим вариантом.
Если у вас есть энергия, чтобы самостоятельно изучить исходный код, соответствующие модули кажутся
person
Elias Riedel Gårding
schedule
07.08.2016
readsPrec
— это простоInt -> String -> [(a, String)]
. Вы можете вернуть пустой список, чтобы обозначить ошибку чтения, или одиночный элемент[(x, "")]
, гдеx
— результат синтаксического анализа, чтобы обозначить успех. Если у вас уже есть анализаторParsec
для вашего типа или любой другой анализатор, просто запустите этот анализатор и преобразуйте его вывод в соответствующую форму. - person user2407038   schedule 07.08.2016""
выглядит неправильно: он не вернет остальную часть строки, которая не проанализирована, верно? - person awllower   schedule 08.08.2016