Wie finde ich Schlüssel, die zur Laufzeit gültigen Werten in einem bestimmten Bereich (n_start, n_end) zugeordnet sind?

Meine Frage bezieht sich auf das Finden von Schlüsseln, die gültigen Werten in einem bestimmten Bereich (n_start, n_end) zur Laufzeit zugeordnet sind, wenn möglich mit minimalem Gas.

mapping(uint => myStruct) clusterContract;

uintenthält die Blocknummer auf der Blockchain. Dies kann also zwischen 0 und 1.000.000 liegen und sich weiter erhöhen. Basierend auf dem Verhalten des Clients wird ein neues Mapping generiert, aber es kann einige leere Stellen geben, z. B. wie mappingsfolgt:

clusterContract[0]    = structA;
clusterContract[1001] = structB;
clusterContract[1002] = structC;
clusterContract[1003] = structD;
clusterContract[1100] = structE;
clusterContract[2000] = structF;
clusterContract[7999] = structG;
clusterContract[8001] = structH;

Folgende Antwort sagt das:

Leider nicht. Eine Zuordnung gleicht nur einen Schlüssel einem Wert zu. Es hat keine Liste von beidem per se.

An dieser Stelle zum Beispiel : Ich möchte nur Strukturen erreichen, die von gültigen Schlüsseln zwischen 1000 und 8000 zeigen, die 1001 (structB), 1002 (structC), 1002 (structD), 2000 (structF) und 7999 (structG) sein sollten ) .

Um diese einzige Lösung zu erreichen, die ich mir ausgedacht habe, besteht darin, alle Elemente von 1000 bis 8000 zu durchlaufen und zu überprüfen, ob jede Blocknummer einen gültigen Schlüssel hat oder nicht, und wenn sie gültig ist, die Operation auszuführen, die ich ausführen möchte:

for(int i=1000; i++; i<8000) {
    if( clusterContract[i] ) //check whether key exists. I could not figure out how to check key exists or not.
       clusterContract[i].StructX.val = 64; //or do something else.
}

[F] Wie Sie sehen können, muss ich 7000 Werte durchlaufen, um zu prüfen, ob ihre Schlüssel gültig sind oder nicht. Ist dies eine vorgeschlagene Lösung, bei der der Gasverbrauch ineffizient sein könnte, da ich 7000 if thenoder mehr machen muss, wenn die Reichweite zunimmt? Jede empfohlene Lösung wäre sehr willkommen. Bitte beachten Sie, dass der binäre Suchbaum eine gute Lösung sein könnte, aber wenn der Baum wieder größer wird, würde es ineffizientes Gas zum Hinzufügen neuer Knoten oder zum Suchen innerhalb des Baums geben.

=> Oder wenn ich die Schlüsselgültigkeitsprüfung entferne (ob Schlüssel ivorhanden ist oder nicht), wenn dieses Codestück von Solidity in das Codestück der niedrigsten Ebene (Ethereum Virtual Machine-Code) konvertiert wird, werden nicht vorhandene Schlüssel automatisch ignoriert, da dies nicht der Fall ist existieren? Daher werden nur gültige Schlüssel berücksichtigt und es werden nicht 7000 Elemente durchlaufen.

for(int i=1000; i++; i<8000) 
   clusterContract[i].StructX.val = 64; //only existing keys are updated.

Vielen Dank für Ihre wertvolle Zeit und Hilfe. Es tut mir leid, wenn es Grammatikfehler gibt, ich habe mein Bestes getan, um dieses Problem mit einigen Beispielen zu erklären.

Antworten (1)

Dies ist eine ziemlich hohe Abstraktionsebene, da ich nicht genau weiß, was die Anwendung tun wird oder wie Sie die Bereitstellung strukturieren werden. Dreiteilige Antwort.

Erstens wäre ich misstrauisch gegenüber einem Schleifenprozess. Versuchen Sie, wann immer möglich zu vermeiden, und halten Sie es in Grenzen, wenn es unvermeidlich ist. Die Begründung beinhaltet die Tatsache, dass die Schätzung von Gas schwierig sein kann und es möglich ist, dass der Prozess aufgrund der Blockgasgrenze überhaupt nicht ausgeführt werden kann.

Es ist oft möglich, so etwas zu umgehen, indem man einen einzelnen Schritt ... // oder etwas anderes ... als Funktion ausgibt. Sie verlassen sich auf etwas außerhalb des Vertrags, dh einen Knotenserver oder einen UI-Client usw., um eine Schleife zu durchlaufen und diese Funktion so oft wie nötig aufzurufen.

Zweiter Teil. Das Iterieren über ein Mapping selbst ist nicht möglich, aber so etwas wird oft benötigt. Eine Lösung besteht darin, einen Index zu verwalten und darüber zu iterieren.

address[] clusterContractIndex;

Als ich darüber nachdachte, wie ich ein Beispiel präsentieren könnte, entwickelte ich das Gefühl, dass die Zuordnung nach Blocknummer nicht der richtige Weg ist, also wechselte ich zur Zuordnung nach Adresse. Mit dieser Änderung kommt es nicht zu Kollisionen, wenn im selben Block mehrere erstellt werden. Möglicherweise möchten Sie die Blocknummer zur Struktur hinzufügen, um sie irgendwo aufzuzeichnen, während Sie die Details ausarbeiten. Beachten Sie, dass dies nicht erforderlich ist, um Verträge in einem bestimmten Block zu erstellen.

Betrachten Sie als allgemeine Gewohnheit die Nützlichkeit einiger Funktionen, um das Mapping iterierbar zu machen:

function getContractCount() returns(uint count) {
    return clusterContractIndex.length;
}

function getContractAtIndex(uint row) returns(address contract) {
    return clusterContractIndex[row];
}

Schreiben Sie innerhalb der Funktion, die sie aufzeichnet, die Strukturen in die Zuordnung und auch push()die Schlüssel in den Index:

function newClusterContract(address newContract) {
    clusterStructs[newContract] = contractStruct;
    clusterContractIndex.push(newContract);

Dies ist ein kumulativer Nur-Hinzufügen-Prozess. Jetzt können Sie die Verträge in der Reihenfolge, in der sie erstellt wurden, oder direkt nach ihrer Adresse erhalten mit:

function getContract(address contract) returns(contract details) {}

Dritter. Effiziente Suche und Filterung. Ich habe noch keinen guten On-Chain-Suchprozess :-)

In der Praxis können Sie diese Anforderung möglicherweise ausschließen und sich auf Offchain-Dienste wie eine Datenbank oder sogar eine In-Memory-Sortierung durch eine Browser-Benutzeroberfläche (je nach erwartetem Umfang) verlassen. Das Fazit hier ist, dass die meisten Dinge, die so aussehen, als ob eine Onchain-Index-/Such-/Filterlösung erforderlich wäre, wirklich besser von Offchain-Prozessen gehandhabt werden.

Fügen Sie dem Vertrag einen Ereignisemitter hinzu, wenn sich der Status ändert.

event LogNewContract(address contract);

Und wenn es passiert:

LogNewContract(address newContract);

Wenn Sie mit dem Erstellen der Benutzeroberfläche beginnen, werden Sie wahrscheinlich feststellen, dass ein Beobachter darüber eine synchronisierte Kopie der On-Chain-Wahrheiten aufbewahren, Dinge schnell sortieren und filtern und dann die Details eines Vertrags abrufen kann, von dem bekannt ist, dass er existiert. So wie es klingt, muss es nur alle Verträge finden, die tatsächlich in einem bestimmten Bereich von Blocknummern existieren. Kunden erhalten die Blocknummer, in der ein Vertrag erstellt wurde, wenn das Ereignis eintrifft. Es ist ein Bonusstück für jedes erhaltene Ereignis.

Ich hoffe es hilft.

ps Sie können sich die Zuordnung als eine Tabelle vorstellen, in der alle möglichen Adressen vorhanden sind und alle verbleibenden Spalten zuverlässig auf 0 initialisiert werden. Wenn Sie eine Struktur von einer "leeren" Adresse ziehen, an die Sie nie geschrieben haben, werden alle Felder in der Struktur sein alle 0. 0, false, 0x0, leerer String, je nach Typ.

AKTUALISIEREN

Ich habe einen Bestellstatistikbaum erstellt, der eine Reihe von Problemen löst, wie z. B. das Finden des Medians oder Rangs eines Werts in einer sortierten Liste, und gleichzeitig eine Methode bietet, um die Gaskosten in jeder Größenordnung auf ein absolutes Maximum / Worst-Case-Limit zu begrenzen.

Dieses Repo baut auf Bokky Poobahs Red Black Tree auf, der die Grundlage für den selbstausgleichenden Baum bildet. https://github.com/rob-Hitchens/OrderStatisticsTree

Vielen Dank für Ihre wertvollen Ratschläge. Bezogen auf den zweiten Teil: Ist es möglich, Objekte mit einem bestimmten Index in ein Array zu verschieben? ( ethereum.stackexchange.com/q/11707/4575 ) @Rob Hitchens
Ja. meinArray[Zeile]=etwas; ... gehen Sie einfach nicht am Ende des Arrays vorbei oder versuchen Sie, dies über das Ende hinaus zu tun. Das Array hat immer eine feste oder dynamische (aber bestimmte) Länge und es mag kein get/set auf Zeilen, die nicht vorhanden sind.
Angenommen, wenn die Schlüssel der Zuordnung im Voraus bekannt sind (z. B. in einem Array gespeichert usw.), sollte das Nachschlagen der Zuordnung mit einem einfachen mappingVariable[key]Ergebnis das Ergebnis liefern, oder? Leider kämpfe ich mit diesem Problem ethereum.stackexchange.com/q/25283/3137 Können Sie bitte helfen?
Ja. Sie kümmern sich richtig. Die Schlüssel könnten außerhalb des Vertrages aufbewahrt werden. Beispielsweise könnten Sie die Schlüssel in Ereignissen ausgeben und sich dann darauf verlassen, dass Clients/Server es von dort aus herausfinden. Oder Sie können einige der hier beschriebenen Muster verwenden: ethereum.stackexchange.com/questions/13167/… . Es zeigt einige Beispiele für die Verwendung von Mappings mit Schlüsselindizes.
Danke für den Link zu Anwendungsfällen @RobHitchens! Ich kann jedoch nicht verstehen, warum mein Versuch, nachzuschlagen, mappingVariable[key]fehlschlägt, wenn der Schlüssel ein Mitglied des Arrays ist; selbst in einer struct. Ich wäre Ihnen dankbar, wenn Sie mir dabei helfen könnten, bitte.