【问题标题】:Haskell Data.Maybe *** Exception: Maybe.fromJust: NothingHaskell Data.Maybe *** 例外:Maybe.fromJust:没有
【发布时间】:2018-07-02 04:27:44
【问题描述】:

我尝试编写一个练习程序。如果它可以根据给定的语法解析字符串,它应该读取一个字符串并返回一个空列表。如果字符串不是有效的语法,它应该返回“Nothing”。喜欢这里:

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

我编写了以下函数,它们在 GHCI 中加载和编译,但是当我使用任何参数运行 prog 时,我得到以下异常:*** Exception: Maybe.fromJust: Nothing

我想我以错误的方式访问或传递了 Maybe 字符串,但不确定在哪里。欢迎任何有关正确处理 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))

【问题讨论】:

  • 异常中错误消息的哪一部分对您来说没有意义?
  • 好吧,基本上它首先发生的原因。在我的理解中,当使用有效参数字符串“c+c*c$”执行 prog 时,它甚至不应该存在 Nothing 状态。如果字符串不正确,它不应该返回异常,只返回“Nothing”。
  • 您正在使用很多偏函数,例如head,tail,fromJust,应尽可能避免。最好使用模式匹配和打开警告,因为它通常允许编译器指出程序崩溃的情况。
  • 这个逻辑有点难以理解,但如果你跟踪它,你会看到第一个 c 匹配,但随后有一个 match '*' 其余返回 Nothing 和你最终在ftail Nothingftail 定义中的第一个守卫尝试评估 fromJust Nothing 并且......你的例外!

标签: haskell maybe


【解决方案1】:

OP 的代码中有几个反模式。我只讨论这个sn-p。

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.next()) 之前检查数据是否存在 (x.hasNext())。忘记检查会导致运行时错误/异常。使用模式匹配,这两个步骤组合在同一个语言结构中,因此无法“忘记”检查并导致程序崩溃。

与原始代码不同,match x (Just []) 不会崩溃,而是返回 Nothing

【讨论】:

  • 我比我更喜欢这个match :)
【解决方案2】:

fromJust 期望传递一个Just 值,它接收一个Nothing 值,这就是发生此异常的原因:

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

请注意,我鼓励您使用 maybe 函数,它可以帮助您澄清我认为的代码(并且......也许会找到错误:))

另外,maybefromJust 更可取,因为它不是偏函数(即保证函数在运行时不会出错)

例如它允许你重写:

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

作为

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

还有一点:headtail 也是部分函数,​​您更喜欢使用这样的模式匹配,以避免在字符串为空时出现运行时异常,例如:

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

(编辑:另外,请参阅@chi 的答案,它给出了一个很好的匹配的惯用实现!)

【讨论】:

    猜你喜欢
    • 2017-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-09
    • 1970-01-01
    • 2016-09-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多