Contract-Development

循環遍歷地圖的所有成員

  • November 16, 2019

我需要遍歷地圖的所有成員並更改它們的屬性(這是一個結構)。

我這樣做的方式是在將成員添加到地圖對象時還將其地址保存在數組中,然後循環遍歷數組並從地圖中載入使用者的結構然後進行更改。

struct Users {
   address user;
   uint balance;
   uint bonus;
}

mapping (address => Users) instructors;
address[] public allUsers;

function setInstructor(address _address, uint balance, uint _bonus) onlyOwner public {
   Users storage instructor = instructors[_address];

   instructor.user = _address;
   instructor.balance = _balance;
   instructor.bonus = _bonus;

   allUsers.push(_address) -1;
}

function addBalance(_amount) onlyOwner public {

   uint length = allUsers.length;

   for (uint i = 0; i < length ; i++) {
       Users[allUsers[i]].balance = Users[allUsers[i]].balance + (Users[allUsers[i]].bonus * _amount);
   }
}

我想知道是否有更好的方法?因為使用者可以使用很多,而 for 循環在 gas 使用方面很昂貴。

我應該使用外部伺服器來執行循環嗎?

相信我。每次(每次!)你都在考慮循環一個數據集,在基於區塊鏈的系統中,你無法在“時間開始”預測其維度,你正在使用區塊鏈做錯誤的事情和/或你的算法必須通過重新思考來改變它。

你的模型應該是這樣的,如果添加了一個新的“使用者”,添加它的簡單操作會改變所有需要的東西來恢復集合的相同需要的屬性。

這樣,只有您可以處理無限數量的“使用者”添加,而沒有無限的計算能力。

順序思維方式(排序、使用“for 循環”在數據集中移動等)來自不同的範式:順序機器。基於區塊鏈的系統並非如此。在這裡,他們只能發揮非常有限的作用。

我希望這可以幫助您解決問題。

在您的評論後添加:

我想給你一個範式的例子(不是解決方案的例子,我不知道你的應用程序)。嘗試考慮維護一些資料結構,該結構跟踪您必須對所有餘額執行的操作以更新它們,並且您在特殊時刻更新單個使用者的餘額,fi 當他進行某些操作時,例如,當他查詢以了解他的更新余額時。如果不是,fi,當有人查詢區塊鏈時,沒有人會對更新區塊鏈中的所有值感興趣。你應該,fi,維護從狀態變數開始計算餘額所需的數據記錄,並在使用者詢問餘額時應用計算一次。如果您能夠製作一個能夠代表情況並使用它更新相關數據的更新值向量,則此方法有效。換句話說:如果您需要一個操作(我們稱之為“OP”)來立即更新您的單個數據記錄,它絕對等同於循環遍歷所有單個數據記錄,並逐個更新所有數據記錄,直到完成取消定義 OP 的資訊,或者維護儲存的單個數據記錄和 OP 並僅在需要時應用 OP。第一個是典型的 FORTRAN 類編碼,第二個是典型的區塊鏈方法。第一個要求你對所有使用者有一個 for 循環,對 OP 的所有屬性有一個 for 循環,第二個只需要你對 OP 表示的屬性應用 for 循環。換句話說:如果您需要一個操作(我們稱之為“OP”)來立即更新您的單個數據記錄,它絕對等同於循環遍歷所有單個數據記錄,並逐個更新所有數據記錄,直到完成取消定義 OP 的資訊,或者維護儲存的單個數據記錄和 OP 並僅在需要時應用 OP。第一個是典型的 FORTRAN 類編碼,第二個是典型的區塊鏈方法。第一個要求你對所有使用者有一個 for 循環,對 OP 的所有屬性有一個 for 循環,第二個只需要你對 OP 表示的屬性應用 for 循環。換句話說:如果您需要一個操作(我們稱之為“OP”)來立即更新您的單個數據記錄,它絕對等同於循環遍歷所有單個數據記錄,並逐個更新所有數據記錄,直到完成取消定義 OP 的資訊,或者維護儲存的單個數據記錄和 OP 並僅在需要時應用 OP。第一個是典型的 FORTRAN 類編碼,第二個是典型的區塊鏈方法。第一個要求你對所有使用者有一個 for 循環,對 OP 的所有屬性有一個 for 循環,第二個只需要你對 OP 表示的屬性應用 for 循環。它絕對等同於循環遍歷所有單個數據記錄,並一一更新所有這些記錄,直到完成取消定義 OP 的資訊,或者維護儲存的單個數據記錄和 OP,並在需要時應用 OP只要。第一個是典型的 FORTRAN 類編碼,第二個是典型的區塊鏈方法。第一個要求你對所有使用者有一個 for 循環,對 OP 的所有屬性有一個 for 循環,第二個只需要你對 OP 表示的屬性應用 for 循環。它絕對等同於循環遍歷所有單個數據記錄,並一一更新所有這些記錄,直到完成取消定義 OP 的資訊,或者維護儲存的單個數據記錄和 OP,並在需要時應用 OP只要。第一個是典型的 FORTRAN 類編碼,第二個是典型的區塊鏈方法。第一個要求你對所有使用者有一個 for 循環,對 OP 的所有屬性有一個 for 循環,第二個只需要你對 OP 表示的屬性應用 for 循環。

我希望這也能有所幫助!

您使用的模式對某些類似表格的集合有效,但您應該注意這些限制。

不建議對循環的合約做任何事情 - Getting Loopy with Solidity

但是,您可以使客戶端循環。應該理解,在這種情況下,“客戶端”是一個鏈下實體,因為如果不是,那麼它會做一些循環的事情。

struct Users {
   // address user; <=== not needed
   uint balance;
   uint bonus;
}

mapping (address => Users) instructors;
address[] public allUsers;

function userCount() public view returns(uint) {
   return allUsers.length;
}

function userAtIndex(uint row) public view returns(address, uint, uint) {
   address userAddr = allUsers[row];
   Users storage u = instructors[userAddr];
   return (userAddr, u.balance, u.bonus);

您不需要結構中的地址,因為要到達那裡,您必須在映射中找到它。如果你能做到這一點,你已經知道了。userCount()是告訴客戶端要迭代多少行,因為通過擷取錯誤(越界時)來發現似乎是錯誤的形式。這不是絕對必要的。

如果我搞砸了這個例子,請原諒我的錯別字。我沒有編譯它。

你說你想更新所有東西的屬性。考慮一種策略,例如將其寫在一個地方。還要考慮到,理論上,如果一個值在循環內的程式碼塊中是可計算的,那麼它可能是即時可計算的。寫入本身是乙太坊中非常昂貴的操作,因此您需要節省這些操作。

allUsers.push(_address) -1;返回數組中剛剛推送值的行。這在依賴指針的系統中很有用。一個例子是使用它來啟用刪除的CRUD模式。您沒有將它用於任何事情(uint row = allUsers.push(_address) -1;),所以allUsers.push(_address);就足夠了。它沒有損壞,-1只是無關緊要。

希望能幫助到你。

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