【问题标题】:Throwing an exception in Haskell when the object being returned is not a string.当返回的对象不是字符串时,在 Haskell 中抛出异常。
【发布时间】:2017-10-16 13:24:52
【问题描述】:

所以我正在用 Haskell 编写一个程序,它接收一个数字 n,告诉它返回从 2 开始的第 n 个素数,即第一个素数。该程序的那部分有效,但是我不明白的是,当数字为 0 或更少时,如何让程序也抛出异常。

pr :: Int -> Int
pr n = (filter(\x -> (getter) == []) [2..]) !! (n-1)

getter 引用了我编写的另一种解决主要问题的方法。它工作正常。

【问题讨论】:

  • 查找您熟悉的引发错误的函数的文档,单击“源代码”按钮,然后查看它们的作用。一个例子是!!
  • 我没有熟悉的,因为这是我第一次使用haskell
  • 要了解 (!!),请单击 here。然后herehere。和here。要阅读有关您的主题的更多信息,请尝试this 并在那里搜索“守卫”。

标签: haskell exception


【解决方案1】:

默认情况下,如果一个函数的方程没有匹配给定的参数,你会得到一个运行时错误:

fromJust :: Maybe a -> a
fromJust (Just a) = a
-- No case for Nothing
-- fromJust Nothing throws error at runtime

但是,这不适用于数字。相反,守卫会做类似的事情:

assertOver0 :: Int -> ()
assertOver0 n | n > 0 = ()
-- No case for n <= 0
-- assertOver0 (-1) throws error at runtime

虽然这是默认行为,但模式/防护不完整是不好的风格。相反,使用errorundefined 显式导致错误:

pr n | n >= 0    = filter (null . getter) [2..] !! n
     | otherwise = error "Table flip"
-- undefined is just like error, except that error lets you give an error message
-- and undefined doesn't (undefined is more useful when you know it will never
-- be evaluated, and you don't need to give an error message)
-- undefined :: a; error :: String -> a
-- That is, they can take on any type you want them to have, because whatever code
-- is after them will never be executed anyway
-- I took liberties with your definition of pr. Your filtering function didn't use
-- x, so I wrote what I think you meant. I also made it 0-indexed.
-- Prelude.null checks for [], but doesn't incur an Eq constraint, so I replaced
-- (== []) with it.
-- Parens are not needed around the filter, because function application has
-- the highest precedence.

Haskell 在Control.Exception 中也有一个更复杂的异常机制,但在这里你可能不需要它。一般来说,异常和部分函数是被看不起的,(因为你只能在IO中处理它们),你应该争取像MaybeEither这样的monad。

import Control.Monad
pr n = do guard $ n >= 0 -- guard True = Just (); guard False = Nothing (in this case)
          return $ filter (null . getter) [2..] !! n
pr 2 = Just 5
pr (-1) = Nothing

不过,所有这些都是不必要的。 (!!) 已经在负索引上出错了

ghci> "abc" !! -1
*** Exception: Prelude.!!: negative index

所以我们又回到了开始的地方:

pr n = filter (null . getter) [2..] !! n

还有a library 将列表操作(包括(!!))重新定义为单子而不是部分。

【讨论】:

  • 进一步的小改进:使用null 代替(== [])。更短且不会产生Eq 约束:-)
【解决方案2】:

在 Haskell 中,基本上一切都只是一个库函数。因此,可以通过在线搜索引擎轻松找到它。这包括错误处理。因此你可以ask Hayoo for error,或for raise,或for throw。这三个都存在——但raise 只是在不同的风格中,专门用于特定的库,而throwerrorbase 的一部分,因此是“Haskell 本身”的一部分。

  • throw 可用于生成正确类型的异常,如果您可能在某个时刻在程序本身中想要捕获/分析错误,则非常适合。
  • error 主要用于使程序崩溃,同时在终端上生成(希望)有用的诊断消息,这似乎是您想要的。

error 的类型是,从 GHC-8 开始,

error :: HasCallStack => String -> a

HasCallStack 是最近添加的,它允许程序告诉您在代码中在哪里发生了错误。不过,这不会改变您使用该功能的方式;在旧版本的 GHC 中,类型只是

error :: String -> a

这意味着,您只需给error 一些错误消息,然后将其用作任何函数的“结果”,无论该函数的实际结果类型应该是什么。在你的情况下,

pr n | n >= 0     = ...
     | otherwise  = error "Table flip"

如果你给这个函数一个负数,那么它不会给出任何实际结果,而是用消息Table flip 使程序崩溃,并且在 GHC>=8 中,还会告诉你这个错误发生在pr 中。

您可能还想知道 pr 的调用位置,以便实际调试问题。您可以自己使用 GHC 调用堆栈模拟:

import GHC.Stack

pr :: HasCallStack => Int -> Int
pr n | n >= 0     = ...
     | otherwise  = error "Table flip"

请注意,我不需要以任何方式更改实现,我只是添加了HasCallStack 约束。


如果你查看the documentation, as of GHC-8.2,你会看到一个相当可怕的签名
error :: forall (r :: RuntimeRep). forall (a :: TYPE r). HasCallStack => [Char] -> a

...别担心,这些只是引发重大错误的实现细节,实际上 Haskell 语言并不那么自然地支持。

【讨论】:

    【解决方案3】:

    行政管理 其他答案为您提供了一些很好的文字,因此我将在这里编写大量代码并进行简单的解释。如果还有什么不清楚的地方,请发表评论,我会尽力填写(或者你们中的任何一个普通的 S.O. 回答者都可以击败我)。

    答案

    您提出了n &lt;= 0 的输入无效的问题:

    pr :: Int -> Int
    pr n = (filter(\x -> (getter) == []) [2..]) !! (n-1)
    

    简单的解决方案是匹配模式或守卫并手动抛出异常:

    pr :: Int -> Int
    pr n | n <= 0    = error "NO!"
         | otherwise = ...
    

    但有时你想要一个非字符串异常,在这种情况下你可能想要Control.Exception:

    {-# LANGUAGE DeriveAnyClass #-}
    -- ^^^ This is not just a comment, enables a language extension
    import Control.Exception as X
    
    data MyException = ZeroOrNegative
        deriving (Exception,Show,Eq,Ord)
                  -- ^^ N.B. you should derive 'Exception' for
                  --    types you want to 'throw'
    
    pr n | n <= 0    = X.throw ZeroOrNegative
         | otherwise = ...
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多