Ist Rekursion ein Beispiel dafür, dass man beim Programmieren „zu schlau“ ist?

8

Ich habe mehrere Bücher gelesen und durch Erfahrung gelernt, dass es nicht wünschenswert ist, Code so weit zu optimieren, dass er unergründlich ist, oder eine extrem schnelle, aber äußerst komplexe Lösung für ein Problem zu finden, wenn Sie in Teams arbeiten oder sogar wenn Sie arbeiten selbst und müssen einige Zeit später Ihre clevere Lösung verstehen.

Meine Frage ist, sollte Rekursion auf die gleiche Weise behandelt werden? Versteht der durchschnittliche Programmierer die Rekursion leicht und sollte sie daher ungestraft verwenden, oder versteht der durchschnittliche Programmierer die Rekursion nicht sehr gut und sollte sie aus Gründen der Gesamtproduktivität des Teams fernhalten?

Ich weiß, dass es einfache Antworten gibt: "Jeder Programmierer, der die Rekursion nicht versteht, ist kein Körnchen Salz wert, also mach dir keine Sorgen", aber ich habe mich gefragt, ob Sie alle eine echte Erfahrung haben, die Sie gerne hätten Teilen, das würde das Thema mehr beleuchten als die Meinung, die ich gerade erwähnt habe.

Macy Abbey
quelle
2
Diese Frage ist programmers.stackexchange.com/questions/24997/… ziemlich ähnlich, die auch heute gestellt wurde. Einige gute Antworten dort.
Nicole
4
Wie können Sie ein durchschnittlicher Programmierer sein, wenn Sie die Rekursion nicht verstehen? (Beachten Sie den Unterschied zu "jedem Programmierer")

Antworten:

22

Versteht der durchschnittliche Programmierer die Rekursion leicht und sollte sie daher ungestraft verwenden, oder versteht der durchschnittliche Programmierer die Rekursion nicht sehr gut und sollte sie aus Gründen der Gesamtproduktivität des Teams fernhalten?

Ich würde sagen, dass der durchschnittliche Programmierer die Rekursion perfekt versteht. Wenn der Programmierer einen Abschluss in Informatik oder Softwaretechnik gemacht hat, ist dies in der Tat so gut wie garantiert. Zugegeben, es gibt einige sehr unterdurchschnittliche Programmierer, aber Sie möchten sie nicht in Ihrem Team haben.

In diesem Fall besteht die Unterscheidung zwischen einem durchschnittlichen und einem guten Programmierer darin, zu wissen, wann die Rekursion verwendet werden muss und wann nicht. Und das hängt davon ab, welches Problem gelöst wird UND welche Sprache verwendet wird, um es zu lösen.

  • Wenn Sie eine funktionale Programmiersprache verwenden, ist die Rekursion eine natürliche und effiziente Lösung für eine Vielzahl von Problemen. (Regeln zur Optimierung der Schwanzrekursion!)

  • Wenn Sie eine OO oder eine einfache prozedurale Sprache verwenden, kann die Rekursion ineffizient und aufgrund von Stapelüberläufen problematisch sein. In einigen Fällen würden Sie also eher eine iterative als eine rekursive Lösung wählen. In anderen Fällen ist die rekursive Lösung jedoch so viel einfacher und eleganter, dass die (möglicherweise effizientere) iterative Lösung die "zu clevere" wäre. (Wenn das Problem beispielsweise das Zurückverfolgen, Bauen oder Gehen von Bäumen / Grafiken usw. erfordert, ist die Rekursion oft einfacher.)

Stephen C.
quelle
1
+1 Für Kommentar zur Schwanzrekursion. Oft führt ein Versuch, die Rekursion zu entfernen, zu vielen Stapelstrukturen und Schleifen in Ihrem Code, die die Call-Stack-Komponente effektiv replizieren.
Orbling
1
@Orbling - das stimmt, aber ein Stapel, der mithilfe eines Arrays oder einer Arrayliste implementiert wurde, ist wahrscheinlich skalierbarer als der Aufrufstapel eines Threads. Zumal Thread-Call-Stacks in vielen Sprachimplementierungen nicht wachsen können.
Stephen C
Ja, es ist ein ewiges Problem des rekursiven Funktionsdesigns. Idealerweise eher ein Problem für die Sprachdesigner als für den Programmierer.
Orbling
" Zugegeben, es gibt einige sehr unterdurchschnittliche Programmierer ": OK, es muss nur gesagt werden ... Per Definition sind die Hälfte der Programmierer unterdurchschnittlich und die Hälfte "sehr unterdurchschnittlich".
Lawrence Dol
@ LawrenceDol - Ich habe noch nie jemanden behaupten sehen, dass "sehr" die Hälfte von allem bedeutet. Was ist Ihre Quelle für Ihre Definition? (Dies muss auch gefragt werden, da Pedanterie nur dann einen Wert hat, wenn sie sachlich begründet ist.)
Stephen C
42

Einige Probleme sind natürlich rekursiv. In diesen Fällen eine iterative Lösung zu finden, kann tatsächlich klobiger und komplexer sein als rekursive. Ein gutes Beispiel ist jeder Algorithmus, der eine hierarchische Baumstruktur durchlaufen muss, was bei der Programmierung keine ungewöhnliche Aufgabe ist.

TL; DR-Version: Nein.

Bobby Tische
quelle
9
Einverstanden. Es ist gefährlicher, Dinge als "schädlich" zu bezeichnen und bis zum Äußersten zu gehen, um sie zu vermeiden - was immer verwirrender und schwieriger wird, weiterzumachen.
Heretik
11

Rekursion ist ein Grundprinzip in den meisten funktionalen Programmiersprachen. Die Iteration (Schleife) in funktionalen Sprachen erfolgt normalerweise durch Rekursion.

Funktionale Sprachen haben in letzter Zeit eine Renaissance erlebt, da mehr Prozessorkerne elegant gehandhabt werden müssen. Funktionale Sprachen helfen dabei, diese Art von Parallelität zu erreichen, indem sie Möglichkeiten bieten, Ihr Programm besser zu verstehen, ohne die Komplexität beim Sperren veränderlicher Strukturen zu beeinträchtigen.

Robert Harvey
quelle
+1 Guter Kommentar. Obwohl ich nicht sicher bin, ob "eine Renaissance" völlig richtig ist oder der Grund für Multi-Cores. Sie waren schon immer ein beliebtes Paradigma für diejenigen, die über sie Bescheid wussten, aber ihre Verbreitung im Mainstream war nie großartig. Funktionale Sprachen sind gerade mehr Mainstream geworden, würde einen ganzen Faden brauchen, um dieses Problem zu lösen. ( programmers.stackexchange.com/questions/20036/…, die Sie bereits gelesen haben)
Orbling
11

Einige Probleme, wie das Gehen einer Baumstruktur (z. B. Gehen einer gesamten Verzeichnisstruktur im Gegensatz zum Suchen nach einem bestimmten B-Baum-Knoten), eignen sich ideal für die Verwendung der Rekursion. Die nicht rekursiven Äquivalente erschweren häufig die Verwaltung Ihres eigenen Stapels.

In diesen Fällen ist die Rekursion die beste, einfachste und am einfachsten zu verstehende Lösung.

Lawrence Dol
quelle
7

Ich würde sagen, Rekursion verwenden, wo es angebracht ist, aber immer nach Wegen suchen, um explizite Rekursion zu vermeiden.

Wenn Sie beispielsweise feststellen, dass eine Baumstruktur manuell durchlaufen werden muss, sollten Sie die Rekursion verwenden. Wenn jemand dumm genug ist, eine rekursive Baumdurchquerung nicht zu verstehen, wird er weder Kopf noch Schwanz der iterativen Version machen.

Eine bessere Option ist jedoch die Verwendung eines Iterators, der die Rekursion bis zu dem Punkt abstrahiert, an dem Sie nur noch "Ich führe diese Operation für alles im Baum aus" sehen.

Anon.
quelle
1
Richtig (und +1), aber natürlich muss jemand die Abstraktion schreiben.
Lawrence Dol
1
Wie @Software Monkey sagt, sollte es normalerweise immer noch rekursiv codiert werden, aber die Abstraktion zu einem Iterator zur "Verwendung" ist in Ordnung.
Orbling
1
+1 für den Punkt, dass jemand, der die rekursive Version höchstwahrscheinlich nicht versteht, die iterative Version nicht versteht. Leider würde diese Person auch nicht erkennen, dass sie die iterative Version nicht versteht.
Larry Coleman
3

Rekursion ist vom Standpunkt der Code-Sauberkeit der einfachste Mechanismus. Wenn Geschwindigkeit absolut entscheidend ist, können Sie möglicherweise ein 2D-Array von Problemparametern verwenden. Beim Laichen eines Tochterprozesses wird dann einfach ein anderes Element an das Array angehängt. Ich habe einmal einen tridiagonalen Assembler-Löser gemacht, das war damals eine große Sache. Der pro Instanz benötigte Kontext betrug 8 Wörter pro Ebene, und jedes Teilproblem war ein Drittel so groß wie das vorherige. Diese "Bibliothek" war nur deshalb beliebt, weil sie alle anderen Implementierungen auf dem Markt übertroffen hat. Dies ist jedoch eine ziemlich seltene Situation bei der Programmierung, sodass Sie nicht der "vorzeitigen Optimierung" erliegen müssen, nur weil diese Lösung verfügbar ist. Offensichtlich ist es für ein paar Dinge ein schrecklicher Overkill, wie das Beispiel der Rekursion 101 "Berechne die Fakultät". Aber für die meisten Apps

Ich habe eine einfache Rechtschreibprüfung, die ich für eine App verwende (wo ich Hinweise zur Korrektur kleinerer Rechtschreibfehler geben möchte), bei der ich den "Abstand" zwischen zwei Zeichenfolgen berechne, sodass Löschungen und Hinzufügungen zulässig sind. Dies führt zu einer möglicherweise großen Baumstruktur, und die Zweige werden abgeschnitten, da wir uns nur um enge Übereinstimmungen kümmern. Mit Rekursion sind es vielleicht zwanzig Codezeilen (ich habe sowohl Fortran- als auch C-Versionen). Ich denke, es wäre sonst chaotisch. Es war viel einfacher, die Überprüfung zu programmieren / zu debuggen, als an diesen Baum zu denken!

Omega Centauri
quelle
0

Ich verstehe es, aber ich habe es nie explizit verwendet. Ich denke, jeder, der sich selbst als Programmierer bezeichnet, sollte wissen oder lernen, was es ist, genauso wie er wissen muss, was der Stapel und der Heap sind. Hey, jeder muss wissen, wie Min / Max auf Tic-Tac-Toe funktioniert, über ausgefallene Rekursion.

Johnny
quelle