2017-05-10 7 views
6

Nach this previous answerList.map in OCaml/F # mit korrekter Nebeneffekt-Reihenfolge neu implementiert?

Sie List.map wie dies implementieren könnte:

let rec map project = function 
    | [] -> [] 
    | head :: tail -> 
     project head :: map project tail ;; 

aber stattdessen wird es wie folgt umgesetzt:

let rec map project = function 
    | [] -> [] 
    | head :: tail -> 
     let result = project head in 
     result :: map project tail ;; 

Sie sagen, dass es auf diese Weise, um sicherzustellen, erfolgt Die Projektionsfunktion wird in der erwarteten Reihenfolge aufgerufen, wenn sie Nebenwirkungen hat, z

map print_int [1;2;3] ;; 

sollte 123 drucken, aber die erste Implementierung 321 drucken. Wenn ich jedoch beide selbst in OCaml und F # teste, produzieren sie genau das gleiche 123 Ergebnis.

(Beachten Sie, dass ich diesen Test in dem OCaml und F # REPLs - Nick in den Kommentaren schlägt dies könnte die Ursache für meine Unfähigkeit, zu reproduzieren, aber warum)

Was bin ich Missverständnis? Kann jemand ausarbeiten, warum sie unterschiedliche Aufträge produzieren sollten und wie ich reproduzieren kann? Das steht im Gegensatz zu meinem vorherigen Verständnis von OCaml-Code, das ich in der Vergangenheit geschrieben habe, also war das für mich überraschend und ich möchte sicherstellen, dass der Fehler nicht wiederholt wird. Wenn ich die beiden lese, lese ich es als genau dasselbe mit einer fremden Zwischenbindung.

Meine einzige Vermutung ist, dass die Reihenfolge der Ausdruck Auswertung mit Cons von links nach rechts ist, aber das scheint sehr seltsam?


Diese rein als Forschung besser zu verstehen getan wird, wie OCaml-Code ausführt, brauche ich nicht wirklich meine eigene List.map für die Produktion Code zu erstellen.

+1

ich beide nur getestet von diesen Implementierungen von "map" in OCaml und erhielt als Ausgabe "321" bzw. "123". Führen Sie diese in der REPL? –

+0

@NickZuber Ja ich bin! Getestet in OCaml und F # REPLs sowie BuckleScript Online REPL. Wenn ich nach Hause komme, werde ich es versuchen, keine REPL verwenden, aber meine nächste Frage ist natürlich, warum das einen Unterschied machen würde und immer noch, warum die beiden nicht gleichwertig sind :) – jayphelps

+2

Beachten Sie, dass die F # Implementierung von 'List.map' in FSharp.Core verwendet tatsächlich eine Mutation unter der Haube, um das "List" -Objekt zu erzeugen, bevor es es zurückgibt, so dass es den Stapel nicht überlaufen lässt. Die obigen "List.map" -Beispiele sind nur Beispiele und würden den Stapel überlaufen lassen, wenn sie unverändert verwendet würden. Ich weiß nicht, was die OCaml-Implementierung unter der Haube tut, aber es verwendet wahrscheinlich einen ähnlichen Trick, um den Stapel auf langen Listen nicht zu überlaufen. Du weißt das sicher schon, @jayphelps, aber ich dachte, es wäre es wert, für irgendjemanden erwähnt zu werden, der später auf diese Frage stoßen könnte. – rmunn

Antwort

3

Also, wenn Sie haben eine Implementierung von map wie folgt aus:

let rec map f = function 
    | [] -> [] 
    | a::l -> f a :: map f l 

keine der Funktionsanwendungen (f a) innerhalb der map Anrufe werden garantiert in der Reihenfolge ausgewertet, nacheinander werden Sie erwarten würden. Also, wenn Sie dies versuchen:

map print_int [1;2;3] 

Sie erhalten die Ausgabe

321- : unit list = [();();()] 

da durch die Zeit, diese Funktion Anwendungen wurden nicht in einer bestimmten Reihenfolge ausgeführt.

Nun, wenn Sie die map wie folgt implementieren:

let rec map f = function 
    | [] -> [] 
    | a::l -> let r = f a in r :: map f l 

Sie sind die Funktionsanwendungen zwingt in der Reihenfolge ausgeführt werden Sie erwarten, weil Sie explizit einen Anruf tätigen let r = f a zu bewerten.

So, jetzt, wenn Sie versuchen:

map print_int [1;2;3] 

Sie

123- : unit list = [();();()] 

bekommen, weil Sie explizit die Mühe gemacht haben die Funktion von Anwendungen, um zu bewerten.

+1

Wow. Das unterscheidet sich also von SML, oder? Diese ganze Zeit habe ich nie realisiert, dass die Auswertung der Anwendung nicht deterministisch war. Geist = geblasen. – jayphelps

+1

@jayphelps Ja, es gibt definitiv ein paar Gotchya's so - Jeffery hat [eine tolle Referenz zu den Dokumenten] (http://caml.inria.fr/pub/docs/manual-ocaml/expr.html) bezüglich dieses Verhaltens wenn Sie sind daran interessiert, mehr darüber zu lesen –

+0

"keine der Funktionsanwendungen (wie' fa') werden ausgeführt, bis die gesamte Funktion beendet ist "ist falsch.'f a 'wird sicherlich vor dem' map'-Aufruf ausgewertet, der es enthält. Der Punkt ist, dass es möglicherweise nicht vor seinem "Geschwister" -Kartenanruf auf dem Rest der Liste ausgewertet wird. –

11

Der Punkt ist, dass die Reihenfolge der Funktion Anwendung in OCaml ist nicht näher, nicht, dass es in einer bestimmten unerwünschten Reihenfolge sein wird.

Bei der Bewertung dieses Ausdrucks:

project head :: map project tail 

OCaml erlaubt ist project head erste oder es kann map project tail zuerst bewerten bewerten. Welchen es wählt, ist nicht spezifiziert. (Theoretisch wäre es wahrscheinlich zulässig, dass die Reihenfolge für verschiedene Aufrufe unterschiedlich ist.) Da Sie eine bestimmte Reihenfolge wünschen, müssen Sie das Formular mit let verwenden.

Die Tatsache, dass die Bestellung nicht spezifiziert ist, ist in Section 6.7 des OCaml-Handbuchs dokumentiert. Siehe Abschnitt Funktion Anwendung:

Die Reihenfolge, in der die Ausdrücke expr, argument1, ..., argumentN ausgewertet wird, wird nicht angegeben.

(Die Behauptung, dass die Auswertungsreihenfolge ist, können Sie nicht etwas, nicht spezifiziert testen. Beweisen Keine Zahl der Fälle von einer bestimmten Reihenfolge, dass diese Ordnung immer gewählt werden wird.)

+0

Ich sehe, du hast deine Antwort bearbeitet, es ist jetzt viel besser, aber ich hatte Nicks bereits akzeptiert. Danke dass du dir die Zeit nimmst! Mein Kopf war durchgebrannt, ich hatte keine Ahnung, dass OCAML-Anwendung nicht deterministisch war. Können Sie bestätigen, ob dies von SML abweicht? Ich schwöre, SML war deterministisch zu diesem Thema .... dann wieder dachte ich, OCAML wäre zu HAH – jayphelps

+2

(Sorry, ich bearbeite oft meine Antworten nachdem ich sie eingereicht habe.) Ich vermute, dass Anwendungsreihenfolge * in SML angegeben ist Sein. Aber jemand, der besser informiert ist als ich, wird sicherlich deine neue SO-Frage beantworten. Ich werde interessiert sein zu sehen, was sie sagen. –

+0

Ich auch! Danke noch einmal! – jayphelps

Verwandte Themen