Warum sind Funktionen in Ocaml/F#, die nicht standardmäßig rekursiv?
Warum ist es, dass die Funktionen in F# und Ocaml (und vielleicht auch andere Sprachen) sind nicht standardmäßig rekursiv?
In anderen Worten, warum hat die Sprache die Designer sich entscheiden, es war eine gute Idee, um explizit zu machen, Sie geben rec
in einer Erklärung wie:
let rec foo ... = ...
und nicht die Funktion rekursive Funktion standardmäßig? Warum die Notwendigkeit für eine explizite rec
Konstrukt?
- Siehe auch stackoverflow.com/questions/3739628/...
Du musst angemeldet sein, um einen Kommentar abzugeben.
Den französischen und britischen Nachfahren der ursprünglichen ML aus verschiedenen Entscheidungen und Ihren Entscheidungen waren geerbt durch die Jahrzehnte zu modernen Varianten. Dies ist also nur Vermächtnis, sondern wirkt sich auf Redewendungen in diesen Sprachen.
Funktionen sind nicht standardmäßig rekursiv in der französischen CAML-Familie von Sprachen (einschließlich OCaml). Diese Auswahl macht es leicht zu ersetzen-Funktion (und variable) Definitionen mit
let
in diese Sprachen, weil Sie können finden Sie auf der vorherigen definition in den Körper einer neuen definition. F# geerbt syntax von OCaml.Beispielsweise die Installation der Funktion
p
bei der Berechnung der Shannon Entropie einer Sequenz in OCaml:Beachte, wie das argument
p
um die höher -, umshannon
Funktion ist abgelöst durch einen anderenp
in der ersten Zeile des Körpers und dann nochp
in der zweiten Zeile des Körpers.Umgekehrt, die britische SML Zweig der ML-Familie von Sprachen, nahm der andere die Wahl und die SML ' s
fun
-gebundenen Funktionen sind standardmäßig rekursiv. Wenn die meisten Funktionsdefinitionen müssen nicht den Zugriff auf frühere Bindungen Ihrer Funktion name, dies führt zu einfacherem code. Jedoch abgelöst Funktionen sind die Verwendung verschiedener Namen (f1
,f2
etc.) das belastet den Rahmen und macht es möglich, versehentlich aufrufen, die falsche "version" einer Funktion. Gibt es nun eine Diskrepanz zwischen implizit-rekursivefun
-gebunden-Funktionen und nicht-rekursiveval
-gebundene Funktionen.Haskell macht es möglich, Rückschlüsse auf die Abhängigkeiten zwischen den Definitionen durch eine Beschränkung auf rein. Das macht toy samples look einfacher, aber kommt an ein Grab Kosten an anderer Stelle.
Beachten Sie, dass die gegebenen Antworten von Ganesh und Eddie sind rote Heringe. Sie erklärt, warum Gruppen von Funktionen können nicht gestellt werden in einem riesigen
let rec ... and ...
denn es wirkt bei Typ-Variablen verallgemeinert. Das hat nichts zu tun mitrec
als Standard in SML, aber nicht OCaml.Einen entscheidenden Grund für die explizite Verwendung von
rec
ist zu tun mit Hindley-Milner type-Inferenz, die zugrunde liegt, alle staticly typisierte funktionale Programmiersprachen (wenn auch verändert und erweitert, die in verschiedenen weisen).Wenn Sie eine definition
let f x = x
ist, würde man erwarten, dass es zu einem Typ -'a -> 'a
und anwendbar auf verschiedene'a
Arten an verschiedenen Punkten. Aber ebenso, wenn Sie schreibenlet g x = (x + 1) + ...
ist, würde man erwarten, dassx
behandelt zu werden wie einint
im rest des Körpers vong
.Den Weg, die Hindley-Milner-Inferenz, beschäftigt sich mit dieser Unterscheidung ist durch eine explizite Verallgemeinerung Schritt. An bestimmten Punkten bei der Bearbeitung von Programm, das Typ-system Stoppt und sagt "ok, die Typen von diesen Definitionen verallgemeinert werden an diesem Punkt, so dass, wenn jemand nutzt Sie, alle freien Variablen in Ihrem Typ frisch instanziiert, und somit keinen Konflikt mit anderen Nutzungen dieser definition."
Es stellt sich heraus, dass der sinnvolle Ort, um diese Verallgemeinerung ist nach der überprüfung eine sich gegenseitig rekursive Funktionen. Alle früheren, und Sie werden verallgemeinern, zu viel, führt zu Situationen, in denen Arten könnten tatsächlich kollidieren. Später, und Sie werden verallgemeinern zu wenig, so dass Definitionen, die nicht verwendet werden können, mit mehreren Typ-Instanzen.
So, gegeben, dass die Typ-checker muss wissen, über welche Mengen von Definitionen, die sich gegenseitig rekursiv, was kann Sie tun? Eine Möglichkeit ist es, einfach eine Abhängigkeitsanalyse auf alle Definitionen in einem Bereich, und ordnen Sie Sie in den kleinsten Gruppen möglich. Haskell tatsächlich tut dies, aber in Sprachen wie F# (und OCaml-und SML), die frei von Nebenwirkungen ist dies eine schlechte Idee, weil es möglicherweise neu ordnen die Nebenwirkungen zu. Anstatt also fragt er den Benutzer explizit die Marke, die Definitionen sind wechselseitig rekursiv ist, und somit durch Erweiterung, wo die Generalisierung erfolgen soll.
let f = (side_effect; fun x => x)
, also Definitionen, die nicht Funktionsdefinition syntax? Denn ich gehe davon aus die meisten Menschen wäre in Ordnung, nur mit Funktionen, die definiert werden mit Hilfe der Funktion definition syntax, die es erlaubt, wechselseitig rekursiv. Das ist, wie die meisten anderen Sprachen arbeiten.rec
aber SML nicht ein offensichtliches Gegenbeispiel. Wenn Typ-Inferenz wurden die Frage nach den Gründen, die Sie beschreiben, OCaml und SML konnte nicht gewählt haben, verschiedene Lösungen, wie Sie Taten. Der Grund ist natürlich, dass Sie redenand
um Haskell relevant.Gibt es zwei wichtige Gründe, warum dies ist eine gute Idee:
Ersten, wenn Sie rekursiver Definitionen, dann können Sie nicht beziehen sich auf eine frühere Bindung an einen Wert mit dem gleichen Namen. Dies ist oft ein nützliches idiom, wenn Sie etwas wie die Erweiterung eines bestehenden Moduls.
Zweite, rekursive Werte, und vor allem Sätze von wechselseitig rekursiven Werte, sind viel schwerer zu Grunde geht, dann sind Definitionen, die gehen in Ordnung, jede neue definition Gebäude auf der Spitze von dem, was wurde bereits definiert. Es ist schön, wenn das Lesen von code zu garantieren, dass, mit Ausnahme der Definitionen explizit gekennzeichnet als rekursive, neue Definitionen beziehen sich nur auf die vorherigen Definitionen.
Einige Vermutungen:
let
ist nicht nur zum binden von Funktionen, aber auch andere normale Werte. Die meisten Formen von Werte dürfen nicht rekursiv sein. Bestimmte Formen rekursiver Werte sind erlaubt (z.B. Funktionen, faul, Ausdrücke, usw.), so braucht es eine explizite syntax, um dies anzugeben.let
Konstrukt ist ähnlich derlet
Konstrukt in Lisp und Scheme; die nicht-rekursive. Es gibt eine separateletrec
- Konstrukt in Scheme für rekursive let ' sGegeben:
Vergleichen:
Mit dabei:
Erstere definiert
f
zur Anwendung der zuvor definiertenf
auf das Ergebnis der Anwendungg
zua
. Letztere definiertf
loop forever anwendeng
zua
, die in der Regel nicht, was Sie wollen in ML Varianten.Sagte, es ist eine Sprache, die designer-Stil-Ding. Nur mit ihm zu gehen.
Ein großer Teil davon ist, dass es gibt dem Programmierer mehr Kontrolle über die Komplexität der lokalen Bereiche. Das Spektrum der
let
,let*
undlet rec
bieten mehr Leistung und Kosten.let*
undlet rec
im wesentlichen die verschachtelten Versionen der einfachenlet
, also entweder man ist teurer. Diese Einstufung ermöglicht es Ihnen, micromanage die Optimierung von Programm, wie Sie können wählen, welche Ebene lassen, die Sie für die Aufgabe zur hand. Wenn Sie nicht brauchen, Rekursion oder die Fähigkeit zu finden, um bisherige Bindungen, dann können Sie zurückgreifen auf eine einfache lassen, um ein bisschen sparen von Leistung.Es ist ähnlich wie bewertet die Gleichheit von Prädikaten, die im Schema. (d.h.
eq?
,eqv?
undequal?
)