Contract-Design

用於過濾查詢的函式內部映射

  • July 19, 2019

我有一個結構和幾個布爾映射,用於跟踪結構的屬性

struct Animal {
   string species;
   bytes32 ID;
}

mapping (bytes32 => Animal) public Animals;

mapping (bytes32 => bool) public Mammals;  
mapping (bytes32 => bool) public Birds;
mapping (bytes32 => bool) public Fish;

mapping (bytes32 => bool) public Carnivore;
mapping (bytes32 => bool) public Herbavore;
mapping (bytes32 => bool) public Omnivore;

在將其中一些布爾值設置為“true”後,我希望使用者呼叫一個函式來檢查動物的屬性。

function CarnivoreFish(bytes32 _ID) public returns (bool){

   //used to check if animal is a carnivorous fish

   if( Fish[_ID] == true &&  Carnivore[_ID] ==true ){
      return true;
   }
   else{
       return false;
   }      
}

我希望能夠概括以前的功能以適用於動物類別和飲食的任何組合。我已經嘗試定義新的映射並將它們設置為等於上面基於關鍵字的映射。

function filterAnimals(bytes32 _ID, string memory _animalClass, string memory _diet) public returns (bool){

   mapping (bytes32 => bool) memory AnimalClass;
   mapping (bytes32 => bool) memory Diet;

   //set first mapping "AnimalClass"
   if (_animalClass == "mammals") {
     AnimalClass = Mammals;  
   } 
   else if (_animalClass == "birds"){
      AnimalClass = Birds;
   }
   else{
       AnimalClass = Fish;
   }

   //set second mapping "Diet"
   if (_animalClass == "mammals") {
     Diet = Carnivore;  
   } 
   else if (_animalClass == "birds"){
      Diet = Herbavore;
   }
   else{
       Diet = Omnivore;
   }


   //check if animal has both propeties 

   if( AnimalClass[_ID] == true &&  Diet[_ID] ==true ){
      return true;
   }
   else{
       return false;
   }
}  

我根據我嘗試定義映射的方式收到錯誤。有沒有辦法像我試圖做的那樣設置一個映射等於另一個映射?有沒有更明顯的方法來概括 CarnivoreFish 函式?

即使編譯器似乎允許,您也不能在函式內分配映射。這將導致儲存損壞。持久變數的儲存佈局必須是全域的。不允許在函式內使用臨時映射函式。Solc 編譯器的監督?不適當的映射聲明會覆蓋儲存

在任何情況下,您都希望重新組織儲存。如果我沒記錯的話,你的方法每隻動物需要 10 個字,而下面建議的是 1 個字(有 30 個字節備用)。我刪除了字元串,因為在最好的情況下它本身需要兩個單詞。

pragma solidity 0.5.1;

contract Animals {

   enum Branch {mammal, bird, fish, insect}
   enum Diet {carnivore, herbivore, onmivore}

   struct Animal {
       // string species; // use a bytes32 or drop entirely
       Branch branch;
       Diet diet;
   }

   mapping(bytes32 => Animal) public animals;

   function setAnimal(bytes32 id, Branch branch, Diet diet) public {
       Animal storage a = animals[id];
       a.branch = branch;
       a.diet = diet;
   }

   function animalIsBranch(bytes32 id, Branch branch) public view returns(bool isIndeed) {
       return animals[id].branch == branch;
   }
   function animalIsDiet(bytes32 id, Diet diet) public view returns(bool isIndeed) {
       return animals[id].diet == diet;
   }

   // now, the combinations
   function carnivoreFish(bytes32 id) public view returns(bool isIndeed) {
       return animalIsBranch(id, Branch.fish) && animalIsDiet(id, Diet.carnivore);
   }
   // keep going. 
}

上述方法是慣用的,適用於少數特徵組合。如果您想進一步概括,可以查看 Gnosis 中的集合處理。它從 a 中緊密打包的 bool 開始uint(bool 打包成完整字節)。這是一種非常簡化的方法,靈感來自他們的收藏集。

像這樣的東西:

// 0b000.....00000000001 // mammal
// 00000.....00000000010 // bird
// 00000.....00000000100 // fish 
// 00000.....00000001000 // carnivore 
// 00000.....00000010000 // herbivore 
// 00000.....00000100000 // onmivore

....= 256 位可用於所有特徵。

您可以通過將這些位組合成其他數字來形成查詢。例如,哺乳動物和草食動物:

// 0b000.....00000100001 // mammal and herbivore

您的函式將使用按位運算來確認查詢中的所有位都存在於數據中(或不存在)。

 function isTrue(bytes32 id, uint profile) public view returns(bool isIndeed) {
   Animal storage a = animals[id];
   uint characteristics = a.characteristics;
   // A bitwise comparison determines what the answer means.
   // Is one of the profile bits present in characteristics bits? (OR)
   // Are all of the profile bits present in the characteristics bits? (AND)
 }

在實踐中,您可能會有兩個這樣的函式,AND 和 OR。讓我們繼續說這個例子是為 AND 設計的。

您可以將這些操作組合成更複雜的查詢,例如所有分支和食草動物。

 function or(bytes32 id, uint profile1, uint profile2) public view returns(bool isIndeed) {
   return isTrue(id, profile1) || isTrue(id, profile2);
 }

 function and(bytes32 id, uint profile1, uint profile2) public view returns(bool isIndeed) {
   return isTrue(id,profile1) && isTrue(id, profile2);
 }

共:

contract AnimalsBitwise {

 struct Animal {
   uint characteristics;
 }

 mapping(bytes32 => Animal) public animals;

 function setAnimal(bytes32 id, uint characteristics) public { 
   // opporunity to validate.
   // disallow impossible creatures, e.g. impossible fish birds
 }

 function allTrue(bytes32 id, uint profile) public view returns(bool isIndeed) {
   Animal storage a = animals[id];
   uint characteristics = a.characteristics;
   // bitwise comparison.
   // Are ALL profile bits are present in characteristics bits?
 }
 function anyTrue(bytes32 id, uint profile) public view returns(bool isIndeed) {
   Animal storage a = animals[id];
   uint characteristics = a.characteristics;
   // bitwise comparison.
   // Is ANY profile bit present in characteristics bits?
 }
 function or(bytes32 id, uint profile1, uint profile2) public view returns(bool isIndeed) {
   return allTrue(id, profile1) || allTrue(id, profile2);
 }

 function and(bytes32 id, uint profile1, uint profile2) public view returns(bool isIndeed) {
   return allTrue(id,profile1) && allTrue(id, profile2);
 }
}

如果您為了完整性而建構,您可能會說任意複雜性的布爾查詢可以在合理的成本下對一組 256 個可能的特徵進行。您甚至可以編寫一個簡單的語法,傳入要執行的操作數組。

在那種情況下會有迭代(我們不喜歡迭代),所以復雜性最終會受到 gasLimit 的限制。有人會認為,只要對原語進行仔細設計以涵蓋所有情況,所需的查詢就不需要超過幾個步驟。

我希望它能給你一些想法。

引用自:https://ethereum.stackexchange.com/questions/73024