我是Haskell的业余爱好者,试图通过“x”的平方根的无限近似列表,其中“acc”代表此步骤的生成。但是,当我运行下面的代码时,我得到了潜在的错误。
as' x acc = ( last(take (acc-1) (as' x (acc-1)))
+ (acc / last(take (acc-1) (as' x (acc-1)))) ) / 2 : as' x (acc+1)
ERROR "a5.hs":34 - Instance of Fractional Int required for definition of as'
此外,当我尝试应用此类型代码时,出现错误:
as' :: Float -> Float -> Float
Type error in application
*** Expression : (last (take (acc - 1) (as' x (acc - 1))) + acc / last (take (acc - 1) (as' x (acc - 1)))) / 2 : as' x (acc + 1)
*** Term : as' x (acc + 1)
*** Type : Float
*** Does not match : [a]
编辑:为了给你一些清晰度,我想在列表的上下文中使用这个功能。 例如因为x = [1,为'x 2]。 这个想法是,这将积累一个无限的列表,因为'将递归地调用自己。因此,为什么我觉得我可以在这里列表上运作。
有人可以给我一些清晰度吗?
答案 0 :(得分:12)
take
的类型签名是
take :: Int -> [a] -> [a]
以下是您使用take
的方式:
take (acc-1) (as' x (acc-1))
所以我们可以得出结论
(acc-1) :: Int -- first parameter to `take`
acc :: Int -- therefore
(as' x (acc-1)) :: [a] -- second parameter to `take`, we don't know what `a` is
但你的代码说
as' :: Float -> Float -> Float
as' x acc = ...
我们从中推断出
x :: Float -- first parameter to `as'`
acc :: Float -- second parameter to `as'`
(as' x (acc-1)) :: Float -- result of `as'`
这导致了一些矛盾:
acc
不能同时是Int
和Float
(as' x (acc-1))
不能同时是[a]
和Float
- 这是第二条错误消息试图告诉您的内容最终,您尝试在不是列表的内容上使用take
。我不确定你要做什么。
您可能打算签名
as' :: Float -> Int -> [Float]
那应该(我没有测试过)修复上面的类型错误,但仍然存在一个更基本的问题:每当你计算列表的 n 元素时,你计算* n列表的第-1个元素重新两次(依此类推,回到列表的开头:重新计算的指数增长),即使可能已经计算了这个元素。没有分享正在进行中。
e.g。考虑
as' x acc = ( prev + (acc / prev) ) / 2 : as' x (acc+1)
where prev = last(take (acc-1) (as' x (acc-1)))
这仍然是低效的:您仍然重新计算列表的先前元素。但是现在你只计算下一个元素时重新计算所有先前的元素。
(我也不应该指出last(take (acc-1) (as' x (acc-1)))
可以简化为(as' x (acc-1)) !! (acc-2)
。)
生成无限列表的常用方法是使用iterate
来确定每个元素仅依赖于前一个元素。
复杂的是,每个元素都取决于累加器以及前一个元素。我们将把累加器合并到列表的每个元素中。当我们完成后,我们将丢弃累加器以产生我们的最终无限列表。
approxRoots :: Float -> [Float]
approxRoots x = map fst $ iterate next (x, 1)
-- I don't know what your initial approximation should be
-- I've put `x` but that's probably wrong
where next (prev, acc) = (prev + acc / prev, acc + 1)
-- First element of each pair is the approximation,
-- second element of each pair is the "accumulator" (actually an index)
-- I've probably transcribed your formula wrongly
答案 1 :(得分:7)
dave4420's answer已经非常好了,我只是想分享一下如何从编译器给你的错误信息中获得最大收益。再来一次:
*** Expression : (last (take (acc - 1) (as' x (acc - 1))) + acc / last (take (acc - 1) (as' x (acc - 1)))) / 2 : as' x (acc + 1)
*** Term : as' x (acc + 1)
*** Type : Float
*** Does not match : [a]
这意味着长表达式中的as' x (acc + 1)
部分预计会产生一个列表,但它实际上会给出Float
值。
为什么编译器希望它是一个列表?好吧,让我们看一下表达式中使用术语的位置:
(last .... ) / 2 : as' x (acc + 1)
即,它被用作(:)
函数的第二个参数,编译器知道该函数的第二个参数必须是一个列表(编译器知道(:)
的签名是a -> [a] -> [a]
虽然它没有在错误消息中提及该部分。)
为什么它实际上是Float
?由于您没有提供函数签名,编译器会为您推导出它并实际打印它:
as' :: Float -> Float -> Float
因此编译器确定as'
需要两个Float
值并产生Float
值。我不知道为什么会这样做。
我的建议是通过自己明确写下函数签名来开始调试此问题。这样做会导致出现不同的错误消息,这更接近于您的期望与实际代码不匹配的原因。