【CryptoZombies - 2 Solidity 進階】005 For循環

目錄

一、前言

二、For循環

1、引入

2、For循環

3、實戰

1.要求

2.代碼


一、前言

看了一些區塊鏈的教程,論文,在網上剛剛找到了一個項目實戰,CryptoZombies。

前面我們講到了Gas,今天我們再來講一下如何節約Gas。

如果你想了解更多有關於機器學習、深度學習、區塊鏈、計算機視覺等相關技術的內容,想與更多大佬一起溝通,那就掃描下方二維碼加入我們吧!

二、For循環

1、引入

爲了實現 getZombiesByOwner 函數,一種解決方案是在 ZombieFactory 中存入”主人“和”殭屍軍團“的映射。

mapping (address => uint[]) public ownerToZombies

然後每次創建新的殭屍的時候,就需要執行ownerToZombies [owner] .push(zombieId) 將其添加到主人的殭屍數組中。而 getZombiesByOwner 函數也非常簡單:

function getZombiesByOwner(address _owner) external view returns (uint[]) {
  return ownerToZombies[_owner];
}

這種做法確實很簡單,很直接,但是它存在問題:

如果我們需要一個函數來把一頭殭屍轉移到另一個主人名下(我們一定會在後面的課程中實現的),又會發生什麼?

這次更換主人的操作需要實現:

1.將殭屍push到新主人的 ownerToZombies 數組中。

2.從舊主的 ownerToZombies 數組中移除殭屍。

3.將舊主殭屍數組中“換主殭屍”之後的的每頭殭屍都往前挪一位,把挪走“換主殭屍”後留下的“空槽”填上。

4.將數組長度減1。

但是第三步實在是太貴了!因爲每挪動一頭殭屍,我們都要執行一次寫操作。如果一個主人有20頭殭屍,而第一頭被挪走了,那爲了保持數組的順序,我們得做19個寫操作。由於寫入存儲是 Solidity 中最費 gas 的操作之一,使得換主函數的每次調用都非常昂貴。更糟糕的是,每次調用的時候花費的 gas 都不同!具體還取決於用戶在原主軍團中的殭屍頭數,以及移走的殭屍所在的位置。以至於用戶都不知道應該支付多少 gas。

有人說,我們也可以把數組中最後一個殭屍往前挪來填補空槽,並將數組長度減少一。但這樣每做一筆交易,都會改變殭屍軍團的秩序。

由於從外部調用一個 view 函數是免費的,我們也可以在 getZombiesByOwner 函數中用一個for循環遍歷整個殭屍數組,把屬於某個主人的殭屍挑出來構建出殭屍數組。那麼我們的 transfer 函數將會便宜得多,因爲我們不需要挪動存儲裏的殭屍數組重新排序,總體上這個方法會更便宜,雖然有點反直覺。

2、For循環

爲了實現上面的功能,我們要用到for循環。for循環對於大家來說,如果學過其他編程語言,就相當熟悉了。

我們看一個簡單的示例:

function getEvens() pure external returns(uint[]) {
  uint[] memory evens = new uint[](5);
  // 在新數組中記錄序列號
  uint counter = 0;
  // 在循環從1迭代到10:
  for (uint i = 1; i <= 10; i++) {
    // 如果 `i` 是偶數...
    if (i % 2 == 0) {
      // 把它加入偶數數組
      evens[counter] = i;
      //索引加一, 指向下一個空的‘even’
      counter++;
    }
  }
  return evens;
}

這個函數將返回一個形爲 [2,4,6,8,10] 的數組。

3、實戰

1.要求

我們在 getZombiesByOwner 函數中通過一條 for 循環來遍歷 DApp 中所有的殭屍, 將給定的‘用戶id'與每頭殭屍的‘主人’進行比較,並在函數返回之前將它們推送到我們的result 數組中。

1.聲明一個變量 counter,屬性爲 uint,設其值爲 0 。我們用這個變量作爲 result 數組的索引。

2.聲明一個 for 循環, 從 uint i = 0 到 i <zombies.length。它將遍歷數組中的每一頭殭屍。

3.在每一輪 for 循環中,用一個 if 語句來檢查 zombieToOwner [i] 是否等於 _owner。這會比較兩個地址是否匹配。

4.在 if 語句中:

  (1)通過將 result [counter] 設置爲 i,將殭屍ID添加到 result 數組中。

  (2)將counter加1。

這樣 - 這個函數能返回 _owner 所擁有的殭屍數組,不花一分錢 gas。

2.代碼

pragma solidity >=0.5.0 <0.6.0;

import "./zombiefeeding.sol";

contract ZombieHelper is ZombieFeeding {

  modifier aboveLevel(uint _level, uint _zombieId) {
    require(zombies[_zombieId].level >= _level);
    _;
  }

  function changeName(uint _zombieId, string calldata _newName) external aboveLevel(2, _zombieId) {
    require(msg.sender == zombieToOwner[_zombieId]);
    zombies[_zombieId].name = _newName;
  }

  function changeDna(uint _zombieId, uint _newDna) external aboveLevel(20, _zombieId) {
    require(msg.sender == zombieToOwner[_zombieId]);
    zombies[_zombieId].dna = _newDna;
  }

  function getZombiesByOwner(address _owner) external view returns(uint[] memory) {
    uint[] memory result = new uint[](ownerZombieCount[_owner]);
    // Start here
    uint counter = 0;
    for(uint i = 0; i < zombies.length; i++) {
      if(zombieToOwner[i] == _owner) {
        result[counter] = i;
        counter++;
      }
    }
    return result;
  }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章