2015-10-07 1 views
-1

я реализовал эти (упрощенный) типы данных в программе Haskell:Read экземпляр комбинированного типа данных с Haskell

data Type = ValA 
      | Valb 
      | ValC 

data Prefix = PrefA 
      | PrefB 
      | PrefC 
      | NoPref 

data Combin = Combin Prefix Type 

instance Show Type where 
    show ValA = "typeA" 
    show Valb = "O" 
    show ValC = "mp" 

instance Read Type where 
    readsPrec _ "typeA" = [(ValA,"")] 
    readsPrec _ "O"  = [(Valb,"")] 
    readsPrec _ "mp" = [(ValC,"")] 

instance Show Prefix where 
    show PrefA = "a" 
    show PrefB = "bm" 
    show PrefC = "c" 
    show NoPref = "" 

instance Read Prefix where 
    readsPrec _ "a" = [(PrefA,"")] 
    readsPrec _ "bm" = [(PrefB,"")] 
    readsPrec _ "c" = [(PrefC,"")] 
    readsPrec _ "" = [(NoPref,"")] 

instance Show Combin where 
    show (Combin pre typ) = show pre++show typ 

с экземплярами, я могу show и read типов Prefix и Type. Тип данных Combin представляет собой конкатенацию Prefix и Type. Теперь я хотел бы реализовать экземпляр чтения для типа данных Combin, и я понятия не имею, как это сделать.

Я думал о выводе типа Combin, но это приводит к тому, что выходная строка Combin PrefA ValC будет «Combin mp». И это не то, что я хочу. Я хочу, чтобы «усилитель» объединился. То же самое, что и для чтения

Я думал о создании шаблона, соответствующего строке ввода, но строки Prefix имеют разную длину и могут быть недействительными (NoPref).

Вы когда-нибудь реализовывали такую ​​функциональную функцию с чтением? Вы знаете, как это сделать?

+1

Почему бы просто не использовать 'Выводя (Читать, Показать) '? Таким образом, вам не нужно писать ни один из этих экземпляров. – bheklilr

+0

Поскольку вывод 'Combin' вызывает вывод строки« Combin PrefA ValC »как« Combin a mp ».И это не то, что я хочу. Я хочу, чтобы «усилитель» объединился. То же самое для чтения. – JeanJouX

ответ

1

Неверные реализации readsPrec, так как они должны принимать действительные префиксы ввода, не обязательно потреблять весь ввод. Ваши функции readsPrec не являются составными.

Ключ к решению, чтобы переписать их так, что они проверяют, если их ввод совпадает с любым из имен:

import Data.List (stripPrefix) 

instance Read Type where 
    readsPrec _ s | Just s' <- stripPrefix "typeA" s = [(ValA, s')] 
        | Just s' <- stripPrefix "O" s  = [(Valb, s')] 
        | Just s' <- stripPrefix "mp" s = [(ValC, s')] 
        | otherwise = [] 

и аналогично для Prefix:

instance Read Prefix where 
    readsPrec _ s | Just s' <- stripPrefix "a" s = [(PrefA, s')] 
        | Just s' <- stripPrefix "bm" s = [(PrefB, s')] 
        | Just s' <- stripPrefix "c" s = [(PrefC, s')] 
        | otherwise = [(NoPref, s)] 

Примечание последняя ветвь readsPrec для Prefix, в котором говорится, что каждая строка, которая не начинается с "a", "bm" или "c", обрабатывается как NoPref. Это работает только для Combin, потому что нет Type, который начинается с любого из разрядов Prefix; в противном случае, синтаксический анализатор для Prefix необходимо будет nondeterminstic, так как строка, как "aXY" может либо соответствовать Prefix из "a" и Type из "XY" или Prefix из NoPref и Type из "aXY". Я пошел с детерминистически нетерпеливой версией здесь, потому что я думаю, вам будет легче понять, как это работает.

После того, как у нас есть эти два Read экземпляров, пишешь ghte один для Combin является простым делом, пытаясь прочитать всю строку с reads для Prefix, а затем пытается прочитать каждый остаток как Type:

instance Read Combin where 
    readsPrec _ s = [(Combin pre typ, s'') | (pre, s') <- reads s, (typ, s'') <- reads s'] 

Если вы знакомы с do нотации и [] монады, вы можете переписать это как

instance Read Combin where 
    readsPrec _ s = do 
     (pre, s) <- reads s 
     (typ, s) <- reads s 
     return (Combin pre typ, s) 
+0

Отличный ответ! Он работает так, как я хочу. – JeanJouX