1

私は 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, as' x 2] のように。as' は自分自身を再帰的に呼び出すため、これは無限リストを蓄積するという考えです。したがって、ここでリストを操作できると感じたのはなぜですか。

誰かが私にいくつかの明確さを提供してもらえますか?

4

2 に答える 2

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'`

これは、いくつかの矛盾につながります。

  • accIntと をFloat同時に指定することはできません
  • (as' x (acc-1))[a]と を同時に指定することはできませんFloat--- これは、2 番目のエラー メッセージが伝えようとしている内容です。

最終的にtake、リストではないもので使用しようとしています。あなたが何をしようとしているのかわからない。


あなたはおそらく署名を持っているつもりでした

as' :: Float -> Int -> [Float]

これで (私はテストしていませんが) 上記の型エラーは修正されるはずですが、さらに根本的な問題が残っています: リストのn番目の要素を計算するたびに、リストの *n-1* 番目の要素を新たに2 回計算します。 (など、リストの先頭に戻ります: 再計算の指数関数的成長)、おそらくこの要素は既に計算されていますが。共有は行われていません。

例えば考慮

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
于 2012-12-21T11:17:13.127 に答える
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]

これはas' x (acc + 1)、長い式の部分がリストを生成することが期待されていたが、実際にはFloat値を与えることを意味します。

  • コンパイラがリストであることを期待するのはなぜですか? さて、用語が式のどこで使用されているか見てみましょう。

    (last .... ) / 2 : as' x (acc + 1)
    

    つまり、関数の 2 番目の引数として使用され、コンパイラは、この関数の 2 番目の引数がリストでなければならないことを認識します (コンパイラは、の署名がis(:)であることを知っています が、エラー メッセージのその部分について言及していません)。 .(:)a -> [a] -> [a]

  • なぜそれが実際にあるのFloatですか?関数シグネチャを提供しなかったため、コンパイラはそれを推測し、実際にも出力しました。

    as' :: Float -> Float -> Float
    

    そのため、コンパイラはas'が 2 つの値を取りFloat、値を生成すると判断しましたFloat。なぜそんなことをしたのか頭のてっぺんからはわかりません。

私のアドバイスは、関数シグネチャを自分で明示的に書き留めて、この問題のデバッグを開始することです。そうすることで、予想と実際のコードとの不一致の原因により近い別のエラー メッセージが表示されます。

于 2012-12-21T12:02:45.370 に答える