Warum ist Perl so viel Angst vor "deep recursion"?
Stolperte ich kürzlich über das Buch Higher-order Perl, die im Grunde zeigt Wege, Dinge zu tun, in Perl in einem funktionalen Weg. Der Autor erklärt, dass Perl 6 von 7 Kern-features von Lisp, während C keine hat.
Hatte ich ein problem, das aussah wie ein guter Kandidat für eine rekursive Lösung, und ich codiert es in diesem Mode. Aber Perl beschwerte sich über "deep recursion". Ich habe ein wenig gegoogelt und fand ein Perl-Mönch erklärt, dass "Perl ist nicht Haskell". Apperently Sie erhalten eine Beschwerde standardmäßig, wenn die Rekursionstiefe von mehr als 100 Ebenen.
Dort sind Möglichkeiten zu erweitern, diese zu begrenzen oder deaktivieren Sie es vollständig, aber meine Frage ist:
- Gibt es einen Grund, dass Perl ist so nervös über Rekursion, während Haskell überhaupt nicht?
Du musst angemeldet sein, um einen Kommentar abzugeben.
Weil in Haskell, wir haben Faulheit & bewacht Rekursion und tail-call-Optimierung und Perl hat weder.
Dies bedeutet im wesentlichen, dass jedem Funktionsaufruf weist eine festgelegte Menge an Speicher, genannt "Stapel", bis die Funktion zurückgibt. Beim schreiben von rekursiven code, erstellen Sie eine riesige Menge an Speicher, da dieser verschachtelte Funktionsaufrufe und schließlich kann die stack-überlauf. TCO ermöglicht es die ungenutzte chunks optimiert werden entfernt.
Ohne diese ist es nicht wirklich eine gute Idee, auf die Sie sich verlassen Rekursion. Zum Beispiel, sagen, Sie schrieb eine rekursive version von
map
, es würde Abstürzen mit jeder anständigen Liste. In Haskell, bewacht Rekursion bedeutet, dass mit großen Listen, die rekursive Lösung ist viel schneller als eine "Schleife" like-Funktion.TLDR: Haskell-Implementierungen sind darauf ausgelegt, eine funktionale/rekursive Stil und perl-Implementierung ist nicht und zurück in die guten alten Tage, 100 Ebenen von Funktionsaufrufen war eine vernünftige Grenze.
goto
Schlüsselwort zu tun, einen Schwanz nennen.goto
Schlüsselwort in Perl führt mehrere verschiedene Aufgaben.goto LABEL
wirkt wie eine traditionelle "springen".goto $coderef
undgoto &subname
tun, einen Schwanz nennen. Wischen Sie alle Spuren der aktuellen sub aus dem Stapel und rufen Sie dann die coderef/sub. (In der Tat, intern realisiert werden Sie als eine sofortige Rückkehr, gefolgt von einem normalen sub-Aufruf.)Sub::Call::Tail
machen viel ordentlicher Schwanz-Anruf mit code.Den "deep recursion" Warnung ist optional, und ein Indikator, dass etwas schief gegangen sein: die meisten der Zeit, eine Funktion ruft sich selbst immer und immer wieder nicht gedacht (Perl ist ein multi-Paradigma Sprache, und viele Menschen nicht verwenden funktionale Idiome). Und auch, wenn bewusst die Beschäftigung von Rekursion, es ist viel zu leicht zu vergessen, der base case.
Es ist leicht zu schalten Sie die "deep recursion" Warnung aus:
Ausgabe:
Es ist wahr, dass Perl nicht ausgeführt tail-Rekursion Optimierung, denn das würde Sie versauen die
caller
Ausgang (was wichtig ist für einige Dinge wie pragmas oderCarp
). Wenn Sie möchten, führen Sie manuell einen Schwanz nennen, dannwird
obwohl schlechte Dinge passieren können, wenn Sie dummerweise
local
niert@_
.Runaway Rekursion Absturz wird entweder ein Perl-Programm oder ein Haskell-Programm nach unbegrenzter Zuteilung schließlich erschöpft den verfügbaren Speicher. Beide Sprachen sich mit diesem Potenzial Fallstrick in unterschiedlicher Weise.
Perl
Perl ist eine multi-Paradigma Sprache, sondern prozedurale am Herzen, und deep stack-Tiefe in diesem Paradigma kann zeigen, runaway Rekursion. Die perldiag-Dokumentation auf diese Diagnose-Staaten so viel, Hervorhebung Hinzugefügt.
Es ist lediglich eine Laufzeit-Warnung, so dass, wenn Sie glauben, dass es unecht ist, deaktivieren Sie es mit einem pragma.
Einen drastischeren und offenbar unnötige Weg, um den gleichen Effekt erzielen, ist beschrieben in dem gleichen Abschnitt der Perl-Dokumentation.
Haskell
Haskell ist eine rein funktionale Sprache ist, wo die Rekursion wesentlich ist. Schreiben Haskell tail-rekursiv, wie in
nutzt die compiler-Unterstützung für lazy evaluation und bewacht, Rekursion, was bedeutet, gut geschriebenen Haskell-code kann sowohl prägnant und haben ausgezeichnete Leistung.
Aber Haskell ist nicht immun gegen Ausreißer Zuweisungen Bezug auf die Rekursion. Schlecht geschrieben von Haskell-code stack-Probleme auch, wie erwähnt, auf die - Stack overflow Seite auf HaskellWiki.
Haskell nicht über einen call-stack wie prozeduralen Sprachen, also diese Abstürze sind bezeichnet als space leaks.
Z.B. die einfache Haskell-Programm unter
wenn auf meiner Maschine zum laufen — wenn es nicht auf deiner, erhöhen die Obergrenze, bis es funktioniert — die Ergebnisse in der
Ändern Sie das Programm, um
dauert eine Weile ausgeführt, aber schließlich bricht mit dem richtigen Ergebnis. Überlegungen zu einem Haskell-Programm die Verwendung des Platzes kann schwierig sein, für Anfänger. Rooting-out-Raum Lecks mit dem profiler ist ein Thema für fortgeschrittene.
Zusammenfassung
Beide sind hervorragende Sprachen, besonders in Ihren jeweiligen Nischen. Wir Programmierer machen manchmal Fehler. Die Ressourcenknappheit und die Halteproblem immer draußen gegen uns verschworen. Programmiersprachen beschäftigen sich mit diesen Einschränkungen in Ihrer eigenen Art und Weise.
Der Standard-Grenzwert ist zu niedrig, aber angemessen war für die kleineren Maschinen Perl ursprünglich lief auf. Jetzt 100, ist lächerlich, wenn Sie dabei schwere rekursive Arbeit, aber wie Sie sagen, es kann abgestimmt werden. Ich gehe davon aus Haskell hat eine andere Art zu fangen unendliche Rekursion?