2010-10-03 8 views
7

sah ich das folgende Beispiel in Video Rich auf Sequenzen http://blip.tv/file/734409 etwa 33-36 Minuten hinein:Auf Leistung von `die Clojure first` Funktion

(first "abcd") => \a 

Nun er sagt, dass dies zu (Art erweitert):

(first "abcd") => (first (seq "abcd")) => (first '(\a \b \c \d)) 

So sieht es aus wie ein O(N) Betrieb, da die vollständige Kopie der Zeichenfolge gemacht wird. Vor allem, wenn ein String unveränderlich ist, warum wird es dann kopiert? (Edit: basierend auf einer Antwort, ist es wahrscheinlich nicht; sah einfach so, wenn gedruckt.) Zweitens angenommen first auf etwas anderes in Java, das veränderbar ist, sagen eine verknüpfte Liste von ganzen Zahlen. Sollte first auf eine faule Weise handeln (z. B. zuerst eine persistente Sequenz erstellen)? Wäre es nicht sinnvoll, es sofort zu bewerten und zu speichern? Es wäre eine Art Hack, der die nette Abstraktion durchbricht, aber die Arbeit schnell erledigt, denke ich. Wenn Sie (seq "abcd") anrufen, wissen Sie nicht, wie es verwendet wird. Wenn Sie eine first auf einer seq anrufen, wissen Sie, was zu tun ist. Aber wenn Sie first auf "abcd" anrufen, denke ich, dass die Durchführung einer hacky und schnell "greifen und speichern", Ansatz ist besser als eine Sequenz und dann rufen Sie first.

Fehle ich etwas? Hat Rich Hickey einige Schritte ausgelassen?

Lassen Sie mich wissen, wenn ich Fragen habe. Vielen Dank!

Antwort

4

Die anderen Antworten sind korrekt, aber ich werde diese Gelegenheit nutzen, um auf einen interessanten Effekt von Clojures Philosophie der Unveränderlichkeit hinzuweisen.

So sieht es aus wie eine O (N) Operation, weil die vollständige Kopie der Zeichenfolge erstellt wird.

Strings, wie andere Datenstrukturen in Clojure, sind unveränderlich. (Sie sind ein Sonderfall, in der JVM anstelle von Clojure implementiert, aber das ist zu diesem Punkt unerheblich.)

Kopien von unveränderlichen Objekten sind frei. Das ist richtig, frei.Weil Sie überhaupt nicht kopieren müssen. Das gleiche zugewiesene Objekt im Speicher kann einfach im Großhandel wiederverwendet werden, da es garantiert immer dasselbe ist, als wenn es "kopiert" wurde.

Also eine Funktion wie seqnie muss eigentlich alles kopieren. Es funktioniert nur mit dem, was direkt übergeben wird, und gibt eine (möglicherweise faule) Sequenz zurück, die eine abstrakte Schnittstelle zu allem bereitstellt, was Sie seq aufgerufen haben.

Und so ist seq immer O (1).

+0

Danke, jede weitere Eingabe zum Arbeiten mit veränderbaren Java-Objekten, die in eine Sequenz umgewandelt werden können? –

+1

IIRC, ruft seq eine faule Clojure-Sequenz auf, die von einem Java-Iterator über die Sammlung unterstützt wird. Wenn also die Lazy-Sequenz realisiert wird, muss sie allen Regeln folgen, die Iteratoren betreffen (was das Mutieren des zugrunde liegenden Objekts betrifft). Sobald es jedoch realisiert ist, ist es eine unveränderliche Clojure-Sequenz. – levand

+0

@Hamish - Wenn Sie ein veränderbares Java-Objekt in eine Sequenz umwandeln möchten, müssen Sie entweder eine defensive Kopie mit O (n) -Kosten erstellen oder das Risiko eingehen, dass die zugrunde liegenden Daten geändert werden (was die normalen Erwartungen von Seq. Verletzen würde) Verhalten und vielleicht kleine Fehler verursachen). Der frühere Ansatz (Kopieren) ist wahrscheinlich vorzuziehen, letzterer ist sehr gefährlich, es sei denn, Sie sind sich absolut sicher, dass im falschen Moment nichts geändert wird. – mikera

6

Es folgt nicht, dass eine vollständige Kopie der Zeichenfolge erstellt wird. (Aber das ist eine gute Frage.)

In der Quelle für clojure.lang.RT Sie werden feststellen, dass die Laufzeit verwendet CharSequence die Sequenz zu erstellen:

static ISeq seqFrom(Object coll){ 
    if(coll instanceof Seqable) 
     return ((Seqable) coll).seq(); 
    else if(coll == null) 
     return null; 
    else if(coll instanceof Iterable) 
     return IteratorSeq.create(((Iterable) coll).iterator()); 
    else if(coll.getClass().isArray()) 
     return ArraySeq.createFromObject(coll); 
    else if(coll instanceof CharSequence) 
     return StringSeq.create((CharSequence) coll); 
      . 
      . 
      . 

wirklich Also, das ist eine Java-Frage, nicht ein clojure Frage. Ich habe das nicht überprüft, aber ich bin ziemlich sicher, dass CharSequence das "Richtige" macht.

+0

Danke für die Antwort, Rob. Angenommen, 'CharSequence' macht das Richtige, weil es möglich ist, weil' String' unveränderlich ist. Nun, was wäre, wenn ich eine veränderbare Liste von 999 Ganzzahlen hätte, die in Java-Code definiert sind, und ich wollte nur das erste Element erfassen. Kann ich es besser machen, als zuerst 999 Gegenstände zu kopieren, oder ist das die Strafe, die ich zahlen müsste, wenn ich Sachen mache, die nicht "clojure" sind? Z.B. Ich hätte eine Klasse machen sollen, die solche unveränderlichen vielleicht enthält? Es scheint mir, als würde es Fälle geben, in denen eine unvollkommene Java-Bibliothek von Clojure benutzt werden muss. Ich verstehe, dass das erste Element groß sein kann. –

+0

(Fortsetzung), so kann das Aufrufen von 'First' eine Weile dauern - sagen wir zum Beispiel, dass wir zuerst eine veränderbare verkettete Liste von veränderbaren verketteten Listen aufrufen. Ich verstehe, warum wir die allererste innere veränderbare verknüpfte Liste durch Kopieren in eine beharrliche Sequenz umwandeln wollen, aber warum machen wir das Gleiche mit dem "Rest" von Dingen, die in diesem speziellen Fall niemals betrachtet werden ? –

+2

Es ist nicht notwendig, 999 Elemente in eine seq zu kopieren, da die seq nur die zugrunde liegende Datenstruktur wie benötigt verwendet und aus Gründen der Leistung und der Unveränderbarkeit zwischenspeichert, was sie findet. Wenn Sie bei einer Sammlung (z. B. Ihrer verknüpften Liste) "zuerst" aufrufen, ist das erste Element dieser Sammlung, das die seq betrachten muss, das erste. Die noch nicht beobachteten Elemente der zugrunde liegenden Sammlung können ohne vorherige Ankündigung geändert werden. Nur wenn der Seq tatsächlich ein Element liest, wird dieses Element innerhalb der Seq "persistent" sein. –

3

Sie könnten die seq so betrachten, als ob sie eine Lazy-Sequenz basierend auf der Zeichenfolge erstellt.

Es erstellt eigentlich keine Lazy-Sequenz, es schließt tatsächlich die Java-Implementierung von CharSequence kurz, aber das ist ein Implementierungsdetail.

Aus Komplexität Sicht first ist O(1) auf einem seq, und Sie können eine seq in konstanter Zeit von irgendetwas ist iterable erstellen.

Verwandte Themen