0
点赞
收藏
分享

微信扫一扫

【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;
}

}

举报

相关推荐

0 条评论