Beim Versuch, Haskell zu lernen, habe ich eine Pi-Berechnung implementiert, um Funktionen und Rekursion richtig zu verstehen.Stapelüberlauf in GHCI beim Versuch, eine Nummer anzuzeigen
die Leibniz Formula Verwendung für pi Berechnung kam ich mit der Follow-up, das pi auf die Toleranz des gegebenen Parameters druckt, und die Anzahl der rekursiven Funktion aufruft, um diesen Wert zu erhalten:
reverseSign :: (Fractional a, Ord a) => a -> a
reverseSign num = ((if num > 0
then -1
else 1) * (abs(num) + 2))
piCalc :: (Fractional a, Integral b, Ord a) => a -> (a, b)
piCalc tolerance = piCalc' 1 0.0 tolerance 0
piCalc' :: (Ord a, Fractional a, Integral b) => a -> a -> a -> b -> (a, b)
piCalc' denom prevPi tolerance count = if abs(newPi - prevPi) < tolerance
then (newPi, count)
else piCalc' (reverseSign denom) newPi tolerance (count + 1)
where newPi = prevPi + (4/denom)
Also, wenn ich dies in GHCI laufen, so scheint es wie erwartet zu funktionieren:
*Main> piCalc 0.001
(3.1420924036835256,2000)
Aber wenn ich meine Toleranz zu fein eingestellt, dies geschieht:
*Main> piCalc 0.0000001
(3.1415927035898146,*** Exception: stack overflow
Dies scheint mir völlig kontraintuitiv zu sein; die tatsächliche Berechnung funktioniert gut, aber nur versuchen, wie viele rekursive Aufrufe zu drucken?
Warum ist das so?
Falls Sie nicht wissen, was ein Thunk ist (habe ich nicht, als ich Haskell startete), ist es im Grunde eine ungelöste Berechnung. In Ihrem ersten Beispiel, bevor Sie "count" drucken, wird es keinen Wert von "2000" haben, es wird einen Wert von "... + 1) +1) +1) +1) +1)' haben (Ich habe die 2000 linken Klammern am Anfang weggelassen: P). Wenn Sie das drucken, wird es tatsächlich addiert. –
Ich werde nur hinzufügen, was @DanielBuckmaster sagte, dass der wichtige Punkt dann ist, dass die Thunks sich weiter aufbauen und mehr und mehr Speicher nehmen, während man naiv erwartet, dass "zählen" etwas wie ein "Int" ist (konstant im Raum) . Sie werden sich daran gewöhnen, aber es ist definitiv etwas, das Sie beißen kann. – gspr