LET gegen LET * in Common Lisp
Verstehe ich den Unterschied zwischen LET und LET* (parallele versus sequentielle Bindung), und als eine theoretische Angelegenheit, es macht absolut Sinn. Aber gibt es einen Fall, wo Sie jemals tatsächlich benötigt LASSEN? In allen meinen Lisp-code, ich habe vor kurzem, könnten Sie ersetzen Sie alle LASSEN mit LET* - keine Veränderung.
Edit: OK, ich verstehe warum einige Kerl erfunden LASSEN*, die vermutlich ein makro, Weg zurück, wenn. Meine Frage ist, da LASS* vorhanden ist, gibt es einen Grund für LASSEN zu bleiben? Haben Sie geschrieben, die tatsächliche Lisp-code, wo ein LET* würden nicht so gut funktionieren wie ein normaler LASSEN?
Ich nicht kaufen, das Effizienz-argument. Erste, erkennen von Fällen, in denen LET* kompiliert werden kann, in etwas so effizient wie LASSEN scheint einfach nicht so schwer. Zweitens, es gibt viele Dinge, die in der CL-spec, die einfach nicht scheinen, wie Sie wurden entwickelt, um die Effizienz bei allen. (Wann ist das Letzte mal sah Sie eine SCHLEIFE mit Typ-Deklarationen? Diese sind so hart, um herauszufinden, ive nie gesehen Sie verwendet werden.) Bevor Dick Gabriels Eckpunkte der späten 1980-s, CL war geradezu langsam.
Sieht es aus wie dies ist ein weiterer Fall von rückwärts-Kompatibilität: mit bedacht, niemand wollte das Risiko gegen so etwas Grundsätzliches wie LASSEN. Das war meine Vermutung, aber es ist tröstlich zu hören, dass niemand eine dumm-einfache Fall, den ich fehlte, wo LASSEN Sie einen Haufen Dinge, die lächerlich einfacher als LET*.
InformationsquelleAutor der Frage Ken | 2009-02-16
Du musst angemeldet sein, um einen Kommentar abzugeben.
LET
sich nicht um eine echte primitive in einem Funktionale Programmierspracheda kann es ersetzt mitLAMBDA
. Wie diese:ist ähnlich
Aber
ist ähnlich
Können Sie sich vorstellen, welche ist die einfachere Sache.
LET
und nichtLET*
.LET
code macht das Verständnis einfacher. Man sieht ein Bündel von Bindungen, und man kann Lesen jede Bindung individuell ohne die Notwendigkeit zu verstehen, die oben-unten/Links-rechts-Fluss der 'Effekte' (rebindings). MitLET*
Signale für den Programmierer (der, der liest-code), der die Bindungen sind nicht unabhängig, aber es gibt eine Art top-down-flow - die Dinge verkompliziert.Common Lisp hat die Regel, dass die Werte für die Bindungen in
LET
berechnet werden, die von Links nach rechts. Wie die Werte für einen Aufruf der Funktion ausgewertet werden - von Links nach rechts. AlsoLET
ist das konzeptionell einfachere Erklärung, und es sollte standardmäßig verwendet werden.Arten in
LOOP
? Werden ziemlich oft verwendet. Es gibt einige primitive Formen von Typ-Deklaration, die einfach zu merken sind. Beispiel:Oben deklariert die variable
i
einefixnum
.Richard P. Gabriel veröffentlicht sein Buch über Lisp benchmarks 1985 und damals diese benchmarks wurden auch mit nicht-CL Lisps. Common Lisp selbst war brand new in 1985 - die CLtL1 Buch, in dem beschrieben wird, die Sprache war gerade in 1984 veröffentlicht. Kein Wunder, dass die Implementierungen waren nicht sehr optimiert zu dieser Zeit. Die Optimierungen umgesetzt wurden, waren im Grunde die gleichen (oder weniger), dass die Implementierungen vor hatte (wie MacLisp).
Aber für
LET
vs.LET*
der Hauptunterschied ist, dass code mitLET
ist einfacher zu verstehen für uns Menschen, da die Bindung Klauseln sind unabhängig von einander - vor allem, weil es ist schlechter Stil, nutzen Sie die Links auf der rechten evaluation (nicht-Einstellung von Variablen als Nebeneffekt).InformationsquelleAutor der Antwort Rainer Joswig
Du nicht müssen LASSEN, aber Sie normalerweise wollen.
LASSEN vermuten, dass Sie tun, standard-parallel-Bindung mit nichts kniffliges Los. LASSEN* induziert Einschränkungen auf den compiler und suggeriert dem Benutzer, dass es gibt einen Grund, dass die sequentielle Bindungen erforderlich sind. In Bezug auf die StilLASSEN ist besser, wenn Sie nicht brauchen, die zusätzliche Einschränkungen LASSEN*.
Kann es effizienter nutzen LASSEN als LET* (je nach compiler, optimizer, usw.):
(Die obigen Punkte gelten für Schema, ein weiterer LISP-Dialekt. clisp kann abweichen).
InformationsquelleAutor der Antwort Mr Fooz
Komme ich Lager erfundene Beispiele. Vergleichen Sie das Ergebnis dieser:
mit dem Ergebnis der Ausführung:
InformationsquelleAutor der Antwort Logan Capaldo
In LISP, gibt es oft ein Wunsch, den schwächsten Konstrukte. Einige style-guides wird Ihnen sagen, zu verwenden
=
eher alseql
wenn Sie wissen, dass die verglichenen Elemente numerisch sind, zum Beispiel. Die Idee ist oft angeben, was du meinst, anstatt Programm, den computer effizient.Dennoch kann es zu tatsächlichen Verbesserungen der Effizienz, die sagen nur das, was Sie bedeuten, und nicht auf die Verwendung stärkerer Konstrukte. Wenn Sie Initialisierungen mit
LET
Sie können parallel ausgeführt werden, währendLET*
Initialisierungen werden sequentiell ausgeführt. Ich weiß nicht, ob alle Implementierungen tatsächlich tun, aber einige können gut in die Zukunft.InformationsquelleAutor der Antwort David Thornley
Ich war vor kurzem das schreiben einer Funktion von zwei Argumenten, in denen der Algorithmus ist am deutlichsten, wenn wir wissen, welches argument größer ist.
Angenommen, dass
b
war größer, wenn wir genutzt hattenlet*
hätten wir versehentlicha
undb
auf den gleichen Wert.InformationsquelleAutor der Antwort Samuel Edwin Ward
Den wichtigsten Unterschied in der Gemeinsamen Liste zwischen LET und LET* ist, dass Symbole, die damit gebunden sind, die parallel und in LET* - gebunden sind, der Reihe nach. Verwenden LASSEN, nicht zulassen, dass die init-Formen parallel ausgeführt werden, noch erlaubt es die Reihenfolge der init-Formen geändert werden. Der Grund ist, dass Common Lisp erlaubt es Funktionen zu Nebenwirkungen haben. Also, die Reihenfolge der Auswertung ist wichtig und ist immer von Links nach rechts im Formular. So, in LASSEN, die init-Formen werden zuerst ausgewertet, von Links nach rechts, dann sind die Bindungen schuf,
Links nach rechtsparallel. In LET* - die init-form ist ausgewertet und dann an das symbol in der Reihenfolge, von Links nach rechts.CLHS: Special-Operator LET, LET* -
InformationsquelleAutor der Antwort tmh
gehe ich einen Schritt weiter und nutzen binden vereint
let
let*
multiple-value-bind
destructuring-bind
etc., und es ist sogar erweiterbar.allgemein nutze ich gerne die "schwächste Konstrukt", aber nicht mit
let
& Freunde, weil Sie einfach geben Sie Rauschen zu dem code (Subjektivität Warnung! keine Notwendigkeit, zu versuchen, überzeugt mich vom Gegenteil...)InformationsquelleAutor der Antwort Attila Lendvai
Vermutlich durch die Verwendung
let
hat der compiler mehr Flexibilität, um die Reihenfolge der code, vielleicht für Raum oder Verbesserungen in der Geschwindigkeit.Stilistisch, über die parallel-Bindungen zeigt die Absicht, dass die Bindungen werden zusammen gruppiert; dies wird manchmal bei Beibehaltung der dynamischen Bindungen:
InformationsquelleAutor der Antwort Doug Currie
Natürlich, das funktionieren würde:
Und so:
Aber es ist der Gedanke, der zählt.
InformationsquelleAutor der Antwort Zorf
Der OP erkundigt sich "jemals tatsächlich benötigt werden LASSEN"?
Wenn Common Lisp erstellt wurde, dort war ein Boot Belastung von bestehenden Lisp-code in verschiedenen Dialekten. Die kurze, die Leute, die entworfen, Common Lisp übernommen zu erstellen, ein Dialekt von Lisp, common ground. Sie "brauchte", um es einfach und attraktiv für Anschluss vorhandener code in Common Lisp. Verlassen LASSEN oder LASSEN* aus der Sprache könnte dazu gedient haben, einige andere Tugenden, aber es ignoriert hätte, die Schlüssel Ziel.
Ich benutzen, LASSEN Sie den Vorzug zu LASSEN,* denn es sagt dem Leser etwas darüber, wie die Daten fließen entfaltet. In meinem code, zumindest, wenn Sie sehen, LASSEN* Sie wissen, dass die Werte gebunden, die früh eingesetzt werden, in einer späteren Bindung. Kann ich "brauchen" zu tun, Nein; aber ich denke, es ist hilfreich. Das sagte ich gelesen habe, selten, code, Standardwerte zu LASSEN* und das Aussehen LASSEN Sie Signale, dass der Autor wirklich wollte. I. e. zum Beispiel tauschen Bedeutung von zwei vars.
Es ist fraglich Szenario, dass die Ansätze, die den tatsächlichen Bedarf". Es entsteht mit Makros. Dieses makro:
funktioniert besser als
seit (M2 c b a) ist nicht zur Arbeit zu gehen. Aber diese Makros sind ziemlich schlampig für verschiedene Gründe; damit untergräbt die "tatsächliche Bedarf" - argument.
InformationsquelleAutor der Antwort Ben Hyde
Neben Rainer Joswig ist Antwort, und aus einer puristischen oder theoretischer Sicht. Lassen Sie & Lassen Sie* stellen Sie zwei Programmierparadigmen; funktionale und sequentielle beziehungsweise.
Ab, warum soll ich einfach weiter mit Let'*, anstatt Lassen Sie, gut, du nimmst den Spaß von mir aus nach Hause kommen und denken in reinen funktionalen Sprache, im Gegensatz zu sequentiellen Sprache, wo ich verbringen die meiste mein Tag der Arbeit mit 🙂
InformationsquelleAutor der Antwort emb
Mit Lassen Sie die parallele Bindung,
Und mit Let* serielle Bindung,
InformationsquelleAutor der Antwort Floydan
Den
let
operator führt eine einheitliche Umgebung für alle Bindungen, die es gibt.let*
zumindest begrifflich (und wenn wir ignorieren Erklärungen für einen moment) führt mehrere Umgebungen:Ist zu sagen:
ist wie:
So, in einem gewissen Sinne
let
ist primitiver, in der Erwägung, dasslet*
ist ein syntaktischer Zucker für das schreiben einer Kaskade vonlet
-s.Aber nie Verstand. (Und ich gebe Rechtfertigung später für warum sollten wir "never mind"). Tatsache ist, es gibt zwei Operatoren, und in "99%" der code, es spielt keine Rolle, welches Sie benutzen. Der Grund zu bevorzugen
let
überlet*
ist einfach, dass es nicht die*
baumelt am Ende.Wann immer Sie haben zwei Operatoren, und man hat einen
*
baumeln auf Ihren Namen verwenden, die man ohne die*
wenn es in dieser situation, zu halten Ihr code weniger hässlich.Alles das ist dort ist zu ihm.
Dass gesagt wird, ich vermute, dass, wenn
let
undlet*
tauschten Ihre Bedeutungen, würde es wahrscheinlich eher selten nutzenlet*
als jetzt. Die serielle Bindungsverhalten wird sich nicht die Mühe die meisten code, der nicht erforderlich ist es: nur selten würden dielet*
müssen verwendet werden, um die Anfrage parallel Verhalten (und solche Situationen können auch behoben werden, durch Umbenennung von Variablen, um Schattenbildung zu vermeiden).Nun für die versprochene Diskussion. Obwohl
let*
konzeptionell stellt mehrere Umgebungen, es ist sehr einfach zu kompilieren dielet*
konstruieren, so dass einer einzelnen Umgebung generiert wird. Also auch wenn (t-zumindest wenn wir ignorieren, ANSI CL Erklärungen), es ist eine algebraische Gleichheit, dass eine einzelnelet*
entspricht mehreren verschachteltenlet
-s, die machtlet
Blick primitiver, es gibt keinen Grund, t tatsächlich zu erweiternlet*
inlet
um es zu kompilieren, und sogar eine schlechte Idee.Eins noch: beachten Sie, dass Common Lisp ist
lambda
tatsächlich verwendetlet*
-wie Semantik! Beispiel:hier, die
x
init-form füry
greift auf die früherex
parameter, und ähnlich(+1 y)
bezieht sich auf die früherey
optional. In diesem Bereich der Sprache, eine klare Präferenz für die sequenzielle Sichtbarkeit in der Bindung zeigt, durch. Es wäre weniger sinnvoll, wenn die Formulare in einemlambda
nicht sehen konnte, die Parameter; einen optionalen parameter konnte nicht lapidar vorgeschlagen in Bezug auf den Wert des vorherigen parameters.InformationsquelleAutor der Antwort Kaz
Unter
let
alle Variablen initialisieren von Ausdrücken finden Sie genau die gleichen lexikalischen Umgebung: das, was Sie umgibt, dielet
. Wenn solche Ausdrücke vorkommen, zu erfassen lexikalische Verschlüsse, Sie können alle teilen die gleiche Umgebung, Objekt.Unter
let*
jeder Initialisierung Ausdruck ist in einer anderen Umgebung. Für jeden weiteren Ausdruck, der Umwelt erweitert werden muss, um eine neue zu erstellen. Zumindest in der abstrakten Semantik, wenn die Verschlüsse gefangen sind, haben Sie verschiedenen Umwelt-Objekte.Einen
let*
muss gut optimiert, um den Zusammenbruch der unnötige Umwelt-Erweiterungen, um geeignet als Alltags-Ersatz fürlet
. Es muss ein compiler funktioniert, welche Formen sind, was den Zugriff auf und konvertiert dann alle selbstständigen in größeren, kombiniertenlet
.(Dies gilt auch, wenn
let*
ist nur ein makro-operator erzeugt, kaskadiertelet
Formen; die Optimierung erfolgt auf diese kaskadiertelet
s).Implementieren zu können
let*
als eine einzelne naivelet
mit versteckten Variablen Zuordnungen zu tun, die Initialisierungen, weil der Mangel des Gültigkeitsbereichs wird offenbart werden:Wenn diese aktiviert ist in
wird es nicht funktionieren, in diesem Fall; die innere
b
ist die Abschattung der äußerenb
so dass wir am Ende das hinzufügen von 2 bisnil
. Diese Art der transformation kann getan werden, wenn wir alpha-benennen Sie alle diese Variablen. Die Umgebung ist dann schön abgeflacht:Dafür brauchen wir betrachten den debug-support; wenn der Programmierer Schritte in diese lexikalischen scope mit einem debugger, wollen wir Ihnen den Umgang mit
#:g01
statta
.Also im Grunde
let*
ist das komplizierte Konstrukt, welches optimiert werden soll gut zu führen sowielet
in den Fällen, wenn es könnte zu reduzieren, umlet
.Allein würde es nicht rechtfertigen, die Begünstigung
let
überlet*
. Nehmen wir an, wir haben einen guten compiler; warum nichtlet*
alle Zeit?Als Allgemeines Prinzip, sollten wir zugunsten höher-level-Konstrukte, die uns produktiv und verringern Sie Fehler über Fehler-anfällig untere-level-Konstrukte und setzen soweit wie möglich auf gute Implementierungen der High-level-Konstrukte, so dass wir nur selten zu opfern, deren Verwendung aus performance-Gründen. Deshalb arbeiten wir in einer Sprache wie Lisp in den ersten Platz.
Dass die Argumentation nicht schön gelten für
let
versuslet*
weillet*
ist nicht eindeutig ein höheres Niveau der Abstraktion relativ zulet
. Sind Sie etwa "gleicher Ebene". Mitlet*
können Sie die Einführung eines bug-ist dieses Problem gelöst, durch einfaches Umschalten auflet
. Und Umgekehrt.let*
ist wirklich nur ein mildes syntaktische Zucker für visuell einstürzendenlet
nisten, und nicht eine wichtige neue Abstraktion.InformationsquelleAutor der Antwort Kaz
Ich vor allem nutzen LASSEN, es sei denn, ich specifgically müssen LASSEN*, aber manchmal Schreibe ich code, der explizit muss LASSEN, in der Regel, wenn dabei verschiedene (meist komplizierte) ausfallenden. Leider, ich habe keine praktische code-Beispiel zur hand.
InformationsquelleAutor der Antwort Vatine
wer hat Lust, neu zu schreiben letf vs letf* wieder? die Anzahl der unwind-protect Anrufe?
leichter zu optimieren sequentiellen Bindungen.
vielleicht ist die Auswirkungen der env?
ermöglicht Fortsetzungen mit dynamischen Umfang?
manchmal (let (x y z)
(setq z 0
y 1
x (+ (setq x 1)
(prog1
(+ x y)
(setq x (1 - x)))))
(Werte () ))
[ Ich denke, das funktioniert ] Punkt ist, einfacher ist es leichter zu Lesen manchmal.
InformationsquelleAutor der Antwort steve