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

Sam*_*inn 1 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)
Run Code Online (Sandbox Code Playgroud)

ERROR "a5.hs":34 - Instance of Fractional Int required for definition of as'

此外,当我尝试应用此类型代码时,我收到一个错误:

as' :: Float -> Float -> Float
Run Code Online (Sandbox Code Playgroud)

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

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

dav*_*420 12

类型签名take

take :: Int -> [a] -> [a]
Run Code Online (Sandbox Code Playgroud)

以下是您的使用方法take:

take (acc-1) (as' x (acc-1))
Run Code Online (Sandbox Code Playgroud)

所以我们可以得出结论

(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
Run Code Online (Sandbox Code Playgroud)

但是你的代码说

as' :: Float -> Float -> Float
as' x acc = ...
Run Code Online (Sandbox Code Playgroud)

我们从中推断出

x               :: Float  -- first parameter to `as'`
acc             :: Float  -- second parameter to `as'`
(as' x (acc-1)) :: Float  -- result of `as'`
Run Code Online (Sandbox Code Playgroud)

这导致了几个矛盾:

  • acc不可能是一个Int和一个Float同时
  • (as' x (acc-1))不能是a [a]和a Float同时---这是第二条错误消息试图告诉你的

最终,您正在尝试使用take不是列表的东西.我不确定你要做什么.


您可能打算签名

as' :: Float -> Int -> [Float]
Run Code Online (Sandbox Code Playgroud)

这应该(我没有测试它)修复类型错误之上,但仍留下一个更根本的问题:当你计算ñ个列表的元素,你计算*列表中的n-1个*个元素重新两次(依此类推,回到列表的开头:重新计算的指数增长),尽管可能已经计算了这个元素.没有共享.

例如考虑

as' x acc = ( prev + (acc / prev) ) / 2 : as' x (acc+1)
  where prev = last(take (acc-1) (as' x (acc-1)))
Run Code Online (Sandbox Code Playgroud)

这仍然是低效的:您仍然重新计算列表的先前元素.但现在,您只需在计算下一个元素时重新计算所有先前的元素.

(我也不能指出我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
Run Code Online (Sandbox Code Playgroud)


Fre*_*abe 7

dave4420的答案已经非常好了,我只是想分享一下如何从编译器给你的错误信息中获得最大的收益.这是它:

*** 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]
Run Code Online (Sandbox Code Playgroud)

这意味着as' x (acc + 1)long表达式中的部分预计会产生一个列表,但实际上它给出了一个Float值.

  • 为什么编译器希望它是一个列表?好吧,让我们看一下表达式中术语的用法:

    (last .... ) / 2 : as' x (acc + 1)
    
    Run Code Online (Sandbox Code Playgroud)

    即,它是作为第二个参数的(:)函数,编译器知道第二个参数给这个函数必须是一个列表(编译器知道的签名 (:)a -> [a] -> [a],虽然它并没有提到在错误信息的那部分) .

  • 它为什么实际上是一个Float?由于您没有提供函数签名,编译器会为您推导出它并实际打印它:

    as' :: Float -> Float -> Float
    
    Run Code Online (Sandbox Code Playgroud)

    因此编译器确定as'采用两个Float值并产生一个Float值.我不知道为什么会这样做.

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