对于Solidity新手来说,简单合适的使用数据结构去组织数据是一个挑战。它想让我们以我们不熟悉的方式去组织所有东西。

对于区块链上的数据组织,是否有完备通用的模式?

原文地址

2 人回答 0

2 个回答
投票数
最旧发布
最近发布

回答发布于 2018-09-03 15:52:44

补充下无梦的回答,请使用revert()作为throw的替代。从version 0.4.13开始,throw关键字被替代了,并会在将来删除。关于更多信息:Solidity 中的require,asset,revert

所以,作为一个例子你应该改变

if(isEntity(entityAddress)) throw;

if(isEntity(entityAddress)) revert();

在无梦提供的代码里。

||
||

回答发布于 2018-09-03 15:47:04

这里以实用性递增的顺序列举了一些简单实用的模式。为了简洁性,Event记录被省略了。而在实践中,对于每个重要的状态改变,最好去emit events。 使用Array的简单列表 优点

  • 可靠的时间顺序
  • 提供计数
  • 通过数组下标(非id)随机访问

缺点

  • 不能通过id随机访问
  • 无法保证唯一性
  • 不能检验重复
  • 不受控制的列表增长

例子

pragma solidity ^0.4.6;

contract simpleList {

  struct EntityStruct {
    address entityAddress;
    uint entityData;
    // more fields
  }

  EntityStruct[] public entityStructs;

  function newEntity(address entityAddress, uint entityData) public returns(uint rowNumber) {
    EntityStruct memory newEntity;
    newEntity.entityAddress = entityAddress;
    newEntity.entityData    = entityData;
    return entityStructs.push(newEntity)-1;
  }

  function getEntityCount() public constant returns(uint entityCount) {
    return entityStructs.length;
  }
}

Mapping和Struct

优点

  • 通过唯一id随机访问
  • Id唯一性保证
  • 在struct中能包含arrays,mappings,structs结构。

缺点

  • 无法列举键值
  • 无法统计键的个数
  • 需要手动检测去区分是默认设置的0还是显示设置的0。

例子:

contract mappingWithStruct {

  struct EntityStruct {
    uint entityData;
    bool isEntity;
  }

  mapping (address => EntityStruct) public entityStructs;

  function isEntity(address entityAddress) public constant returns(bool isIndeed) {
    return entityStructs[entityAddress].isEntity;
  }

  function newEntity(address entityAddress, uint entityData) public returns(bool success) {
    if(isEntity(entityAddress)) throw; 
    entityStructs[entityAddress].entityData = entityData;
    entityStructs[entityAddress].isEntity = true;
    return true;
  }

  function deleteEntity(address entityAddress) public returns(bool success) {
    if(!isEntity(entityAddress)) throw;
    entityStructs[entityAddress].isEntity = false;
    return true;
  }

  function updateEntity(address entityAddress, uint entityData) public returns(bool success) {
    if(!isEntity(entityAddress)) throw;
    entityStructs[entityAddress].entityData = entityData;
    return true;
  }
}

有唯一id的Struct数组

优点

  • 通过数组下标随机访问
  • Id唯一性保证
  • 在struct中能包含arrays,mappings,structs.

缺点

  • 无法通过id随机访问
  • 不受控制的数组增长

例子:

contract arrayWithUniqueIds {

  struct EntityStruct {
    address entityAddress;
    uint entityData;
  }

  EntityStruct[] public entityStructs;
  mapping(address => bool) knownEntity;

  function isEntity(address entityAddress) public constant returns(bool isIndeed) {
    return knownEntity[entityAddress];
  }

  function getEntityCount() public constant returns(uint entityCount) {
    return entityStructs.length;
  }

  function newEntity(address entityAddress, uint entityData) public returns(uint rowNumber) {
    if(isEntity(entityAddress)) throw;
    EntityStruct memory newEntity;
    newEntity.entityAddress = entityAddress;
    newEntity.entityData = entityData;
    knownEntity[entityAddress] = true;
    return entityStructs.push(newEntity) - 1;
  }

  function updateEntity(uint rowNumber, address entityAddress, uint entityData) public returns(bool success) {
    if(!isEntity(entityAddress)) throw;
    if(entityStructs[rowNumber].entityAddress != entityAddress) throw;
    entityStructs[rowNumber].entityData    = entityData;
    return true;
  }
}

映射的Struct加索引

优点:

  • 通过Id或数组下标随机访问
  • Id唯一性保证
  • 在struct中能包含arrays,mappings,structs
  • 数组维护了声明的顺序
  • 统计记录个数
  • 列举id

缺点

  • 不受控制的数组增长

例子:

contract MappedStructsWithIndex {

  struct EntityStruct {
    uint entityData;
    bool isEntity;
  }

  mapping(address => EntityStruct) public entityStructs;
  address[] public entityList;

  function isEntity(address entityAddress) public constant returns(bool isIndeed) {
      return entityStructs[entityAddress].isEntity;
  }

  function getEntityCount() public constant returns(uint entityCount) {
    return entityList.length;
  }

  function newEntity(address entityAddress, uint entityData) public returns(uint rowNumber) {
    if(isEntity(entityAddress)) throw;
    entityStructs[entityAddress].entityData = entityData;
    entityStructs[entityAddress].isEntity = true;
    return entityList.push(entityAddress) - 1;
  }

  function updateEntity(address entityAddress, uint entityData) public returns(bool success) {
    if(!isEntity(entityAddress)) throw;
    entityStructs[entityAddress].entityData    = entityData;
    return true;
  }
}

映射的Struct加可删除的索引

优点:

  • 通过id或数组下标随机访问
  • Id唯一性保证
  • 在stuct中可以包含arrays,mappings,structs.
  • 统计记录个数
  • 列举id
  • 使用delete函数,逻辑上控制数组大小

缺点:

  • 略微提升了代码复杂度
  • 略微提升了存储消耗
  • 无序的键值数组

例子:

contract mappedWithUnorderedIndexAndDelete {

  struct EntityStruct {
    uint entityData;
    uint listPointer;
  }

  mapping(address => EntityStruct) public entityStructs;
  address[] public entityList;

  function isEntity(address entityAddress) public constant returns(bool isIndeed) {
    if(entityList.length == 0) return false;
    return (entityList[entityStructs[entityAddress].listPointer] == entityAddress);
  }

  function getEntityCount() public constant returns(uint entityCount) {
    return entityList.length;
  }

  function newEntity(address entityAddress, uint entityData) public returns(bool success) {
    if(isEntity(entityAddress)) throw;
    entityStructs[entityAddress].entityData = entityData;
    entityStructs[entityAddress].listPointer = entityList.push(entityAddress) - 1;
    return true;
  }

  function updateEntity(address entityAddress, uint entityData) public returns(bool success) {
    if(!isEntity(entityAddress)) throw;
    entityStructs[entityAddress].entityData = entityData;
    return true;
  }

  function deleteEntity(address entityAddress) public returns(bool success) {
    if(!isEntity(entityAddress)) throw;
    uint rowToDelete = entityStructs[entityAddress].listPointer;
    address keyToMove   = entityList[entityList.length-1];
    entityList[rowToDelete] = keyToMove;
    entityStructs[keyToMove].listPointer = rowToDelete;
    entityList.length--;
    return true;
  }

}

最后一个有一个解释,这里:

https://medium.com/@robhitchens/solidity-crud-part-2-ed8d8b4f74ec#.ekc22r5lf

和这里:

https://bitbucket.org/rhitchens2/soliditycrud/src/83703dcaf4d0c4b0d6adc0377455c4f257aa29a7/docs/?at=master

文件夹树例子:在Solidity我们如何去组织一个文件夹树或者对象树?

链表的例子展示了一个维护有序列表的方式:https://github.com/ethereum/dapp-bin/blob/master/library/linkedList.sol

||
||

回答发布于 2018-09-03 15:47:04

这里以实用性递增的顺序列举了一些简单实用的模式。为了简洁性,Event记录被省略了。而在实践中,对于每个重要的状态改变,最好去emit events。 使用Array的简单列表 优点

  • 可靠的时间顺序
  • 提供计数
  • 通过数组下标(非id)随机访问

缺点

  • 不能通过id随机访问
  • 无法保证唯一性
  • 不能检验重复
  • 不受控制的列表增长

例子

pragma solidity ^0.4.6;

contract simpleList {

  struct EntityStruct {
    address entityAddress;
    uint entityData;
    // more fields
  }

  EntityStruct[] public entityStructs;

  function newEntity(address entityAddress, uint entityData) public returns(uint rowNumber) {
    EntityStruct memory newEntity;
    newEntity.entityAddress = entityAddress;
    newEntity.entityData    = entityData;
    return entityStructs.push(newEntity)-1;
  }

  function getEntityCount() public constant returns(uint entityCount) {
    return entityStructs.length;
  }
}

Mapping和Struct

优点

  • 通过唯一id随机访问
  • Id唯一性保证
  • 在struct中能包含arrays,mappings,structs结构。

缺点

  • 无法列举键值
  • 无法统计键的个数
  • 需要手动检测去区分是默认设置的0还是显示设置的0。

例子:

contract mappingWithStruct {

  struct EntityStruct {
    uint entityData;
    bool isEntity;
  }

  mapping (address => EntityStruct) public entityStructs;

  function isEntity(address entityAddress) public constant returns(bool isIndeed) {
    return entityStructs[entityAddress].isEntity;
  }

  function newEntity(address entityAddress, uint entityData) public returns(bool success) {
    if(isEntity(entityAddress)) throw; 
    entityStructs[entityAddress].entityData = entityData;
    entityStructs[entityAddress].isEntity = true;
    return true;
  }

  function deleteEntity(address entityAddress) public returns(bool success) {
    if(!isEntity(entityAddress)) throw;
    entityStructs[entityAddress].isEntity = false;
    return true;
  }

  function updateEntity(address entityAddress, uint entityData) public returns(bool success) {
    if(!isEntity(entityAddress)) throw;
    entityStructs[entityAddress].entityData = entityData;
    return true;
  }
}

有唯一id的Struct数组

优点

  • 通过数组下标随机访问
  • Id唯一性保证
  • 在struct中能包含arrays,mappings,structs.

缺点

  • 无法通过id随机访问
  • 不受控制的数组增长

例子:

contract arrayWithUniqueIds {

  struct EntityStruct {
    address entityAddress;
    uint entityData;
  }

  EntityStruct[] public entityStructs;
  mapping(address => bool) knownEntity;

  function isEntity(address entityAddress) public constant returns(bool isIndeed) {
    return knownEntity[entityAddress];
  }

  function getEntityCount() public constant returns(uint entityCount) {
    return entityStructs.length;
  }

  function newEntity(address entityAddress, uint entityData) public returns(uint rowNumber) {
    if(isEntity(entityAddress)) throw;
    EntityStruct memory newEntity;
    newEntity.entityAddress = entityAddress;
    newEntity.entityData = entityData;
    knownEntity[entityAddress] = true;
    return entityStructs.push(newEntity) - 1;
  }

  function updateEntity(uint rowNumber, address entityAddress, uint entityData) public returns(bool success) {
    if(!isEntity(entityAddress)) throw;
    if(entityStructs[rowNumber].entityAddress != entityAddress) throw;
    entityStructs[rowNumber].entityData    = entityData;
    return true;
  }
}

映射的Struct加索引

优点:

  • 通过Id或数组下标随机访问
  • Id唯一性保证
  • 在struct中能包含arrays,mappings,structs
  • 数组维护了声明的顺序
  • 统计记录个数
  • 列举id

缺点

  • 不受控制的数组增长

例子:

contract MappedStructsWithIndex {

  struct EntityStruct {
    uint entityData;
    bool isEntity;
  }

  mapping(address => EntityStruct) public entityStructs;
  address[] public entityList;

  function isEntity(address entityAddress) public constant returns(bool isIndeed) {
      return entityStructs[entityAddress].isEntity;
  }

  function getEntityCount() public constant returns(uint entityCount) {
    return entityList.length;
  }

  function newEntity(address entityAddress, uint entityData) public returns(uint rowNumber) {
    if(isEntity(entityAddress)) throw;
    entityStructs[entityAddress].entityData = entityData;
    entityStructs[entityAddress].isEntity = true;
    return entityList.push(entityAddress) - 1;
  }

  function updateEntity(address entityAddress, uint entityData) public returns(bool success) {
    if(!isEntity(entityAddress)) throw;
    entityStructs[entityAddress].entityData    = entityData;
    return true;
  }
}

映射的Struct加可删除的索引

优点:

  • 通过id或数组下标随机访问
  • Id唯一性保证
  • 在stuct中可以包含arrays,mappings,structs.
  • 统计记录个数
  • 列举id
  • 使用delete函数,逻辑上控制数组大小

缺点:

  • 略微提升了代码复杂度
  • 略微提升了存储消耗
  • 无序的键值数组

例子:

contract mappedWithUnorderedIndexAndDelete {

  struct EntityStruct {
    uint entityData;
    uint listPointer;
  }

  mapping(address => EntityStruct) public entityStructs;
  address[] public entityList;

  function isEntity(address entityAddress) public constant returns(bool isIndeed) {
    if(entityList.length == 0) return false;
    return (entityList[entityStructs[entityAddress].listPointer] == entityAddress);
  }

  function getEntityCount() public constant returns(uint entityCount) {
    return entityList.length;
  }

  function newEntity(address entityAddress, uint entityData) public returns(bool success) {
    if(isEntity(entityAddress)) throw;
    entityStructs[entityAddress].entityData = entityData;
    entityStructs[entityAddress].listPointer = entityList.push(entityAddress) - 1;
    return true;
  }

  function updateEntity(address entityAddress, uint entityData) public returns(bool success) {
    if(!isEntity(entityAddress)) throw;
    entityStructs[entityAddress].entityData = entityData;
    return true;
  }

  function deleteEntity(address entityAddress) public returns(bool success) {
    if(!isEntity(entityAddress)) throw;
    uint rowToDelete = entityStructs[entityAddress].listPointer;
    address keyToMove   = entityList[entityList.length-1];
    entityList[rowToDelete] = keyToMove;
    entityStructs[keyToMove].listPointer = rowToDelete;
    entityList.length--;
    return true;
  }

}

最后一个有一个解释,这里:

https://medium.com/@robhitchens/solidity-crud-part-2-ed8d8b4f74ec#.ekc22r5lf

和这里:

https://bitbucket.org/rhitchens2/soliditycrud/src/83703dcaf4d0c4b0d6adc0377455c4f257aa29a7/docs/?at=master

文件夹树例子:在Solidity我们如何去组织一个文件夹树或者对象树?

链表的例子展示了一个维护有序列表的方式:https://github.com/ethereum/dapp-bin/blob/master/library/linkedList.sol

||
||

回答发布于 2018-09-03 15:52:44

补充下无梦的回答,请使用revert()作为throw的替代。从version 0.4.13开始,throw关键字被替代了,并会在将来删除。关于更多信息:Solidity 中的require,asset,revert

所以,作为一个例子你应该改变

if(isEntity(entityAddress)) throw;

if(isEntity(entityAddress)) revert();

在无梦提供的代码里。

||
||

回答发布于 2018-09-03 15:52:44

补充下无梦的回答,请使用revert()作为throw的替代。从version 0.4.13开始,throw关键字被替代了,并会在将来删除。关于更多信息:Solidity 中的require,asset,revert

所以,作为一个例子你应该改变

if(isEntity(entityAddress)) throw;

if(isEntity(entityAddress)) revert();

在无梦提供的代码里。

||
||

回答发布于 2018-09-03 15:47:04

这里以实用性递增的顺序列举了一些简单实用的模式。为了简洁性,Event记录被省略了。而在实践中,对于每个重要的状态改变,最好去emit events。 使用Array的简单列表 优点

  • 可靠的时间顺序
  • 提供计数
  • 通过数组下标(非id)随机访问

缺点

  • 不能通过id随机访问
  • 无法保证唯一性
  • 不能检验重复
  • 不受控制的列表增长

例子

pragma solidity ^0.4.6;

contract simpleList {

  struct EntityStruct {
    address entityAddress;
    uint entityData;
    // more fields
  }

  EntityStruct[] public entityStructs;

  function newEntity(address entityAddress, uint entityData) public returns(uint rowNumber) {
    EntityStruct memory newEntity;
    newEntity.entityAddress = entityAddress;
    newEntity.entityData    = entityData;
    return entityStructs.push(newEntity)-1;
  }

  function getEntityCount() public constant returns(uint entityCount) {
    return entityStructs.length;
  }
}

Mapping和Struct

优点

  • 通过唯一id随机访问
  • Id唯一性保证
  • 在struct中能包含arrays,mappings,structs结构。

缺点

  • 无法列举键值
  • 无法统计键的个数
  • 需要手动检测去区分是默认设置的0还是显示设置的0。

例子:

contract mappingWithStruct {

  struct EntityStruct {
    uint entityData;
    bool isEntity;
  }

  mapping (address => EntityStruct) public entityStructs;

  function isEntity(address entityAddress) public constant returns(bool isIndeed) {
    return entityStructs[entityAddress].isEntity;
  }

  function newEntity(address entityAddress, uint entityData) public returns(bool success) {
    if(isEntity(entityAddress)) throw; 
    entityStructs[entityAddress].entityData = entityData;
    entityStructs[entityAddress].isEntity = true;
    return true;
  }

  function deleteEntity(address entityAddress) public returns(bool success) {
    if(!isEntity(entityAddress)) throw;
    entityStructs[entityAddress].isEntity = false;
    return true;
  }

  function updateEntity(address entityAddress, uint entityData) public returns(bool success) {
    if(!isEntity(entityAddress)) throw;
    entityStructs[entityAddress].entityData = entityData;
    return true;
  }
}

有唯一id的Struct数组

优点

  • 通过数组下标随机访问
  • Id唯一性保证
  • 在struct中能包含arrays,mappings,structs.

缺点

  • 无法通过id随机访问
  • 不受控制的数组增长

例子:

contract arrayWithUniqueIds {

  struct EntityStruct {
    address entityAddress;
    uint entityData;
  }

  EntityStruct[] public entityStructs;
  mapping(address => bool) knownEntity;

  function isEntity(address entityAddress) public constant returns(bool isIndeed) {
    return knownEntity[entityAddress];
  }

  function getEntityCount() public constant returns(uint entityCount) {
    return entityStructs.length;
  }

  function newEntity(address entityAddress, uint entityData) public returns(uint rowNumber) {
    if(isEntity(entityAddress)) throw;
    EntityStruct memory newEntity;
    newEntity.entityAddress = entityAddress;
    newEntity.entityData = entityData;
    knownEntity[entityAddress] = true;
    return entityStructs.push(newEntity) - 1;
  }

  function updateEntity(uint rowNumber, address entityAddress, uint entityData) public returns(bool success) {
    if(!isEntity(entityAddress)) throw;
    if(entityStructs[rowNumber].entityAddress != entityAddress) throw;
    entityStructs[rowNumber].entityData    = entityData;
    return true;
  }
}

映射的Struct加索引

优点:

  • 通过Id或数组下标随机访问
  • Id唯一性保证
  • 在struct中能包含arrays,mappings,structs
  • 数组维护了声明的顺序
  • 统计记录个数
  • 列举id

缺点

  • 不受控制的数组增长

例子:

contract MappedStructsWithIndex {

  struct EntityStruct {
    uint entityData;
    bool isEntity;
  }

  mapping(address => EntityStruct) public entityStructs;
  address[] public entityList;

  function isEntity(address entityAddress) public constant returns(bool isIndeed) {
      return entityStructs[entityAddress].isEntity;
  }

  function getEntityCount() public constant returns(uint entityCount) {
    return entityList.length;
  }

  function newEntity(address entityAddress, uint entityData) public returns(uint rowNumber) {
    if(isEntity(entityAddress)) throw;
    entityStructs[entityAddress].entityData = entityData;
    entityStructs[entityAddress].isEntity = true;
    return entityList.push(entityAddress) - 1;
  }

  function updateEntity(address entityAddress, uint entityData) public returns(bool success) {
    if(!isEntity(entityAddress)) throw;
    entityStructs[entityAddress].entityData    = entityData;
    return true;
  }
}

映射的Struct加可删除的索引

优点:

  • 通过id或数组下标随机访问
  • Id唯一性保证
  • 在stuct中可以包含arrays,mappings,structs.
  • 统计记录个数
  • 列举id
  • 使用delete函数,逻辑上控制数组大小

缺点:

  • 略微提升了代码复杂度
  • 略微提升了存储消耗
  • 无序的键值数组

例子:

contract mappedWithUnorderedIndexAndDelete {

  struct EntityStruct {
    uint entityData;
    uint listPointer;
  }

  mapping(address => EntityStruct) public entityStructs;
  address[] public entityList;

  function isEntity(address entityAddress) public constant returns(bool isIndeed) {
    if(entityList.length == 0) return false;
    return (entityList[entityStructs[entityAddress].listPointer] == entityAddress);
  }

  function getEntityCount() public constant returns(uint entityCount) {
    return entityList.length;
  }

  function newEntity(address entityAddress, uint entityData) public returns(bool success) {
    if(isEntity(entityAddress)) throw;
    entityStructs[entityAddress].entityData = entityData;
    entityStructs[entityAddress].listPointer = entityList.push(entityAddress) - 1;
    return true;
  }

  function updateEntity(address entityAddress, uint entityData) public returns(bool success) {
    if(!isEntity(entityAddress)) throw;
    entityStructs[entityAddress].entityData = entityData;
    return true;
  }

  function deleteEntity(address entityAddress) public returns(bool success) {
    if(!isEntity(entityAddress)) throw;
    uint rowToDelete = entityStructs[entityAddress].listPointer;
    address keyToMove   = entityList[entityList.length-1];
    entityList[rowToDelete] = keyToMove;
    entityStructs[keyToMove].listPointer = rowToDelete;
    entityList.length--;
    return true;
  }

}

最后一个有一个解释,这里:

https://medium.com/@robhitchens/solidity-crud-part-2-ed8d8b4f74ec#.ekc22r5lf

和这里:

https://bitbucket.org/rhitchens2/soliditycrud/src/83703dcaf4d0c4b0d6adc0377455c4f257aa29a7/docs/?at=master

文件夹树例子:在Solidity我们如何去组织一个文件夹树或者对象树?

链表的例子展示了一个维护有序列表的方式:https://github.com/ethereum/dapp-bin/blob/master/library/linkedList.sol

||
||