Frage

Eines der Argumente, die ich gegen funktionale Sprachen gehört habe, ist, dass einzelne Zuordnung Codierung zu hart ist, oder zumindest deutlich härter als „normal“ Programmierung.

Aber ein Blick durch meinen Code, ich erkennen, dass ich wirklich nicht viele (alle?) Verwenden Muster, die nicht nur geschrieben werden können als auch einzelne Zuordnung Formular verwenden, wenn Sie in einer einigermaßen modernen Sprache gerade schreiben.

Also, was sind die Anwendungsfälle für Variablen, die in einem einzigen Aufruf von ihrem Umfang variieren? Bedenkt man, dass Schleife Indizes, Parameter und andere Werte Umfang gebunden, variieren zwischen Anrufungen sind nicht mehrere Zuweisungen in diesem Fall (es sei denn, Sie wird zu sie im Körper verändern aus irgendeinem Grunde), und unter der Annahme, dass Sie in etwas, das ein weit genug über der Assemblersprache Ebene schreiben, wo man Dinge wie

schreiben
values.sum

oder (im Fall Summe ist nicht vorgesehen)

function collection.sum --> inject(zero, function (v,t) --> t+v )

und

x = if a > b then a else b

oder

n = case s 
  /^\d*$/ : s.to_int
  ''      : 0
  '*'     : a.length
  '?'     : a.length.random
  else    fail "I don't know how many you want"

, wenn Sie benötigen, und haben Listenkomprehensionen, Karte / sammeln und so weiter zur Verfügung.

Sie finden Sie, dass Sie noch wollen / müssen wandelbar Variablen in einer solchen Umgebung, und wenn ja, wofür?

Um zu klären, ich bitte nicht für eine Rezitation der Einwand gegen SSA-Form, sondern auch die konkreten Beispiele, in denen diese Einwände gelten würden. Ich suche ein Stück Code, der mit wandelbaren Variablen klar und prägnant sind und nicht so ohne sie geschrieben werden.

Meine Lieblingsbeispiele bisher (und die beste Einwand ich sie erwarten):

  1. Paul Johnson Fisher-Yates-Algorithmus Antwort, die ziemlich stark ist, wenn Sie die big-O Einschränkungen enthalten. Aber dann, als catulahoops weist darauf hin, das Big-O Problem ist nicht auf die SSA Frage gebunden, sondern wandelbar Datentypen zu haben, und mit diesem Satz beiseite kann der Algorithmus ziemlich deutlich in SSA geschrieben werden:

     shuffle(Lst) ->
         array:to_list(shuffle(array:from_list(Lst), erlang:length(Lst) - 1)).
     shuffle(Array, 0) -> Array;
     shuffle(Array, N) ->
         K = random:uniform(N) - 1,
         Ek = array:get(K, Array),
         En = array:get(N, Array),
         shuffle(array:set(K, En, array:set(N, Ek, Array)), N-1).
    
  2. jpalecek der Fläche eines Polygons Beispiel:

    def area(figure : List[Point]) : Float = {
      if(figure.empty) return 0
      val last = figure(0)
      var first= figure(0)
      val ret = 0
      for (pt <- figure) {
        ret+=crossprod(last - first, pt - first)
        last = pt
      }
      ret
    }
    

    , die noch etwas geschrieben werden könnte:

    def area(figure : List[Point]) : Float = {
        if figure.length < 3
            0
          else
            var a = figure(0)
            var b = figure(1)
            var c = figure(2)
            if figure.length == 3
                magnitude(crossproduct(b-a,c-a))
              else 
                foldLeft((0,a,b))(figure.rest)) { 
                   ((t,a,b),c) => (t+area([a,b,c]),a,c)
                   }
    

    Oder, da einige Leute auf die Dichte dieser Formulierung widersprechen, könnte es neu gefasst werden:

    def area([])    = 0.0   # An empty figure has no area
    def area([_])   = 0.0   # ...nor does a point
    def area([_,_]) = 0.0   # ...or a line segment
    def area([a,b,c]) =     # The area of a triangle can be found directly
        magnitude(crossproduct(b-a,c-a))
    def area(figure) =      # For larger figures, reduce to triangles and sum
        as_triangles(figure).collect(area).sum
    
    def as_triangles([])      = []  # No triangles without at least three points
    def as_triangles([_])     = []
    def as_triangles([_,_])   = []
    def as_triangles([a,b,c | rest) = [[a,b,c] | as_triangles([a,c | rest])]
    
  3. Prinzessin Punkt über die Schwierigkeit O (1) Warteschlangen mit unveränderlichen Strukturen der Implementierung ist interessant (und auch die Basis für ein überzeugendes Beispiel liefern kann), aber da es im Grunde um die Veränderlichkeit der Datenstruktur angegeben, und nicht direkt über die Mehrfachzuordnung Ausgabe.

  4. Ich bin fasziniert von dem Sieb des Eratosthenes Antwort, aber nicht überzeugt. Die richtige Big-O, ziehen so viele Primzahlen, wie Sie Generator in dem Papier gegeben möchte er zitiert nicht einfach aussehen zu implementieren richtig mit oder ohne SSA.


Nun, vielen Dank jeder für den Versuch. Da die meisten der Antworten stellte sich heraus, dass sie entweder 1), basierend auf änderbare Datenstrukturen, nicht auf Einzelbelegung und 2), soweit sie über einzelne Zuordnung Form waren leicht von Fachleuten auf dem Gebiet begegnet, werde ich schlagen die Linie von meinem Vortrag und / oder Umstrukturierung (vielleicht haben sie in Backup als Diskussionsthema in dem unwahrscheinlichen Fall, ich aus Worten laufen, bevor ich die Zeit abgelaufen ist).

Vielen Dank noch einmal.

War es hilfreich?

Lösung

Ich habe noch nie einen solchen Fall identifiziert. Und während Sie können immer nur neue Namen erfinden, wie in der Umstellung auf SSA-Form, finde ich eigentlich ist es leicht und natürlich für jeden Wert einen eigenen Namen zu haben. Eine Sprache wie Haskell gibt mir eine Menge Möglichkeiten, über die auf Namen Werte und zwei verschiedenen Orten Namensbindungen (let und where) zu setzen. Ich finde die Einzelbelegung Form ganz natürlich und gar nicht schwer.

Ich vermisse gelegentlich in der Lage, Verweise auf veränderliche Objekte auf dem Heap zu haben. Aber diese Dinge haben keine Namen, es ist also nicht das gleiche Einwand. (Und ich, dass auch finden, wenn ich veränderbare Objekte auf dem Heap verwenden, neige ich dazu, mehr Fehler zu schreiben!)

Andere Tipps

Das schwierigste Problem, das ich begegnet bin ist schlurfenden eine Liste. Die Fisher-Yates Algorithmus (auch manchmal als der Knuth-Algorithmus bekannt) beinhaltet, um die Liste iterieren jedes Element mit einem zufälligen anderen Artikel tauschen. Der Algorithmus ist O (n), gut bekannt und längst korrekt ist (eine wichtige Eigenschaft in einigen Anwendungen) unter Beweis gestellt. Aber es erfordert änderbare Arrays.

Das ist nicht zu sagen, dass Sie nicht tun können, in einem funktionalen Programm schlurfen. Oleg Kiselyov hat diesen geschrieben. Aber wenn ich ihn richtig verstehe, funktionelle schlurfenden ist O (n. N log), weil es durch den Bau eines binären Baum funktioniert.

Natürlich, wenn ich brauche den Fisher-Yates-Algorithmus in Haskell schreiben würde ich es nur in dem ST Monade , mit dem Sie einen Algorithmus einpacken lässt einen href Beteiligung <=" http://www.haskell.org/ghc/docs /latest/html/libraries/array/Data-Array-ST.html“rel = "noreferrer"> änderbare Arrays innerhalb einer schönen reine Funktion , wie folgt aus:

-- | Implementation of the random swap algorithm for shuffling.  Reads a list
-- into a mutable ST array, shuffles it in place, and reads out the result
-- as a list.

module Data.Shuffle (shuffle) where


import Control.Monad
import Control.Monad.ST
import Data.Array.ST
import Data.STRef
import System.Random

-- | Shuffle a value based on a random seed.
shuffle :: (RandomGen g) => g -> [a] -> [a]
shuffle _ [] = []
shuffle g xs = 
    runST $ do
      sg <- newSTRef g
      let n = length xs
      v <- newListArray (1, n) xs
      mapM_ (shuffle1 sg v) [1..n]
      getElems v

-- Internal function to swap element i with a random element at or above it.
shuffle1 :: (RandomGen g) => STRef s g -> STArray s Int a -> Int -> ST s ()
shuffle1 sg v i = do
  (_, n) <- getBounds v
  r <- getRnd sg $ randomR (i, n)
  when (r /= i) $ do
    vi <- readArray v i
    vr <- readArray v r
    writeArray v i vr
    writeArray v r vi


-- Internal function for using random numbers
getRnd :: (RandomGen g) => STRef s g -> (g -> (a, g)) -> ST s a
getRnd sg f = do
  g1 <- readSTRef sg
  let (v, g2) = f g1
  writeSTRef sg g2
  return 

v

Wenn Sie das akademische Argument machen wollen, dann ist es natürlich nicht technisch notwendig eine Variable mehr als einmal zuzuordnen. Der Beweis dafür ist, dass die gesamte Code in SSA (Single statische Zuordnung) Form dargestellt werden. Tatsächlich, das ist die nützlichste Form für viele Arten von statischen und dynamischen Analyse.

Zur gleichen Zeit gibt es Gründe, warum wir nicht alle Schreibcode in SSA-Form zu beginnen:

  1. Es dauert in der Regel mehr Anweisungen (oder mehr Zeilen Code) Code auf diese Weise zu schreiben. Brevity hat Wert.
  2. Es ist fast immer weniger effizient. Ja ich weiß, Sie sprechen über höhere Sprachen - eine faire Scoping - aber auch in der Welt von Java und C #, weit weg von der Montage, Geschwindigkeit zählt. Es gibt nur wenige Anwendungen, bei denen Geschwindigkeit irrelevant ist.
  3. Es ist nicht so leicht zu verstehen. Obwohl SSA „einfacher“ im mathematischen Sinne ist, dann ist es abstraktere von gesundem Menschenverstand, das, was in der realen Welt Programmierung zählt, ist. Wenn Sie wirklich intelligent sein, es zu grok, dann hat es keinen Platz in der Programmierung im Allgemeinen.

Auch in der oben genannten Beispielen ist es einfach, Löcher zu stecken. Nehmen Sie sich case Aussage. Was, wenn es eine Verwaltungsoption, ob '*' bestimmt ist erlaubt, und ein separates für ob '?' erlaubt? Auch Null ist für die ganze Zahl Fall nicht erlaubt, es sei denn, der Benutzer ein System die Berechtigung hat, die es erlaubt.

Dies ist ein reales Beispiel mit Niederlassungen und Bedingungen. Könnten Sie dies als eine einzige schreiben „Aussage?“ Wenn ja, ist Ihr „Aussage“ wirklich unterscheidet sich von vielen unterschiedlichen Aussagen? Wenn nicht, wie viele temporäre Schreib nur Variablen benötigen Sie? Und ist diese Situation deutlich besser als nur eine einzelne Variable mit?

Ich glaube, Sie werden die produktivsten Sprachen erlauben finden Sie funktionelle und zwingend notwendig, Stile, wie OCaml und F # zu mischen.

In den meisten Fällen kann ich Code schreiben, die einfach eine lange Reihe von „Karte x zu y, reduzieren y z“. In 95% der Fälle, die funktionale Programmierung vereinfacht meinen Code, aber es gibt einen Bereich, in dem Unveränderlichkeit zeigt seine Zähne:

Die große Diskrepanz zwischen den einfachen Implementierung und unveränderlich Stack und einer unveränderlichen Warteschlange.

Stacks sind einfach und Netz gut mit Ausdauer, Warteschlangen sind lächerlich.

Die gemeinsame Implementierungen von unveränderlichen Warteschlangen einen oder mehr interne Stapel verwenden und Rotationen stapeln. Der Vorteil ist, dass diese Warteschlangen in O laufen (1) die meiste Zeit , aber einige Operationen in O (n) ausgeführt werden. Wenn Sie auf Ausdauer in Ihrer Anwendung angewiesen sind, dann ist es prinzipiell möglich, dass jeder Betrieb läuft in O (n). Diese Warteschlangen sind nicht gut, wenn Sie in Echtzeit (oder zumindest konsistent) Leistung benötigen.

Chris Okasaki der stellt eine Implementierung von unveränderlichen Warteschlangen in rel="noreferrer">, die sie verwenden Trägheit O (1) für alle Operationen zu erzielen. Es ist eine sehr klug, vernünftig kurze Implementierung einer Echtzeit-Warteschlange -. Aber es erfordert tiefes Verständnis der zugrunde liegender Implementierungsdetails, und seine immer noch eine Größenordnung komplexe als ein unveränderlicher Stapel

In constrast, kann ich einen Stapel schreiben und Warteschlange wandelbar verkettete Listen verwendet, die für alle Operationen in konstanter Zeit ausgeführt werden, und der resultierende Code wäre sehr einfach sein.


Im Hinblick auf die Fläche eines Polygons, ist es einfach, es zu funktionaler Form zu konvertieren. Nehmen wir an, wir ein Vektor-Modul wie folgt aussehen:

module Vector =
    type point =
        { x : float; y : float}
        with
            static member ( + ) ((p1 : point), (p2 : point)) =
                { x = p1.x + p2.x;
                  y = p1.y + p2.y;}

            static member ( * ) ((p : point), (scalar : float)) =
                { x = p.x * scalar;
                  y = p.y * scalar;}

            static member ( - ) ((p1 : point), (p2 : point)) = 
                { x = p1.x - p2.x;
                  y = p1.y - p2.y;}

    let empty = { x = 0.; y = 0.;}
    let to_tuple2 (p : point) = (p.x, p.y)
    let from_tuple2 (x, y) = { x = x; y = y;}
    let crossproduct (p1 : point) (p2 : point) =
        { x = p1.x * p2.y; y = -p1.y * p2.x }

Wir können unsere Gegend Funktion ein wenig Tupel Magie definieren:

let area (figure : point list) =
    figure
    |> Seq.map to_tuple2
    |> Seq.fold
        (fun (sum, (a, b)) (c, d) -> (sum + a*d - b*c, (c, d) ) )
        (0., to_tuple2 (List.hd figure))
    |> fun (sum, _) -> abs(sum) / 2.0

Oder wir können das Kreuzprodukt verwenden, anstatt

let area2 (figure : point list) =
    figure
    |> Seq.fold
        (fun (acc, prev) cur -> (acc + (crossproduct prev cur), cur))
        (empty, List.hd figure)
    |> fun (acc, _) -> abs(acc.x + acc.y) / 2.0

Ich finde es auch nicht Funktion nicht lesbar.

Der Shuffle-Algorithmus ist trivial mit einzelner Zuordnung zu implementieren, in der Tat genau es ist das gleiche wie die zwingend notwendig Lösung mit der Iteration neu geschrieben Endrekursion. (Erlang, weil ich es schreibt schneller als Haskell.)

 shuffle(Lst) ->
     array:to_list(shuffle(array:from_list(Lst), erlang:length(Lst) - 1)).

 shuffle(Array, 0) -> Array;
 shuffle(Array, N) ->
     K = random:uniform(N) - 1,
     Ek = array:get(K, Array),
     En = array:get(N, Array),
     shuffle(array:set(K, En, array:set(N, Ek, Array)), N-1).

Wenn die Effizienz dieser Array-Operationen ist ein Anliegen, dann ist das eine Frage über änderbare Datenstrukturen und hat nichts mit Einzelbelegung zu tun.

Sie werden keine Antwort auf diese Frage bekommen, weil keine Beispiele existieren. Es ist nur eine Frage der Vertrautheit mit diesem Stil.

Als Antwort auf Jason -

function forbidden_input?(s)
    (s = '?' and not administration.qmark_ok) ||
    (s = '*' and not administration.stat_ok)  ||
    (s = '0' and not 'root node visible' in system.permissions_for(current_user))

n = if forbidden_input?(s)
    fail "'" + s + "' is not allowed."
  else
    case s
      /^\d*$/ : s.to_int
      ''      : 0
      '*'     : a.length
      '?'     : a.length.random
      else    fail "I don't know how many you want"

Ich würde Zuweisungen in einer nicht rein funktionalen Sprache verpassen. Vor allem, weil sie behindern die Nützlichkeit von Schleifen. Beispiele (Scala):

def quant[A](x : List[A], q : A) = {
  var tmp : A=0
  for (el <- x) { tmp+= el; if(tmp > q) return el; }
  // throw exception here, there is no prefix of the list with sum > q
}

Dies sollte die Quantil einer Liste berechnen, beachten Sie den Akkumulator tmp, die mehrfach zugeordnet ist.

Ein ähnliches Beispiel wäre:

def area(figure : List[Point]) : Float = {
  if(figure.empty) return 0
  val last = figure(0)
  var first= figure(0)
  val ret = 0
  for (pt <- figure) {
    ret+=crossprod(last - first, pt - first)
    last = pt
  }
  ret
}

Hinweis meist die last Variablen.

Diese Beispiele könnten neu geschrieben werden, um ein Tupel mit falten, um mehr Aufgaben zu vermeiden, aber das wäre wirklich die Lesbarkeit nicht helfen.

Local (Methode) Variablen sicher nie wird zu doppelt vergeben werden. Aber auch in der funktionalen Programmierung Neuzuweisung eine Variable ist erlaubt. Es ist Ändern (eines Teils) der Wert, der nicht erlaubt ist. Und wie dsimcha bereits beantwortet, für sehr große Strukturen (vielleicht an der Wurzel einer Anwendung) ist es nicht möglich scheint mir die gesamte Struktur zu ersetzen. Denk darüber nach. Der Zustand einer Anwendung wird alle letztlich vom Einstiegspunkt Methode der Anwendung enthalten sind. Wenn absolut kein Staat ohne ersetzt ändern ist, würden Sie Ihre Anwendung mit jedem Tastendruck neu starten. : (

Wenn Sie eine Funktion, die einen faule Liste / Baum baut dann reduziert es wieder, ein funktionelles Compiler in der Lage sein, kann es zu optimieren mit Abholzung .

Wenn es schwierig ist, könnte es nicht. Dann bist du eine Art von Glück, Leistung und Speicher weise, es sei denn, Sie laufen kann und eine veränderliche Variable verwenden.

Durch die Church-Turing-These, wissen wir, dass alles, was in einer Turing-complete-Sprache geschrieben werden kann, kann in geschrieben wird jede Turing-complete Sprache. Also, wenn Sie rechts unten, um es zu bekommen, gibt es nichts, was man nicht in Lisp tun kann, die Sie nicht in C # tun könnten, wenn man hart genug versucht, oder umgekehrt. (Mehr zu dem Punkt, entweder man geht ohnehin in den meisten Fällen auf x86 Maschinensprache erhalten zusammengestellt unten).

So ist die Antwort auf Ihre Frage: Es gibt keine solche Fälle. Insgesamt gibt es Fälle, die leichter sind für die Menschen in einem Paradigma / Sprache oder another-- und die Leichtigkeit des Verständnisses zu begreifen, hier ist gebunden an Ausbildung und Erfahrung.

Vielleicht ist das Hauptproblem hier ist der Stil in einer Sprache des Looping. In langauges, wo wir Rekursion verwenden, werden alle Werte über den gesamten Verlauf einer Schleife zu ändern neu gebunden, wenn die Funktion erneut aufgerufen wird. Sprachen Iteratoren in Blöcken (zum Beispiel Smalltalk und Ruby inject Verfahren) dazu neigen, ähnlich zu sein, auch wenn viele Menschen in Ruby noch each verwenden würden und eine veränderlichen Variablen über inject verwendet wird.

Wenn Sie Code-Loops while und for verwenden, auf der anderen Seite, haben Sie nicht die einfache Wiederverwendung Bindung von Variablen, die automatisch kommt, wenn Sie in mehreren Parametern zu Ihrem Stück Code passieren kann, dass eine Iteration der tut Schleife, so unveränderlich Variablen wäre eher ungünstig.

in Haskell zu arbeiten, ist ein wirklich guter Weg, um die Notwendigkeit von änderbaren Variablen zu untersuchen, da der Standard unveränderlich ist, sondern wandelbar diejenigen zur Verfügung steht (wie IORefs, MVars, und so weiter). Ich habe vor kurzem gewesen, äh, auf diese Weise selbst „Untersuchung“ gekommen und haben zu folgenden Schlussfolgerungen.

  1. In der überwiegenden Mehrzahl der Fälle veränderbare Variablen sind nicht notwendig, und ich bin glücklich leben ohne sie.

  2. Für Inter-Thread-Kommunikation, wandelbar Variablen sind wesentliche, für ziemlich offensichtlichen Gründen. (Dies ist spezifisch für Haskell, Laufzeitsysteme, die Nachricht auf der untersten Ebene vorbei verwenden brauchen sie nicht, natürlich.) Allerdings ist diese Verwendung selten genug ist, dass mit Funktionen zu verwenden, um sie zu lesen und schreiben (readIORef fooRef val etc.) ist nicht viel von einer Belastung.

  3. Ich habe änderbare Variablen in einem einzigen Thread verwendet, weil es scheint, bestimmte Dinge einfacher zu machen, aber später bereute es, als ich erkennen, dass es sich um sehr schwer zu Grunde wurde, was auf den Wert dort gespeichert vorging. (Mehrere verschiedene Funktionen manipulierten diesen Wert.) Dies war ein bisschen ein Augenöffner; in typischem Frosch-in-the-Pot-of-Warmwasser-Stil, würde ich nicht realisiert, wie einfach Haskell es gemacht hatte, für mich über die Verwendung von Werten an der Vernunft, bis ich in ein Beispiel lief, wie ich verwenden, um sie zu verwenden, .

So diesen Tagen habe ich auf der Seite der unveränderlichen Variablen ziemlich fest kommen.

Da frühere Antworten auf diese Frage, diese Dinge verwirrt haben, fühle ich mich gezwungen, hier ganz eindringlich darauf hin, dass dieses Problem orthogonal sowohl auf Reinheit und die funktionale Programmierung. Ich fühle, dass Ruby zum Beispiel Single-Zuordnung lokale Variablen aus, die profitieren würden, auch wenn sie möglicherweise ein paar andere Änderungen an der Sprache, wie das Hinzufügen von Endrekursion, wäre notwendig, dies ist wirklich bequem zu machen.

Was ist, wenn Sie benötigen kleine Änderungen große Datenstrukturen zu machen? Sie wollen nicht wirklich jedes Mal eine ganze Reihe oder große Klasse kopieren Sie ein paar Elemente ändern würden.

Ich habe nicht wirklich darüber nachgedacht, so viel, außer jetzt, dass Sie es heraus sind zeigen.

Eigentlich verwende Ich versuche, nicht mehr Zuweisungen unbewusst.

Hier ist ein Beispiel dafür, was Im reden, in Python

start = self.offset%n
if start:
    start = n-start

Geschriebene auf diese Weise eine unneccesary zusätzliche Modulo oder Subtraktion zu vermeiden. Dies wird mit bignum Stil langen Ints verwendet, so ist es eine sinnvolle Optimierung. Daran aber ist, dass es wirklich eine einzige Aufgabe ist.

Ich würde nicht mehrere Zuordnung vermissen überhaupt.

Ich weiß, dass Sie Code gefragt, die die Vorteile von änderbaren Variablen zeigen taten. Und ich wünsche ich es schaffen könnte. Aber nach wie vor darauf hingewiesen - es gibt kein Problem, das nicht in den beiden Moden ausgedrückt werden. Und vor allem, weil Sie haben darauf hingewiesen, dass jpalecek die Fläche eines Polygons Beispiel mit einem klappbaren algo geschrieben werden kann (was messier IMHO Weg und nimmt das Problem zu unterschiedlichen Graden an Komplexität) - gut es machte mich fragen, warum Sie auf Veränderlichkeit kommen nach unten, so hart. Also werde ich versuchen, das Argument für eine gemeinsame Basis zu machen und eine Koexistenz von unveränderlichen und veränderlichen Daten.

Meiner Meinung nach fehlt diese Frage den Punkt ein wenig. Ich weiß, dass wir Programmierer neigen zu mögen Dinge sauber und einfach, aber wir manchmal vermissen, dass eine Mischung als auch möglich ist. Und das ist wahrscheinlich der Grund dafür in der Diskussion um die Unveränderlichkeit es selten jemand ist, den Mittelweg nehmen. Ich frage mich nur, warum, denn seien sie ehrlich - Unveränderlichkeit ein großes Werkzeug ist von allen Arten von Problemen zu abstrahieren. Aber manchmal ist es ein großer Schmerz im Arsch . Manchmal ist es einfach zu einzuschränken. Und das allein macht mich zu stoppen und Sache - Wollen wir wirklich Veränderlichkeit verlieren? Ist es wirklich entweder-oder? Gibt es nicht einige Gemeinsamkeiten können wir ankommen? Wann hilft Unveränderlichkeit mir meine Ziele schneller zu erreichen, wenn Veränderlichkeit tut? Welche Lösung ist einfacher zu lesen und zu pflegen? (was für mich ist die größte Frage)

Viele dieser Fragen werden von einem Programmierer Geschmack beeinflusst und durch das, was sie in Programm verwendet werden, um Also werde ich auf einen der Aspekte konzentrieren, die in der Regel das Zentrum der meisten Pro-Unveränderlichkeit Argumente - Parallelismus:.

Oft Parallelität in das Argument geworfen Unveränderlichkeit umgibt. Ich habe auf Problemstellungen gearbeitet, die mehr als 100 CPUs erforderlich in einer angemessenen Zeit zu lösen. Und es hat mir gelehrt, eine sehr wichtige Sache: Die meiste Zeit die Manipulation von graphischen Darstellungen der Daten zu parallelisieren ist wirklich nicht die Art von Dingen, die der effizienteste Weg sein werden, parallelisieren. Es ist sicher stark profitieren, aber Ungleichgewicht ist ein echtes Problem in diesem Problem Raum. Also in der Regel auf mehr veränderbaren grafischen Darstellungen, die parallel arbeiten und Informationen mit unveränderlichen Austausch von Nachrichten ist viel effizienter. Das bedeutet, wenn ich weiß, dass der Graph isoliert ist, dass ich es nicht nach außen offenbart habe, würde Ich mag meine Operationen in der knappste Weise auszuführen ich mich vorstellen kann. Und das in der Regel beinhaltet die Daten mutiert. Aber nach dieser Operation auf den Daten möchte ich die ganze Welt, um die Daten öffnen - und das ist der Punkt, wo ich in der Regel ein bisschen nervös, wenn die Daten wandelbar ist. Da andere Teile des Programms könnte die Daten beschädigt, wird der Zustand ungültig, ... weil, nachdem die Daten oft in der Welt eröffnen bekommen in der Welt der Parallelität.

So reale Welt parallele Programme haben in der Regel Bereiche, in denen Daten Diagramme in endgültigen einzelnen Thread-Operationen verwendet werden - weil sie einfach nicht nach außen bekannt ist - und Bereiche, in denen sie in Multi-Threaded-Operationen beteiligt sein könnten (hoffentlich nur Daten liefern nicht manipuliert). Während dieser Multi-Threaded-Teile wollen wir nie ändern, um sie - es einfach besser ist auf veraltete Daten zu arbeiten, als auf inkonsistente Daten. Die durch den Begriff der Unveränderlichkeit gewährleistet werden kann.

Das hat mich zu einem einfachen Schluss gekommen: Das eigentliche Problem für mich ist, dass nicht des Programmiersprachen ich vertraut bin mit mir sagen lassen: „Nach diesem Punkt dieser ganze Datenstruktur shal unveränderlich sein“ und „geben Sie mir eine änderbare Kopie dieser hier unveränderliche Datenstruktur, überprüfen Sie bitte, dass nur kann ich die änderbare Kopie sehen“ . Im Moment habe ich es mir garantieren, indem sie eine Nur-Lese-Bit oder etwas ähnliches Spiegeln. Wenn wir Compiler-Unterstützung für sie haben könnte, nicht nur wäre es für mich sicher, dass ich nichts stup tatid nach Umklappen der Bit, aber es könnte der Compiler tun verschiedene Optimierungen tatsächlich helfen, die es vorher nicht möglich war. Plus -. Die Sprache wäre immer noch für Programmierer interessant sein, die mehr vertraut mit dem Imperativ Programmiermodell sind

So zusammenzufassen. IMHO Programme in der Regel einen guten Grund, beide unveränderlich und wandelbar verwenden Darstellungen von Daten Diagramme . Ich würde behaupten, dass Daten sein sollten unveränderlich standardmäßig und der Compiler soll, dass erzwingen - aber wir sollten den Begriff der Privat wandelbar Darstellungen haben, denn es gibt natürlich Bereiche, in denen multi- Einfädeln wird nie erreichen -. und Lesbarkeit und Wartbarkeit von einer dringlichen Strukturierung profitieren könnten

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top