本篇預計閱讀時間 15 分鐘, 實作時間 30 分鐘。
在開始之前先來思考底下的練習題,閱讀後即可練習完成。
練習題
-
練習題 1
-
如何利用迴圈找出陣列 [34, 231, 4534, 231, 23, 45, 80, 67798, 67783] 的最小值與最大值。
-
-
練習題 2
-
如何利用迴圈找出 [34, 231, 4534, 231, 23, 45, 80, 67798, 67783] 有幾個奇數。
-
-
練習題 3
-
如何利用迴圈找出 [2, 256, 4534, 1024, 23, 45, 0, 2048, 67783, 8, 64] 有幾個為 2 的正整數次方。
-
-
練習題 4
-
如何利用 mapping 建立一個名為 whitelist 的容器,並儲存三筆 address 映射 bool 資料。
-
-
練習題 5
-
小明想要架設一個交易多餘電力的合約,在合約中宣告一個 struct 表示當前加入此電力系統的會員們,並在內宣告一個 mapping 需要由 address 映射 uint,代表他們當前擁有可販賣的多餘電力。
-
-
練習題 6
-
承上題,宣告一個 enum 分別代表三種會員電力系統的狀態:malfunction, insufficient, surplus。
-
-
練習題 7
-
承上題,在練習題 5 的 struct 中再宣告一個 mapping 由 address 映射 uint,用於儲存每位會員的電力系統狀態。
-
Array
一個程式語言中最經典的資料結構莫過於 array 了,陣列的性質如下:
-
擁有兩個成員:
.length,.push(element) -
可為固定或動態的長度,取決於我們是否宣告陣列的長度。
-
T[][5]是五個動態陣列長度的組合(reverse notation)


pragma solidity ^0.8.11; contract MyArray {uint256[] arr_1;uint256[] arr_2 = [3,2,4];uint256[5] arr_3;uint[][] array2D = [ [1,2,3], [4,5,6] ];function getValueOfIndex(uint256 _index) public view returns (uint256) {return arr_2[_index];}// 在一個陣列的最後加入一個元素function addToArray(uint256 _value) public {arr_2.push(_value);}// 調用一個陣列的長度function valueCount() public view returns(uint) {return arr_3.length;}}
動態記憶體配置

uint[] memory arr = new uint[](3);
如果我們訪問了超過記憶體的值,Solidity 不會向其他語言一樣丟 Runtime Error 回來,而是會返回該型態的預設值。那我們所謂的非法訪問其實是訪問一個負的 index。
Mapping
mapping 在 Solidity 的運作就像是 python 的 dict 或常見的資料結構 hash-map。

圖 6-1 雜湊表
在圖 6-1 中的例子,當我們 call "k1" 的時候,這個 mapping 就會回傳 key "k1" 指向的值,也就是 "AAA,BBB,CCC"。此外,我們還可以儲存新的資料進去 mapping。

mapping(uint => string) public names;
在以上這段語法中,每一個 key 在這個名為 names 的 mapping 中,是以 uint 形態存在。而相對應的每個 value 型態為 string。
宣告語法為:mapping(_keyType => _valueType) name;
-
_keyType可以是任何的元素型態,也就是說它可以為任何的既有數值型態包含bytes和string -
_valueType可以是任何的型態,包含複雜的資料結構,甚至是mapping
Mapping具有以下的特色:
-
所有元素都會被初始化
-
不存在長度,如果需要紀錄長度得額外宣告一個變數
-
mapping的 Public state variables 會成為Getter Function -
如果需要可遍歷的
mapping可以藉由導入一些libraries達到。


pragma solidity ^0.8.11; contract MyMappings {mapping(uint256 => address) nfts; uint256 counter = 0;function getOwnerOfNFT(uint256 _id) public view returns (address) {return nfts[_id];}function mintNFT() public {nfts[counter] = msg.sender;counter++;}}
如同二維陣列,我們也可以宣告 Nested mapping 來做為類似的用途:

mapping(address => mapping(address => bool)) a;
既然用途很像,我們什麼時候用 array,什麼時候用 mapping 呢?
我會用使用情況來區分我們要使用哪種資料結構:
-
當常常需要遍歷(iterate)這個資料結構時,建議使用
array -
如果我們需要常常查看某些特定的鍵值(key)時,建議使用
mapping
需要注意的是,我們沒辦法使用動態記憶體配置來宣告 mapping。同時 mapping 也不具有可以直接呼叫長度的成員函式。
Structs
宣告 struct 在這裡的用途和 C 的語言一樣,如果是沒有學過 C/C++ 的話,可以把 struct 理解成一種比較低階的 C++, python 的 class。
定義一個 struct 之後會產生一種新的自定義、抽象、複雜的的資料型態。
我們就可以藉由這個資料型態宣告我們自己的結構變數。

struct User {
address id;
string name;
}
//Method 1 依照順序宣告 (argument order matters)
User("0xAio90....", "Mike");
//Method 2 註明則不必按照順序宣告 (argument order does not
matter)
User({name: "Mike", id: "0xAio90...."});
就像是我們定義一個新的形態是車子,並且定義一台車子裡面要有的資料有:持有者(address)、品牌(string)、公升數(uint)。
此後就可以宣告一個資料型態為車子的變數 myCar,並定義這個變數裡面的每個型態。

struct Car {
address owner;
string brand;
uint cc;}
myCar = Car({string: "Toyota", address: "0xAio90....", cc: 2000});
Structs 具有以下的性質:
-
在 Solidity 中不可以使用遞迴性的方式宣告變數,也就是說
struct的成員的型別不可是該struct。 -
以 structs 取代 objects 可以達到 gas consumption
-
可以組合
struct和mapping或甚至其他容器來建置些複雜的資料結構,像是以下例子是使用巢狀的資料結構(Nested Data Structures)來達成目的。

struct User {
uint id;
string name;
}
uint[] userIds;
mapping(uint => User) users;
Enum
枚舉(enum)和 struct 非常類似,只是 enum 中的每個參數會自動從 0 開始依序初始化。
Enum 具有以下的性質:
-
內部已有整數型態的元素存在
-
可以存進 uint8 的型態,如果數值數量介於 0~255,若大於 256 個值則可宣告為 uint16

enum ActionChoices { GoLeft, GoRight, GoStraight, SitsStill }ActionChoices choice;
ActionChoices constant defaultChoice = ActionChoices.Gostraight;
在 Solidity 中 enum 更常被用來進行模型選擇(model choice),或者追蹤狀態(track of state)。


pragma solidity ^0.8.11;
contract MyEnums {
enum Rarity {
original, // 0
rare, // 1
super_rare // 2
}
Rarity public rarity;
constructor() {
rarity = Rarity.rare;
}
// 我們可以直接將一個型態為該 enum 的變數賦值為某特定屬性
function makeSuperRare() public {
rarity = Rarity.super_rare;
}
// 我們可以藉由 enum 初始化的 INDEX 來進行賦值
function set(Rarity _rarity) public {
rarity = _rarity;}
// 我們可以藉由 delete 來重置這個 enum 成他的初始值 0function reset() public {delete rarity;
}
}
struct 和 enum 的差別:
-
Struct 用於表示一個複雜變數
-
Enum 用於表示一個單一變數的不同狀態或值
練習題解答
-
練習題解答 1
-
利用迴圈找出陣列 [34, 231, 4534, 231, 23, 45, 80, 67798, 67783] 的最小值與最大值。
-
提供 2 個方法來取最大值與最小值,分別是根據型別的定義和 type().min 或 .max 這兩者。


pragma solidity ^0.8.11;
contract Practice{
uint[] array = [34, 231, 4534, 231, 23, 45, 80, 67798, 67783];
function getMin() public view returns(uint){
uint min = 2**256 - 1;
for(uint i = 0; i < array.length; i++){
if(array[i] < min){
min = array[i];
}
}
return min;
}
function getMax() public view returns(uint){
uint max = type(uint256).min;
// for(uint i = 0; i < array.length; i++){
// if(array[i] > max){
// max = array[i];
// }
//}
-
練習題解答 2
-
利用迴圈找出 [34, 231, 4534, 231, 23, 45, 80, 67798, 67783] 有幾個奇數。
-

pragma solidity ^0.8.11; contract Practice{uint[] array = [34, 231, 4534, 231, 23, 45, 80, 67798, 67783];function getOdd() public view returns(uint){ uint cnt = 0;for(uint i = 0; i < array.length; i++){ if(array[i] % 2 != 0){cnt++; }}return cnt; }}
-
練習題解答 3
-
利用迴圈找出 [2, 256, 4534, 1024, 23, 45, 0, 2048, 67783, 8, 64] 有幾個為 2 的正整數次方。
-


pragma solidity ^0.8.11;
contract Practice{
uint[] array = [2, 256, 4534, 1024, 23, 45, 0, 2048, 67783, 8, 64];
function getEven() public view returns(uint){
uint cnt = 0;
for(uint i = 0; i < array.length; i++){
if(array[i] % 2 == 0){
cnt++;
}
}
return cnt;
}
}
這個例子跟取得偶數的函式是一樣的。
-
練習題解答 4
-
利用 mapping 建立一個名為 whitelist 的容器,並儲存三筆 address 映射bool 資料。
-

pragma solidity ^0.8.11;
contract Practice{
mapping(address => bool) public whitelist;
}
-
練習題解答 5
-
小明想要架設一個交易多餘電力的合約,在合約中宣告一個 struct 表示當前加入此電力系統的會員們,並在內宣告一個 mapping 需要由 address映射 uint,代表他們當前擁有可販賣的多餘電力。
-


-
練習題解答 6 與 7
-
宣告一個 enum 分別代表三種會員電力系統的狀態:malfunction, insufficient, surplus。
- 在我們 Practice 5 的 struct 中再宣告一個 mapping 由 address 映射 uint,用於儲存每位會員的電力系統狀態。
-

pragma solidity ^0.8.11;
contract Practice{
enum status {malfunction, insufficient, surplus}
struct System {
mapping(address => bool) whitelist;
mapping(address => uint) electricity;
mapping(address => status) UserStatus;
}
}