Wo soll ich Defrecord in Clojure verwenden?


74

Ich verwende viele Maps und Strukturen in meinen Clojure-Programmen. Was sind die Vorteile (abgesehen von der Leistung) bei der Konvertierung in Defrecords?

Antworten:


93

Ich halte Strukturen für effektiv veraltet, daher verwende ich sie überhaupt nicht.

Wenn ich einen festen Satz bekannter Schlüssel habe, die in vielen Karteninstanzen verwendet werden, erstelle ich normalerweise einen Datensatz. Die großen Vorteile sind:

  • Performance
  • Die generierte Klasse hat einen Typ, den ich in Multimethoden oder anderen Situationen einschalten kann
  • Mit zusätzlichen Makromaschinen rund um Defrecord kann ich Feldvalidierung, Standardwerte und alles andere, was ich will, abrufen
  • Datensätze können beliebige Schnittstellen oder Protokolle implementieren (Karten können nicht)
  • Aufzeichnungen dienen für die meisten Zwecke als Karten
  • Die Rückgabe von Schlüsseln und Werten führt zu einer stabilen Reihenfolge (pro Erstellung)

Einige Nachteile von Aufzeichnungen:

  • Da Datensätze Java-Klasseninstanzen sind (keine Clojure-Maps), gibt es keine strukturelle Freigabe, sodass dieselbe Datensatzstruktur wahrscheinlich mehr Speicher benötigt als die entsprechende Map-Struktur, die geändert wurde. Es gibt auch mehr Objekterstellung / -zerstörung, wenn Sie einen Datensatz "ändern", obwohl die JVM speziell dafür entwickelt wurde, diese Art von kurzlebigem Müll zu essen, ohne ins Schwitzen zu geraten.
  • Wenn Sie während der Entwicklung Datensätze ändern, müssen Sie Ihre REPL wahrscheinlich häufiger neu starten, um diese Änderungen zu übernehmen. Dies ist normalerweise nur bei engen Entwicklungsabschnitten ein Problem.
  • Viele vorhandene Bibliotheken wurden nicht aktualisiert, um Datensätze zu unterstützen (Postwalk, Zip, Matchure usw. usw.). Wir haben diese Unterstützung nach Bedarf hinzugefügt.

Die Konstruktorform, die Sie in Ihrem dritten Argument beschreiben, wird von Common Lisp als "BOA-Konstruktor" bezeichnet - in der Reihenfolge der Argumente , die in mit definierten Konzerttypen verwendet wird defstruct. Siehe lispworks.com/documentation/HyperSpec/Body/… .
seh

Hallo Alex, ich denke, Sie werden feststellen, dass Datensätze tatsächlich strukturelle Freigabe unter der Haube verwenden. Sie implementieren eine vollständig persistente Datenstruktur, sodass alle Vorteile regulärer Karten erhalten bleiben.
Mikera

Mikera - Ein Datensatz generiert eine Java-Klasse mit endgültigen Feldern. Das Zuordnen zu einem muss ein neues Java-Objekt generieren. Es kann Feldinstanzen wiederverwenden, da sie unveränderlich sind, aber es ist nicht so effizient wie eine normale Clojure-Karte.
Alex Miller

Was ist defrecord2, auf das Sie sich beziehen? Ich kann es weder im Kern noch im Beitrag finden.
Petr Gladkikh

3
Ich würde argumentieren, dass Karten auch einfacher zu serialisieren / deserialisieren sind, zum Beispiel mit edn. Ich erinnere mich an einige Probleme beim Zurücklesen von zuvor serialisierten Datensätzen, weil der Code, der die Deserialisierung durchführt, den kompilierten Code für den von mir definierten Datensatztyp nicht finden konnte, obwohl ich ihn schließlich irgendwie aussortiert habe. Hätte ich nur Karten verwendet, hätte ich dieses Problem vollständig vermieden.
Regel

14

Stuart Sierra hat kürzlich einen interessanten Artikel über "Lösen des Ausdrucksproblems mit Clojure 1.2" geschrieben, der auch einen Abschnitt über Folgendes enthält defrecord:

http://www.ibm.com/developerworks/java/library/j-clojure-protocols/index.html#datatypes

Ich denke, der gesamte Artikel ist ein guter Ausgangspunkt für das Verständnis von Protokollen und Aufzeichnungen.


3
Dies ist eine reine Linkantwort. Es braucht mindestens eine Zusammenfassung imo.
MasterMastic

11

Ein weiterer großer Vorteil ist, dass der Datensatz einen Typ (seine Klasse) hat, von dem Sie versenden können.

Ein Beispiel, das diese Funktion verwendet, aber nicht für alle Verwendungsmöglichkeiten repräsentativ ist, ist das folgende:

(defprotocol communicate
  (verbalize [this]))

(defrecord Cat [hunger-level]
  communicate
  (verbalize [this]
    (apply str (interpose " " (repeat hunger-level "meow")))))

(defrecord Dog [mood]
  communicate
  (verbalize [this]
    (case mood
      :happy "woof"
      "arf")))

(verbalize (->Cat 3))
; => "meow meow meow"

(verbalize (->Dog :happy))
; => "woof"

1
Nicht wirklich, da Sie auf jede Funktion versenden können, nicht nur tippen. Sie können eine normale Karte verwenden und beispielsweise einen weiteren Eintrag mit einem Schlüssel namens "Typ" hinzufügen. Anschließend können Sie eine Versandfunktion verwenden, die den Wert des Eintrags "Typ" überprüft.
Goran Jovic

6
@Goran, das gilt für Multimethoden, bei denen Protokolle verwendet werden. Sie können nur nach Typ versenden. Unabhängig davon war mein Punkt, Defrecords zu verwenden, fügt implizit einen Typ hinzu, während ein Defstruct oder eine Map einen Typ nicht automatisch hinzufügt.
Bmillare

Diese Antwort ist wirklich knapp, ein Beispiel könnte eine nette Ergänzung sein.
Matanster

Ein Beispiel
hinzugefügt

2

Verwenden Sie in den meisten Fällen Karten und Datensätze nur, wenn Sie Polymorphismus benötigen. Nur mit Karten können Sie immer noch Multimethoden verwenden. Sie benötigen jedoch Datensätze, wenn Sie Protokolle wünschen. Warten Sie daher, bis Sie Protokolle benötigen, bevor Sie auf Datensätze zurückgreifen. Vermeiden Sie sie bis dahin zugunsten eines datenzentrierteren und einfacheren Codes.


1

Zusätzlich zu dem, was zuvor erwähnt wurde, sind Datensätze nicht nur in Bezug auf die Leistung im Allgemeinen gleich oder überlegen und bieten dieselbe Programmierschnittstelle wie eine Karte, sondern auch eine milde Struktur: Schlüsselnamen und die Anzahl der Schlüssel werden zum Zeitpunkt von erzwungen Definition. Dies kann nützlich sein, um dumme Fehler zu vermeiden, bei denen von vielen Werten dieselbe Struktur erwartet wird (oder ansonsten nur künstlich starr).

Unabhängig von den ursprünglichen Motivationen unterscheidet sich auch diese Eigenschaft von Karten.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.