我正在尝试实现生成器,选择器模式以近似计算haskell中的平方根
我的生成器如下:
generator :: (Double -> Double) -> Double -> [Double]
generator f a = generator f (f a)
我的选择器:
selector :: Double -> [Double] -> Double
selector eps (a : b : r)
| abs(a - b) <= eps = b
| otherwise = selector eps (b : r)
和近似函数:
next :: Double -> Double -> Double
next n x = (x + n/x) / 2
像selector 0.1 (generator (next 5) 2)
这样称呼
应该给我...(next 5( next 5 (next 5 2)))
,所以[2.25, 2.23611111111111, 2.2360679779158,...]
因为我的eps参数是0.1 abs(a - b) <= eps
应该在第一次执行时为true,因此给我2.23611111111111
。但是,我确实会陷入无尽的循环。
有人可以向我解释这些功能的实现有什么问题吗?
预先感谢
答案 0 :(得分:4)
此定义
generator f a = generator f (f a)
永远不会生成任何列表元素:它会陷入无限递归中。你可能想要
generator f a = a : generator f (f a)
这使a
成为第一个元素,随后是我们使用递归生成的所有其他元素。
避免在列表中放入未经评估的重击也是有益的。为避免这种情况,可以使用
generator f a = a `seq` (a : generator f (f a))
,以便a
得到较早的评估。在您的代码中,这无关紧要,因为
选择器一生成它们,便立即评估它们。
答案 1 :(得分:3)
正如chi's answer正确指出的那样,您的生成器函数缺少a:
。但是,有一个比仅添加方法更好的解决方案。完全摆脱generator
,而改用内置方法iterate
(或iterate'
from Data.List,以避免不必要的重击)。这些方法具有与generate
相同的行为,但支持list fusion这样的优化,而您自己的方法则不会。当然,还有一个优点是,您不必编写和维护该函数即可。