Ich bin wirklich neu in Haskell (Eigentlich sah ich "Real World Haskell" von O'Reilly und dachte "hmm, ich denke, ich werde gestern die funktionale Programmierung lernen") und ich frage mich: Ich kann den Konstruktoperator verwenden Füge ein Element am Anfang einer Liste hinzu:
%Vor%Ich habe versucht, einen Beispieldatentyp zu erstellen, den ich im Buch gefunden habe, und dann damit zu spielen:
%Vor%etc ... es wiederholt sich einfach für immer, ist dies, weil es faul Auswertung verwendet?
- BEARBEITEN -
okay, also wird es in meinen Kopf gehämmert, das lost order_list = CashOnDelivery: order_list fügt CashOnDelivery nicht zur ursprünglichen order_list hinzu und setzt dann das result auf order_list, sondern rekursiv und erstellt eine unendliche Liste, die CashOnDelivery für immer hinzufügt zum Anfang von selbst. Natürlich erinnere ich mich, dass Haskell eine funktionale Sprache ist und ich den Wert der ursprünglichen order_list nicht ändern kann, also was soll ich für eine einfache "Wende das bis zum Ende (oder Anfang, was auch immer) dieser Liste?" Erstellen Sie eine Funktion, die eine Liste und BillingInfo als Argumente akzeptiert und dann eine Liste zurückgibt?
- EDIT 2 -
Nun, basierend auf all den Antworten, die ich bekomme, und dem Mangel, ein Objekt als Referenz übergeben zu können und Variablen zu mutieren (wie ich es gewohnt bin) ... Ich denke, ich habe gerade diese Frage gestellt zu früh und dass ich mich wirklich weiter in das funktionale Paradigma vertiefen muss, bevor ich wirklich die Antworten auf meine Fragen verstehen kann ... Ich denke, was ich suchte, war, wie man eine Funktion oder etwas schreibt, eine Liste und einen Gegenstand nimmt und eine Liste unter dem gleichen Namen zurückgeben, so dass die Funktion mehr als einmal aufgerufen werden konnte, ohne jedes Mal den Namen zu ändern (als ob es tatsächlich ein Programm wäre, das einer Auftragsliste tatsächliche Aufträge hinzufügen würde und der Benutzer dies nicht hätte) an jedes Mal einen neuen Namen für die Liste zu denken, sondern ein Element an die gleiche Liste anzuhängen).
Sie tun dies:
%Vor% Es ist wichtig zu beachten, dass Sie nicht nur einen CashOnDelivery
-Eintrag zu Ihrer ersten order_list
hinzufügen. Sie definieren eine neue Variable order_list
, die nichts mit der ersten zu tun hat. Dies ist eine rekursive Definition, der order_list
auf der rechten Seite bezieht sich auf den order_list
, den Sie links definieren, nicht auf den in der vorherigen Zeile definierten. Wegen dieser Rekursion erhalten Sie eine unendliche Liste.
Ich vermute, du wolltest wirklich so etwas machen:
%Vor% Als ein sich erholender ML-Programmierer werde ich ständig von diesem erwischt . Es ist einer der wenigen Ärger in Haskell, dass man einen Namen nicht einfach in let
oder where
Klauseln neu binden kann. Wenn Sie let
oder where
verwenden möchten, müssen Sie neue Namen erfinden. Wenn Sie in der load-eval-print-Schleife der obersten Ebene nur einen Namen gleichzeitig binden möchten, haben Sie keine andere Wahl. Wenn Sie jedoch Konstrukte verschachteln möchten, können Sie die Schreibweise do
mit der Identitätsmonade missbrauchen:
Ja, dieser Code ist verabscheuungswürdig --- und es ist für dieses Beispiel nicht wert - aber wenn ich eine lange Liste von Rebindings habe, könnte ich darauf zurückgreifen, damit ich nicht 5 oder 6 bedeutungslose Variationen erfinden muss derselbe Name.
Antwort auf Frage bearbeiten:
Also, was soll ich für eine einfache "tack diese bis zum Ende (oder Anfang, was auch immer) dieser Liste?" Erstellen Sie eine Funktion, die eine Liste und BillingInfo als Argumente akzeptiert und dann eine Liste zurückgibt?
Ah, aber es gibt bereits eine "Funktion", die ein Element vorlistet: Es ist der Cons% code_% Konstruktor: -)
Ihr bestehender Code wird also gut funktionieren, solange Sie nicht den gleichen Namen für zwei verschiedene Variablen verwenden, weil die Bindung des zweiten Namens die erste schattiert (versteckt).
%Vor%Bezüglich der zweiten Änderung:
Da du fragst, wie du so etwas in einem aktuellen Programm machen würdest, würde ich folgendes sagen:
Wenn Sie wiederholt Dinge zu einer Liste hinzufügen, möchten Sie nicht jedes Mal neue Namen für diese Liste erfinden. Aber das Schlüsselwort ist hier "wiederholt", in der imperativen Programmierung würden Sie hier eine Schleife verwenden (und eine Variable in der Schleife modifizieren).
Da dies eine funktionale Programmierung ist, können Sie keine Schleifen verwenden, aber Sie können Rekursion verwenden. So würde ich ein Programm schreiben, das es einem Benutzer erlaubt, Befehle einzugeben und eine Liste zu sammeln:
%Vor% Zur Wiederholung; Die Funktion (:)
ruft sich rekursiv auf, wobei jedes Mal ein neues Element zur Liste hinzugefügt wird. Bis der Benutzer "quit" eingibt, hört er auf sich selbst anzurufen und gibt die endgültige Liste zurück.
Ursprüngliche Antwort in Bezug auf faule Bewertung:
Wie andere bereits sagten, ist dies eine rekursive Definition, die loop
zu einer unendlichen Liste macht, die nur order_list
-Werte enthält.
Eine faule Auswertung ist zwar nicht die Ursache, macht sie aber nützlich.
Wegen der Lazy-Evaluierung können Sie CashOnDelivery
wie folgt verwenden:
Wenn Sie keine faule Auswertung haben, stürzt der Aufruf von order_list
ab, weil er zuerst versuchen würde, take
(was unendlich ist) auszuwerten.
Nun, für order_list
ist das nicht wirklich nützlich, aber es gibt viele andere Orte, an denen es sehr praktisch ist, mit unendlichen (oder nur sehr großen) Datenstrukturen zu programmieren.
Ja, Sie versuchen, eine unendliche Liste zu drucken, die Sie mit fauler Auswertung erstellen können. Zum Beispiel
%Vor%erstellt eine unendliche Liste von Einsen, und Sie können mit der Take-Funktion oder beim Drucken so viele von ihnen aufnehmen, wie Sie möchten. Beachten Sie, dass Sie den gleichen Bezeichner auf der linken und rechten Seite der Gleichung verwenden, und es funktioniert: order_list ist CashOnDelivery: order_list, jetzt ersetzen: order_list = CashOnDelivery: (CashOnDelivery: order_list) = Cash ... etc.
Wenn Sie die [Cash ..., Invoice] -Liste erstellen möchten, verwenden Sie solche Namen nicht erneut.
Die cdr der von Ihnen erstellten Cons verweist auf sich selbst: Die Definition von order_list
, die in der zweiten let
verwendet wird, ist die Definition, die erstellt wird. Verwenden Sie einen anderen Variablennamen, um das Rekursionsproblem vollständig zu umgehen, und der Code wird auch weniger verwirrend sein.
EDIT: Nachdem ich Joels Antwort gelesen habe, scheint es, dass ich hier mit einem Lisp spreche. Lazy Evaluation oder Nein, in jedem Fall haben Sie eine rekursive Definition erstellt ...
Haskell verwendet faule Auswertung ... nichts wird ausgewertet, bis es benötigt wird. Deshalb wird order_list als ein Cons gespeichert, der CashOnDelivery und eine andere, nicht bewertete Zelle enthält, die order_list erneut referenziert.
Ich denke, das wichtige Problem hier ist nicht Faulheit , sondern Scope . Der Ausdruck let x = ...
führt eine neue Definition von x
ein, die jede vorherige Definition ersetzt. Wenn x
auf der rechten Seite erscheint, ist die Definition rekursiv. Es scheint, dass Sie die Verwendung von order_list
auf der rechten Seite von
bezieht sich auf die erste Definition von order_list
(d. h. [Invoice 2345]
). Der Ausdruck let
hat jedoch einen neuen Bereich eingeführt. Stattdessen haben Sie eine unendliche Liste von CashOnDelivery
-Elementen definiert.
Ich denke, du meinst "ist das, weil es faule Auswertung verwendet? Die Antwort ist ja:
%Vor%Dies sagt uns, dass ol das Element CashOnDelivery und dann das Ergebnis des Ausdrucks ol enthält. Dieser Ausdruck wird erst bei Bedarf ausgewertet (daher: Faulheit). Wenn ol gedruckt wird, wird CashOnDelivery zuerst gedruckt. Nur dann wird das nächste Element der Liste bestimmt, was zu einem unendlichen Verhalten führt.
X: L bedeutet "Erstellen Sie eine Liste, die mit X beginnt, gefolgt von den Elementen in der durch L definierten Liste."
Sie definieren dann order_list als CashOnDelivery, gefolgt von den Positionen in der Liste defined order_list. Diese Definition ist rekursiv, weshalb die Listenauswertung immer CashOnDelivery zurückgibt. Ihre Liste enthält tatsächlich eine infinate Zahl von CashOnDelivery Werten gefolgt durch einen einzelnen Rechnungswert.
order_list
in dieser Zeile:
ist eine andere Variable als order_list
in dieser Zeile
Die zweite Zeile ändert nicht den Wert von order_list
. Es führt eine neue Variable mit demselben Namen, aber einem anderen Wert ein.
order_list
auf der rechten Seite der zweiten Zeile ist das gleiche order_list
wie auf der linken Seite der zweiten Zeile; Es ist nicht verwandt mit dem order_list
in der ersten Zeile. Sie erhalten eine unendliche Liste voller CashOnDelivery
--- es gibt keine Invoice
s in dieser zweiten Liste.
In% ist val
nicht rekursiv. Sie müssen val rec
für rekursive Werte angeben.
In Haskell sind alle Top-Level-, let
- und where
-Bindungen rekursiv - es gibt keine nicht-rekursive Bindung.
Die Ausnahme: in do
, <-
binding ist nicht rekursiv. Wenn Sie jedoch {-# LANGUAGE RecursiveDo #-}
und import Control.Monad.Fix
verwenden, erhalten Sie mdo
, wobei eine <-
-Bindung rekursiv ist.
Vielleicht versuchen Sie das
Wir machen eine Funktion um dies zu tun (wie in Spaß Programmierung)
$ let order_list = [Invoice 2345]
$ let f x = x : order_list
$ let order_List = f cashOnDelivery
$ order_list
[CashOnDelivery, [Invoice 2345]]
~ Beachten Sie, dass wir die Funktion let f x = x : order_list
jedesmal neu anfügen müssen, wenn wir die order_list anhängen, so dass wir sie an die letzte order_list
$ let f x = x : order_list
$ let order_List = f cashOnDelivery
$ order_list
[CashOnDelivery, CashOnDelivery, [Invoice 2345]]
Prost!
ps, die unglaubliche Macht der funktionalen Programmierung ist in der Lage, grenzenlose Mengen von Funktionen und Objekten auf asynchronen (parallel / superfast) Systemen nahtlos auszuführen, da alle Funktionen und Objs per Definition unabhängig sind.
Tags und Links haskell functional-programming operators