Haskell Data.Maybe *** Исключение: Maybe.fromJust: ничего - PullRequest
0 голосов
/ 01 июля 2018

Я пытаюсь написать программу для упражнения. Он должен прочитать строку и вернуть пустой список, если он может анализировать строку в соответствии с заданной грамматикой. Если строка не в правильной грамматике, она должна вернуть «Nothing». Как здесь:

>prog "c+c*c$"
Just""
>prog "c+c-c$"
Nothing

Я написал следующие функции, они загружаются и компилируются в GHCI, но когда я запускаю prog с любым аргументом, я получаю следующее исключение: *** Exception: Maybe.fromJust: Nothing

Я полагаю, что я получаю доступ или передаю Возможно строку неправильно, но не уверен, где. Любая помощь относительно правильного обращения со структурами Maybe приветствуется.

Вот мой код:

import Data.Maybe


match :: Char -> Maybe String -> Maybe String
match x input
  | (isNothing input == False) && (x == head (fromJust(input))) = Just (tail (fromJust(input)))
  | otherwise = Nothing


prog :: String -> Maybe String
prog x = match '$' (expr (Just (x)))


expr :: Maybe String -> Maybe String
expr x = ttail (term x)


term :: Maybe String -> Maybe String
term x = ftail (factor x)


ttail :: Maybe String -> Maybe String
ttail x
  | fromJust(x) == [] = Just []
  | otherwise = ttail (term (match '+' x))


factor :: Maybe String -> Maybe String
factor x = match 'c' x


ftail :: Maybe String -> Maybe String
ftail x
  | fromJust(x) == [] = Just []
  | otherwise  = ftail ( factor ( match '*' x))

Ответы [ 2 ]

0 голосов
/ 01 июля 2018

В коде ОП есть несколько антипаттернов. Я буду обсуждать только этот фрагмент.

match :: Char -> Maybe String -> Maybe String
match x input
  | (isNothing input == False) && (x == head (fromJust(input))) = Just (tail (fromJust(input)))
  | otherwise = Nothing
  • Использование isNothing, fromJust является антипаттерном, поскольку последний является частичной функцией, которая вызывает сбой программы при вводе с Nothing. Программист должен быть осторожен, чтобы всегда проверять isJust заранее, что легко забыть. Гораздо проще полностью забыть об этих функциях и вместо этого полагаться на сопоставление с образцом (см. Ниже).
  • .. == False следует переписать как not ..
  • not (isNothing ..) должно быть isJust .. (но опять же сопоставление с образцом делает это бессмысленным)
  • head,tail,!! также являются частичными функциями, и, по возможности, их следует заменить сопоставлением с образцом. Выше head потенциально вызывается на [], поэтому нам нужно проверить это заранее. Сопоставление с образцом позволяет избежать необходимости.
  • Вместо .. == [] можно использовать null .. (или, лучше, сопоставление с образцом).
  • Никогда не пишите f(x) для вызова функции, скобки там не имеют смысла.
  • Включите предупреждения с помощью флага -Wall: компилятор часто обнаруживает проблемы в коде.

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

Для сравнения приведенный выше код можно переписать так:

match :: Char -> Maybe String -> Maybe String
match x (Just (y:ys)) | x==y = Just ys
match _ _                    = Nothing

Обратите внимание, как сопоставление с образцом одновременно проверяет, является ли аргумент Just с непустым списком внутри, и извлекает данные внутри конструкторов. Когда это терпит неудачу, берется следующий случай совпадения (вместо сбоя программы).

В языках без сопоставления с образцом (скажем, Java) библиотеки часто заставляют нас не забывать проверять наличие данных (x.hasNext()) перед доступом к данным (x.next()). Забывание проверки вызывает ошибку / исключение во время выполнения. При сопоставлении с образцом эти два шага объединяются в одной языковой конструкции, так что невозможно «забыть» проверку и завершить работу программы.

В отличие от исходного кода, match x (Just []) не вылетает, а вместо этого возвращает Nothing.

0 голосов
/ 01 июля 2018

fromJust ожидает, что ему будет передано значение Just, и он получит значение Nothing, поэтому возникает исключение:

http://hackage.haskell.org/package/base-4.11.1.0/docs/Data-Maybe.html#v:fromJust

Обратите внимание, что я бы посоветовал вам использовать функцию maybe, которая может помочь прояснить ваш код, я думаю (и ... возможно, найти ошибку :))

Кроме того, maybe предпочтительнее, чем fromJust, потому что это не частичная функция (т. Е. Гарантируется, что функция не выдаст ошибку во время выполнения)

например, он позволяет переписать:

match :: Char -> Maybe String -> Maybe String
match x input
  | (isNothing input == False) && (x == head (fromJust(input))) = Just (tail (fromJust(input)))
  | otherwise = Nothing

1021 * а *

match :: Char -> Maybe String -> Maybe String
match x input =
  maybe
    Nothing
    (\i ->
      if x == head i
        then Just $ tail i
        else Nothing)
    input

Еще одна вещь: head и tail тоже являются частичными функциями, вы бы предпочли использовать сопоставление с образцом, как это, чтобы избежать исключений времени выполнения, когда строка пуста, например:

match :: Char -> Maybe String -> Maybe String
match x input =
  maybe
    Nothing
    (\i -> case i of
      [] -> Nothing
      first:rest -> 
        if x == first
          then Just rest
          else Nothing)
    input

(Edit: также, смотрите ответ @chi, который дает хорошую идиоматическую реализацию match!)

...