2015-04-27 4 views
1

Мне нужно написать программу, которая декодирует список из четырех значений, который может быть либо I, либо O в список [Either Bool Bool]. Я знаю, что я должен использовать, может быть, но я просто не могу обвести вокруг себя голову. Сейчас я полностью отчаянный, потому что я просто не могу решить эту проблему.Haskell beginner не может соответствовать ожидаемому типу

Пример ввода и вывода может выглядеть следующим образом: [I, O, O, I] => [Left Правда, правый Ложные]

Вот текущий код у меня есть:

module Blueprint where 
import Prelude 
import Data.Maybe 

data Bit = O | I deriving (Eq, Show) 

encode :: [Either Bool Bool] -> [Bit] 
encode [] = [] 
encode l = case head l of 
      Left False -> [I, I] ++ encode (tail l) 
      Left True -> [I, O] ++ encode (tail l) 
      Right False -> [O, I] ++ encode (tail l) 
      Right True -> [O, O] ++ encode (tail l) 

decode :: [Bit] -> Maybe [Either Bool Bool] 
decode [] = Nothing 
decode [x] = Nothing 
decode l = if isNothing (decode (tail (tail l))) 
    then Nothing 
    else case head l of 
    I -> if l!!1 == I 
      then [Left False] ++ decode (tail (tail l)) 
      else [Left True] ++ decode (tail (tail l)) 
    O -> if l!!1 == I 
      then [Right False] ++ decode (tail (tail l)) 
      else [Right True] ++ decode (tail (tail l)) 

и эти ошибки я получаю:

Prelude> :load serialise 
[1 of 1] Compiling Blueprint  (serialise.hs, interpreted) 

serialise.hs:22:16: 
    Couldn't match expected type `Maybe [Either Bool Bool]' 
       with actual type `[Either Bool b0]' 
    In the expression: [Left False] ++ decode (tail (tail l)) 
    In the expression: 
     if l !! 1 == I then 
      [Left False] ++ decode (tail (tail l)) 
     else 
      [Left True] ++ decode (tail (tail l)) 
    In a case alternative: 
     I -> if l !! 1 == I then 
       [Left False] ++ decode (tail (tail l)) 
      else 
       [Left True] ++ decode (tail (tail l)) 

serialise.hs:22:33: 
    Couldn't match expected type `[Either Bool b0]' 
       with actual type `Maybe [Either Bool Bool]' 
    In the second argument of `(++)', namely `decode (tail (tail l))' 
    In the expression: [Left False] ++ decode (tail (tail l)) 

serialise.hs:23:16: 
    Couldn't match expected type `Maybe [Either Bool Bool]' 
       with actual type `[Either Bool b1]' 
    In the expression: [Left True] ++ decode (tail (tail l)) 
    In the expression: 
     if l !! 1 == I then 
      [Left False] ++ decode (tail (tail l)) 
     else 
      [Left True] ++ decode (tail (tail l)) 
    In a case alternative: 
     I -> if l !! 1 == I then 
       [Left False] ++ decode (tail (tail l)) 
      else 
       [Left True] ++ decode (tail (tail l)) 

serialise.hs:23:32: 
    Couldn't match expected type `[Either Bool b1]' 
       with actual type `Maybe [Either Bool Bool]' 
    In the second argument of `(++)', namely `decode (tail (tail l))' 
    In the expression: [Left True] ++ decode (tail (tail l)) 

serialise.hs:25:16: 
    Couldn't match expected type `Maybe [Either Bool Bool]' 
       with actual type `[Either a0 Bool]' 
    In the expression: [Right False] ++ decode (tail (tail l)) 
    In the expression: 
     if l !! 1 == I then 
      [Right False] ++ decode (tail (tail l)) 
     else 
      [Right True] ++ decode (tail (tail l)) 
    In a case alternative: 
     O -> if l !! 1 == I then 
       [Right False] ++ decode (tail (tail l)) 
      else 
       [Right True] ++ decode (tail (tail l)) 

serialise.hs:25:34: 
    Couldn't match expected type `[Either a0 Bool]' 
       with actual type `Maybe [Either Bool Bool]' 
    In the second argument of `(++)', namely `decode (tail (tail l))' 
    In the expression: [Right False] ++ decode (tail (tail l)) 

serialise.hs:26:16: 
    Couldn't match expected type `Maybe [Either Bool Bool]' 
       with actual type `[Either a1 Bool]' 
    In the expression: [Right True] ++ decode (tail (tail l)) 
    In the expression: 
     if l !! 1 == I then 
      [Right False] ++ decode (tail (tail l)) 
     else 
      [Right True] ++ decode (tail (tail l)) 
    In a case alternative: 
     O -> if l !! 1 == I then 
       [Right False] ++ decode (tail (tail l)) 
      else 
       [Right True] ++ decode (tail (tail l)) 

serialise.hs:26:32: 
    Couldn't match expected type `[Either a1 Bool]' 
       with actual type `Maybe [Either Bool Bool]' 
    In the second argument of `(++)', namely `decode (tail (tail l))' 
    In the expression: [Right True] ++ decode (tail (tail l)) 
Failed, modules loaded: none. 

Сейчас ничего, что помогает мне решить эту проблему можно только приветствовать. Я пробовал это в течение большей части дня, и я просто не могу его решить.

ответ

5

Вам нужно поставить Just перед вашим case заявление, чтобы преобразовать эти значения в Maybe [Either Bool Bool] вместо просто [Either Bool Bool]:

decode :: [Bit] -> Maybe [Either Bool Bool] 
decode [] = Nothing 
decode [x] = Nothing 
decode l = if isNothing (decode (tail (tail l))) 
    then Nothing 
    else Just $ case head l of 
    I -> if l!!1 == I 
      then [Left False] ++ decode (tail (tail l)) 
      else [Left True] ++ decode (tail (tail l)) 
    O -> if l!!1 == I 
      then [Right False] ++ decode (tail (tail l)) 
      else [Right True] ++ decode (tail (tail l)) 

Но это не решит все. У вас также есть decode, встроенный в это вычисление, и его тип - Maybe [Either Bool Bool], но для использования ++ вам нужен только [Either Bool Bool], так как ++ работает только со списками. Это где Maybe монада пригождается:

decode [] = Nothing 
decode [x] = Nothing 
decode (x:y:rest) = do 
    end <- decode rest 
    let this = case (x, y) of 
      (I, I) -> Left False 
      (I, O) -> Left True 
      (O, I) -> Right False 
      (O, O) -> Right True 
    return (this : end) 

Дела заявление здесь действительно просто другой способ написания тематического/вложенное сослагательного наклонения. Я также использовал сопоставление шаблонов, чтобы избежать всех видов использования head, !! и tail, так как эти функции обычно следует избегать (они вместо этого выполняют error, вместо того, чтобы делать правильную обработку ошибок с использованием Maybe). Синтаксис <- для Maybe будет выходить из вычисления раньше, если возвращается Nothing, поэтому end <- decode rest - это то же самое, что и if isNothing (decode rest) then Nothing else <continue>. Затем вместо того, чтобы строить весь результат в каждой ветви case/ifs, просто вычислите единственный результат из x и y, а затем создайте список результатов в конце.

+0

Также обратите внимание, что новая версия использует только общие методы, что делает ее более безопасной. Учет соответствия шаблону 'x/y' позволяет избежать того, можно ли безопасно выполнять' head' или '!! 1'. Несмотря на то, что после анализа это безопасно, полезно избегать анализа. – Guvante

+0

@Ruhrpottpatriot: Понимание монады «Может быть» требует понимания монадов. В этом случае преобразование простое, вы можете удалить 'return' /' do' и заменить 'end <- decode rest' на предоставленный фрагмент, и вы будете иметь идентичное выполнение. Я всегда думаю о монаде «Maybe» как «если' <-' возвращает 'Nothing' вы в конечном итоге с' Nothing' и 'return' является синонимом' Just' ' – Guvante

+0

@Ruhrpottpatriot тип 'Maybe' определяется как 'data Может быть a = Nothing | Просто. В этом нет ничего особенного, у типа только два конструктора: 'Nothing :: Maybe a' и' Just :: a -> Maybe a'. Чтобы построить значение типа 'Maybe a', вы используете один из этих двух конструкторов. Применение 'Just' к значению типа' a' создает новое значение типа 'Maybe a', а использование' Nothing' просто строит значение типа 'Maybe a', никаких входных данных не требуется. – bheklilr