2009-11-27 23 views
2

任何人可以帮助我理解这段代码Haskell的执行顺序

solve s | s == 0 = Nothing 
     | s == 1 = Just 1 
     | otherwise = 
      check [solve (s-(x*2)) | x <- [1..9]] 

check x = case x of 
      []   -> Nothing 
      (Nothing:xs) -> check xs 
      (x:xs)  -> x  

为什么这给出了流程,当我试图用偶数值运行栈,并且有在Haskell任何方式,我可以调试,看到了正在运行的程序的实际价值,就像在eclipse中我们做的一样?

谢谢

回答

9

至少有GHCI,也没有办法为“一步”通过代码(编辑:不再是真实的,见下面的评论),但你肯定可以添加使用Debug.Trace调试语句。换句话说,如果你要检查所有的递归调用solve你可以说:

check [trace ("solving for " ++ show (s-(x*2)))) (solve (s-(x*2))) | x <- [1..9]] 

有写一个更清洁的方式,但只是描述了一个思路。

在这种特殊情况下,它无限递归的原因是解决的基本情况永远不会达到。 solve 2例如,解析为check [solve 0, solve -2, solve -4 ..., solve -16]solve -2解析为check [solve -4, solve -6, ...]

+2

这不是真的!查看GHCi最新版本的手册:http://www.haskell.org/ghc/docs/latest/html/users_guide/ghci-debugger.html – porges 2009-11-28 11:11:48

+0

谢谢。我会检查一下。 – Dan 2009-11-28 17:04:42

2

堆栈溢出表明无限递归。对“解决” 的其他情况的分支不保证终止。我不确定代码应该在这里做什么,所以我不能提出修复建议。希望有所帮助!

1
solve s | s == 0 = Nothing 
     | s == 1 = Just 1 
     | otherwise = 
      check [solve (s-(x*2)) | x <- [1..9]] 

solve 2的情况下:

(2 - (1 * 2)) = 0 (0 - (2 * 2)) = -2

等等。

我不确定调试的东西,但这就是为什么它溢出堆栈。它无限递归。

2

添加到Dan的答案中,您可以简单地在解决方案中推送跟踪,然后会显示问题。

solve s | s == 0 = Nothing 
     | s == 1 = Just 1 
     | otherwise = 
      trace (show s) $ check [solve (s-(x*2)) | x <- [1..9]] 
2

可以重写使用图案代替匹配第一部分,它比警卫要好很多符号(if语句);)

solve 0 = Nothing 
solve 1 = Just 1 
solve s = check [solve (s - (x * 2)) | x <- [1..9]] 

列表理解通过9进料范围为1至解决方法,这要求用 s - (x * 2)解决,说实话,我不能直观地告诉那将终止...但让我们考虑一些例子。

solve 2 

调用解决2将导致下面的列表,因为Haskell是惰性该列表将不会有值,直到你(有副作用)尝试和打印这些...

solve s - 1 * 2 
solve s - 2 * 2 
solve s - 3 * 2 
solve s - 4 * 2 
solve s - 5 * 2 
solve s - 6 * 2 
solve s - 7 * 2 
solve s - 8 * 2 
solve s - 9 * 2 

一个简单的solve 2将尝试solve -2这将试图解决其他事情,而且'不会结束。

0
check :: [Maybe a] -> Maybe a 
check = Data.Maybe.listToMaybe . Data.Maybe.catMaybes 

solve :: (Num a, Num b) => a -> Maybe b 
solve 0 = Nothing 
solve 1 = Just 1 
solve s = solve (s-2) 

在这里,这是很明显的是,solve -1solve 5.3运行无限。 解决方案的这个版本就像一个while循环一样运行。 您发布的原始版本会在每次调用不需要的东西时将垃圾邮件发送到您的内存/堆栈中。

你可以重写此:

solve s = check [solve (s-x)|x<-[2,4..18]] 

这样:

solve s = check [solve (s-2),solve (s-4),solve (s-6),solve (s-8),solve (s-10),solve (s-12),solve (s-14),solve (s-16),solve (s-18)] 

但每当solve (s-2)回报没有,那么每个solve (s-x)将返回任何结果,因为该值已经被测试是这样的:solve ((((s-2)-2)-2)-2)

它是一个指数算法来测试某事,可以在线性时间内测试或在c时间。

我建议阅读这本书: Haskell: The Craft of Functional Programming