4

Ich werde mit Julia veranschaulichen:Funktionale Programmierung: Sind Karten sequenziell? Folgen für Verschlüsse

Angenommen, ich habe eine Funktion counter(), die eine Schließung ist.

function mycl()                                                  
     state=0                                                  
     function counter()                                               
       state=state+1                                              
     end                                                   
end 

Jetzt nehme ich die Funktion mycoutner erstellen:

mycounter=mycl() 

und Karte jetzt diese Funktion über ein Array der Länge 10, wobei alle Elemente 1.

map(x->x+mycounter(),ones(1:10)) 

Der Ausgang zu sein, ist wie folgt:

julia> map(x->x+mycounter(),ones(1:10)) 
10-element Array{Int64,1}: 
    2 
    3 
    4 
    5 
    6 
    7 
    8 
    9 
10 
11 

Es scheint, dass die Funktion sequenziell auf das zu mappende Array angewendet wird.

Letztendlich versuche ich für for-Schleifen zu vermeiden, aber mit der lokalen Statusvariable der Schließung muting Zustand, ich brauche dies nacheinander angewendet werden. Dies scheint der Fall zu sein, ist das ein angenommener Standard? (habe die äquivalente R-Version nicht mit * apply getestet). Und ist das wirklich "funktional", weil die lokale Zustandsvariable mutiert?

+0

Dies ist der Fall mit Karte. Wenn Sie stattdessen mit pmap versuchen, sehen Sie, dass Ihre Ausgabe etwa [2 2 2 2 2 3 3 3 4 4] usw. ist, abhängig von der Anzahl der von Ihnen definierten Prozesse. Und, ja, wenn Ihre Funktion einen Zustand hat, ist sie nicht funktional und die Ausgabe ist in diesem Fall völlig unvorhersehbar. –

+3

Sie sollten 'reduce' /' fold' (wie auch immer es in Julia heißt) für Dinge verwenden, die sequentiell erledigt werden müssen, nicht 'map'. Der Zweck von 'map' ist, einen neuen Container zu erstellen, keine Nebenwirkungen auszuführen. – Bergi

Antwort

1

Um die zweite Frage zuerst zu beantworten, nein, Ihr Code ist nicht rein funktional, da er den Status mutiert.

Wie für die erste Frage hängt es von jeder Sprache ab. In Schema, R7RS sagt "[t] he map procedure gilt proc Element-weise zu den Elementen der list s und gibt eine Liste der Ergebnisse, in der Reihenfolge" (Seite 51, Schwerpunkt von mir), wo es scheint etwas mehrdeutig, ob "in order" sich auf die Reihenfolge der Listen oder der Elemente der Listen bezieht (vielleicht ersteres). In OCaml, sagt der manual dass List.map „gilt Funktion f zu a1, ..., an und baut die Liste [f a1; ...; f an] mit den zurückgegebenen Ergebnissen von f“, die auch nicht eindeutig ist, aber seine implementation sequentiell explizit geschrieben let als a::l -> let r = f a in r :: map f l verwenden.

+1

R7RS sagt direkt nach Ihrem Zitat, dass "die dynamische Reihenfolge, in der' proc' auf die Elemente der '' Liste angewendet wird, nicht spezifiziert ist. –

5

Die aktuelle Julia-Implementierung von map wendet sein Argument function auf seine Auflistungsargumente in der Reihenfolge an, obwohl dies kein explizit dokumentiertes Feature ist. In Zukunft könnte sich die Reihenfolge der Tests ändern, wenn Multithreading zu einem nicht-experimentellen Sprachfeature wird, aber das wird nicht ohne Warnung geschehen. Es ist auch wahrscheinlich, dass dies nicht auftritt, indem das Verhalten von map geändert wird, sondern entweder als ein Opt-in-Merkmal - z. über tmap für "Threaded Map" - oder als eine Optimierung in Fällen, in denen der Compiler weiß, dass die Funktion, die abgebildet wird, rein funktional ist.

+0

Es gibt eine "pmap" -Funktion, die genau das tut, also denke ich, es ist eine vernünftige Annahme, dass die Karte immer sequenziell sein wird. Auch wäre ich sehr überrascht, wenn es nicht schon eine Optimierung für reine Funktionen gäbe, denn das zu überprüfen ist sehr einfach. –

+0

Die 'pmap'-Funktion ist für verteilte parallele Karten, nicht für threading map im aktuellen Prozess. (Natürlich könnte man beides wünschen, aber das ist ein Problem mit dem API-Design für die Zukunft.) Es gibt keine Optimierung, um reine Funktionen über mehrere Threads hinweg auszuführen, da die Ausführung von Julia-Code in anderen Threads als das experimentelle Feature ist zunehmend stabil). – StefanKarpinski

1

Die reine funktionale Funktion, nach der Sie suchen, ist eine "akkumulierende Karte", die den Zustand vermeidet und stattdessen einen akkumulativen Parameter einfädelt. Siehe zum Beispiel die verschiedenen haskell Implementierungen.

Diese Funktion ist in Julia auch relativ einfach zu implementieren, obwohl argumentiert werden kann, dass eine for-Schleife geeigneter ist. In jedem Fall ist eine for-Schleife mit einer einzigen veränderlichen Zustandsvariablen viel besser als eine Funktion höherer Ordnung mit einem Zustand, der den Zustand mutiert, da in der ersteren die Mutation offensichtlich und enthalten ist, während in der letzteren die Mutation unklar ist Aufruf der map Funktion.