作为学习 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
安装我需要的每个软件包)
诚实的问题是两个:
哪里是错误(在这种特定情况下)?
我需要掌握哪个工具来查找这样的错误(或一般的错误)?
提前致谢。
答案 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
不为零,则其他实现会产生相同的结果。
将mod
或mod'
与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
函数,因为它们没有为零分母定义。