Haskell - 定义文件名所需的分数Int实例?

时间:2012-12-21 10:50:51

标签: haskell

我是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]。 这个想法是,这将积累一个无限的列表,因为'将递归地调用自己。因此,为什么我觉得我可以在这里列表上运作。

有人可以给我一些清晰度吗?

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不能同时是IntFloat
  • (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值。我不知道为什么会这样做。

我的建议是通过自己明确写下函数签名来开始调试此问题。这样做会导致出现不同的错误消息,这更接近于您的期望与实际代码不匹配的原因。