¿Cómo encontrar claves asignadas a valores válidos en un rango determinado (n_start, n_end) en tiempo de ejecución?

Mi pregunta está relacionada con la búsqueda de claves asignadas a valores válidos en un rango dado (n_start, n_end) en tiempo de ejecución usando el mínimo de gas, si es posible.

mapping(uint => myStruct) clusterContract;

uintcontiene el número de bloque en la cadena de bloques. Entonces esto puede estar entre 0 y 1,000,000 y seguir aumentando. Según el comportamiento del cliente, se generará una nueva asignación, pero puede haber algunos espacios vacíos, por ejemplo , salir mappingsde la siguiente manera:

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

La siguiente respuesta dice que:

Lamentablemente no. Una asignación solo hace coincidir una clave con un valor. No tiene una lista de ninguno per se.

En este punto, por ejemplo : solo quiero llegar a las estructuras que apuntan desde claves válidas entre 1000 y 8000, que deberían ser 1001 (structB), 1002 (structC), 1002 (structD), 2000 (structF) y 7999 (structG) ) .

Para hacer eso, la única solución que se me ocurrió es iterar a través de todos los elementos del 1000 al 8000 y verificar que cada número de bloque sea una clave válida o no y si es válida hacer la operación que quiero:

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.
}

[P] Como puede ver, necesito recorrer 7000 valores para verificar si sus claves son válidas o no. ¿Es esta una solución sugerida, donde el uso de gas podría ser ineficiente ya que necesito hacer 7000 if theno más si aumenta el rango? Cualquier solución recomendada sería muy apreciada. Tenga en cuenta que el Árbol de búsqueda binaria podría ser una buena solución, pero una vez más, si el árbol es más grande, habría gas ineficiente para agregar nuevos nodos o buscar dentro del árbol.

=> O si elimino la verificación de validez de la clave (ya sea que la clave iexista o no), cuando Solidity convierte esta pieza de código en una pieza de código de nivel más bajo (código de máquina virtual de Ethereum), ¿las claves inexistentes se ignorarán automáticamente, ya que no lo hacen? ¿existir? Por lo tanto, solo se considerarán las claves válidas y no iterará a través de 7000 elementos.

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

Gracias por su valioso tiempo y ayuda. Lo siento si hay errores gramaticales, hice todo lo posible para explicar este problema con algunos ejemplos.

Respuestas (1)

Este es un nivel bastante alto de abstracción porque no sé exactamente qué hará la aplicación o cómo estructurará la implementación. Respuesta en tres partes.

En primer lugar, desconfiaría de un proceso descabellado. Trate de evitar siempre que sea posible y manténgalo bien delimitado cuando sea inevitable. La justificación incluye el hecho de que estimar el gas puede ser difícil y, a escala, es posible que el proceso no pueda ejecutarse en absoluto debido al límite de gas del bloque.

A menudo es posible sortear ese tipo de cosas al exponer un solo paso... // o hacer algo más... como una función. Depende de algo fuera del contrato, es decir, un servidor de nodo o un cliente de interfaz de usuario, etc., para iterar sobre un bucle y llamar a esa función con la frecuencia necesaria.

Segunda parte. No es posible iterar sobre un mapeo en sí mismo, pero a menudo se necesita algo así. Una solución es mantener un índice e iterar sobre eso.

address[] clusterContractIndex;

Reflexionando sobre cómo presentar un ejemplo, comencé a desarrollar la sensación de que el mapeo por número de bloque no es el camino a seguir, así que cambié al mapeo por dirección. No habrá ninguna colisión si se crean múltiplos en el mismo bloque con este cambio. Es posible que desee agregar el número de bloque a la estructura para registrarlo en algún lugar mientras trabaja en los detalles. Tenga en cuenta que esto no será necesario para crear contratos en un determinado bloque.

Como hábito general, considere la utilidad de algunas funciones para hacer que el mapeo sea iterable:

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

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

Dentro de la función que los registra, escriba las estructuras para el mapeo y también push()las claves para el índice:

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

Este es un proceso acumulativo de solo agregar. Ahora puedes obtener los contratos en el orden en que fueron creados, o por su dirección directamente con:

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

Tercero. Búsqueda y filtrado eficientes. Todavía no tengo un buen proceso de búsqueda en cadena :-)

En la práctica, es posible que pueda eliminar ese requisito y depender de servicios fuera de la cadena, como una base de datos o incluso una ordenación en memoria por parte de una interfaz de usuario del navegador (según la escala esperada). La conclusión aquí es que la mayoría de las cosas que parecen necesitar una solución de índice/búsqueda/filtro en la cadena se manejan mejor con procesos fuera de la cadena.

Agregue un emisor de eventos al contrato cada vez que cambie el estado.

event LogNewContract(address contract);

Y cuando sucede:

LogNewContract(address newContract);

Cuando comience a construir la interfaz de usuario, probablemente encontrará que un observador puede mantener una copia sincronizada de las verdades en cadena, ordenar y filtrar rápidamente las cosas, y luego obtener los detalles de un contrato que se sabe que existe. Por lo que parece, solo necesita encontrar cualquier contrato que realmente exista en un rango determinado de números de bloque. Los clientes obtienen el número de bloque en el que se creó un contrato cuando llega el evento. Es una pieza extra de cada evento recibido.

Espero eso ayude.

pd Puede pensar en el mapeo como una tabla en la que existen todas las direcciones posibles y todas las columnas restantes se inicializan de manera confiable en 0. Si extrae una estructura de una dirección "vacía" a la que nunca escribió, entonces todos los campos en la estructura serán todo 0. 0, falso, 0x0, cadena vacía, según el tipo.

ACTUALIZAR

Creé un árbol de estadísticas de pedidos que resuelve una serie de problemas, como encontrar la mediana o el rango de un valor en una lista ordenada, al mismo tiempo que proporciona un método para limitar el costo de la gasolina a un límite máximo absoluto/peor de los casos en cualquier escala.

Este repositorio se basa en Red Black Tree de Bokky Poobah, que proporciona la base para el árbol de autoequilibrio. https://github.com/rob-Hitchens/OrderStatisticsTree

Gracias por tu valioso consejo. Relacionado con la segunda parte, ¿es posible insertar objetos en una matriz con un índice dado? ( ethereum.stackexchange.com/q/11707/4575 ) @Rob Hitchens
Sí. myArray[fila]=algo; ... simplemente no te alejes del final de la matriz ni intentes hacerlo más allá del final. La matriz siempre tendrá una longitud fija o dinámica (pero definida) y no le gusta obtener/establecer filas que no están allí.
Supongamos que, si las claves de la asignación se conocen de antemano (por ejemplo, guardadas en una matriz, etc.), entonces buscar la asignación con un simple mappingVariable[key]debería obtener el resultado, ¿verdad? Desafortunadamente, estoy luchando con este problema ethereum.stackexchange.com/q/25283/3137 ¿Pueden ayudarme?
Sí. Te importa correcto. Las llaves podrían ser almacenadas fuera del contrato. Por ejemplo, podría emitir las claves en eventos y luego confiar en que los clientes/servidores lo averigüen a partir de ahí. O puede usar algunos de los patrones descritos aquí: ethereum.stackexchange.com/questions/13167/… . Muestra algunos ejemplos sobre el uso de asignaciones con índices de claves.
¡Gracias por el enlace a los casos de uso @RobHitchens! Sin embargo, no puedo entender por qué mappingVariable[key]falla mi intento de buscar cuando la clave es un miembro de la matriz; mismo en un struct. Te agradecería si me pudieras ayudar con esto por favor.