找出“比率有零分母”的起源

时间:2015-09-22 18:43:26

标签: debugging haskell

作为学习 Haskell 过程中的个人练习,我正在尝试将此 F#片段移植到Random Art

我没有嵌入完整的源代码以避免问题膨胀,但可以gist显示。

该计划的一个重要部分是Expr类型:


data Expr =
    VariableX
  | VariableY
  | Constant
  | Sum Expr Expr
  | Product Expr Expr
  | Mod Expr Expr
  | Well Expr
  | Tent Expr
  | Sin Expr
  | Level Expr Expr Expr
  | Mix Expr Expr Expr
  deriving Show

和两个功能:

    在给定多次迭代的情况下,
  • gen :: Int -> IO Expr随机生成树状结构

  • eval :: Expr -> IO (Point -> Rgb Double)遍历树并终止生成绘图功能。

传递给gen的数字越高,产生以下异常的概率就越高:Ratio has zero denominator

我是Haskell的新手,所以要解决我尝试编译的问题,如上所述:


ghc RandomArt.hs -prof -auto-all -caf-all

仅获取此信息(对我来说毫无用处)信息:


$ ./RandomArt +RTS -xc

*** Exception (reporting due to +RTS -xc): (THUNK_STATIC), stack trace:
  GHC.Real.CAF
  --> evaluated by: Main.eval.\,
  called from Main.eval,
  called from Main.tga.pxs',
  called from Main.tga,
  called from Main.save,
  called from Main.main,
  called from :Main.CAF:main
  --> evaluated by: Main.eval.\.r,
  called from Main.eval.\,
  called from Main.eval,
  called from Main.tga.pxs',
  called from Main.tga,
  called from Main.save,
  called from Main.main,
  called from :Main.CAF:main
*** Exception (reporting due to +RTS -xc): (THUNK_STATIC), stack trace:
  Main.tga,
  called from Main.save,
  called from Main.main,
  called from GHC.Real.CAF
RandomArt: Ratio has zero denominator

将生成的函数保存到 TGA 文件的代码可以正常工作,因为它是我的previous excercize(来自 OCaml 的端口)。

我尝试从 GHCi 执行各种Expr树,通过 hand 组装数据或应用程序中的函数但我无法找出错误。

Haskell docs讨论了一个名为loch的软件包,它应该能够编译保留的源代码行号,但我无法安装它(我通常使用cabal install安装我需要的每个软件包)

诚实的问题是两个:

  • 哪里是错误(在这种特定情况下)?

  • 我需要掌握哪个工具来查找这样的错误(或一般的错误)?

提前致谢。

1 个答案:

答案 0 :(得分:6)

例外

让我们首先关注异常。

查找错误

  

哪里是错误(在这种特定情况下)?

mod'。如果我们提供替代版本而非Data.Fixed

,我们可以轻松检查此问题
mod' :: RealFrac a => a -> a -> a
mod' _ 0 = error "Used mod' on 0"
mod' a b =
    let k = floor $ a / b
    in a - (fromInteger k) * b

我们现在获得Used mod' on 0

原理

  

我需要掌握哪个工具来查找这样的错误(或一般的错误)?

在这种情况下,必要的提示已经出现在异常消息中:

Ratio has zero denominator

这意味着在Ratio的上下文中有一个被零除的地方。所以你需要照顾你划分东西的所有地方。由于您只使用(/)mod',因此可以归结为其中一个是否可以抛出此异常:

    如果在(/)上使用,
  • ±Infinity通常会将除以Double除以零,
  • mod'在内部使用toRational,即Ratio Integer

所以只有一个罪魁祸首。请注意,如果b不为零,则其他实现会产生相同的结果。

实际问题

modmod'b == 0一起使用的定义不明确。毕竟,modulo operation应该包含以下属性:

prop_mod :: Integral n => n -> n -> Bool
prop_mod a b = 
  let m = a `mod` b
      d = a `div` b

  in a == b * d + m   -- (1)
     && abs m < abs b -- (2)

如果b == 0,则不存在任何对(d, m),以便(1)(2)成立。如果我们放松这个法则并扔掉(2),mod的结果不再是唯一的。这导致以下定义:

mod' :: RealFrac a => a -> a -> a
mod' a 0 = a -- this is arbitrary
mod' a b =
    let k = floor $ a / b
    in a - (fromInteger k) * b

然而,这是一个任意的定义。你必须问自己,“如果我不能以理智的方式使用mod,我真正想做什么”。由于F#显然没有抱怨a % 0,所以请查看他们的文档。

无论哪种方式,您都不能使用库mod函数,因为它们没有为零分母定义。