Язык Solidity: Массивы и соответствия (Урок 4)
Язык Solidity: Массивы и соответствия (Урок 4)
Предыдущие уроки:
Язык Solidity: Неllo World (Урок 1)
Язык Solidity: Типы данных (Урок 2)
Язык Solidity: Переменные состояния контракта (Урок 3)
Массивы позволяют упорядоченно хранить значения одинакового типа. Это удобно, когда требуется работать с данными связанными общим признаком (например, товар, цена и т.п.). Естественно в финансовых скриптах, массивы очень важные средства для разработки. В Solidity их можно разделить на 2 типа: обычные индексные массивы и соответствия (похожи на ассоциативные массивы). Я придумал несколько простых примеров, которые показывают как работать с массивами. Сами по себе эти контракты просто хранят данные в массивах. Нам же они интересны в целях обучения.
Массив строк
Первый пример контракта, позволяет хранить строки в кодировке UTF-8 в массиве. Наподобие как это бы можно было сохранять в текстовом файле обычной операционной системы.pragma solidity ^0.4.0;
contract Lines {
// Массив строк
string[] lines;
// Возвращает кол-во строк
function getLinesCount() constant returns (uint) {
return lines.length;
}
// Добавляет строку
// Строка должна быть в двойных кавычках ("str")
function addLine(string s) {
lines.push(s);
}
// Возвращает последнюю строку (pop)
function getLastLine() constant returns (string) {
return lines[lines.length-1];
}
// Возвращает строку по ее индексу в массиве с 0
function getLineByIndex(uint index) constant returns (string) {
if(index >= 0) {
return lines[index];
} else {
return "empty";
}
}
}
string[] lines;
- это и есть наш индексный массив строк.
У массивов есть члены - метод push() добавляет значение типа массива в его конец, а свойство length поваляет узнать количество элементов которые мы добавили в массив.
В нашей функции getLastLine()
мы воспроизводим поведение функции pop() - обратной push().
Она возвращает последний добавленный в массив элемент (т.е с самым большим индексом): return lines[lines.length-1];
.
Так как отсчет элементов в массивах начинается с 0, а при каждом добавлении значение свойства length увеличивается на 1, мы вычитаем единицу, чтобы получить правильный индекс элемента.
В функции getLineByIndex(uint index)
мы также ни чего не придумываем и минимальное значение параметра index также равно 0.
Массив чисел
Числовые массивы ведут себя также как и строковые:
pragma solidity ^0.4.0;
contract Arrays {
/* contract stat property */
uint32[] nums;
/* contract functions */
function getNumsCount() returns (uint) {
return nums.length;
}
// Добавляем элемент в массив
function addNum(uint32 n) {
nums.push(n);
}
function printNums() constant returns (uint32[]) {
return nums;
}
function getNum(uint i) constant returns (uint32) {
return nums[i];
}
function getLastNum() constant returns (uint32) {
return nums[nums.length-1];
}
}
Все что мы делаем для создания массива - ставим квадратные скобки после типа в объявлении переменной (массива).
Также в этом примере у нас появилась функция printNums()
, которая выводит весь массив наших 32-битных без знаковых чисел (uint32).
Соответствие (mapping)
Соответствия похожи на массивы, но не индексные. Соответственно у них нет членов push() и length.
За то в место индекса (ключа) можно использовать значение любого из доступных типов Solidity.
Например, можно соотнести никнейм пользователя с некоторым значением (у нас это уелое беззнаковое (uint8) число.
А сам ник представлен последовательностью байтов длинной 30 байт (bytes30) (Я выбрал наугад длину). Максимальная длина bytes - 32 байта (bytes32), если бы нам понадобилась более длинная строка для ключа, нужно бы было взять за тип ключа string.
pragma solidity ^0.4.0;
contract Mapp {
/*
Так определяется соответствие -
подобие ассоциативного массива
первый аргумент соответствия (ключ) у нас
имеет тип bytes30 - последовательность в 30 байт
этого хватит для хранения ника
этот ник соответствует заданному для него числу
*/
mapping(bytes30 => uint8) userVal;
// Определим функции для работы с нашим соответствием
function addItem(bytes30 nick, uint8 val) {
// Добавляются элементы в соответствие подобно массивам
userVal[nick] = val;
}
// Получить значение по никнейму
function getItem(bytes30 nick) constant returns (uint8) {
return userVal[nick];
}
}
Кстати, если вы будите эксперементировать с добавлением ключа и значения, не забудьте никнейм взять в двойные кавычки: "mynick". Иначе интерпретатор не поймет, что вы хотите передать строку и выдаст ошибку несоответствия типов.
Чем же так хороши соответствия. Дело в том, что во время транзакции контракту приходят данные состояния блока (принцип наподобие как подобные данные получает большинство ботов в Golos-е, но сами данные другие, так как блокчейн Ethereum) и в том числе там есть адрес отправителя транзакции.
Если задать ключ соответствия типа address, то мы сможем ассоциировать отправителя транзакции (адрес аккаунта - пользователя контракта) с некоторыми данными, например, с некоторым балансом.
Именно по этому принципу создаются подвалюты (контракты подвалют, банки) в блокчейне Ethereum.
Я думаю в следующем уроке следует рассмотреть структуры, так как они относятся к данным, а затем рассмотрим системные функции и переменные Ethereum, сообщающие нам всю необходимую информацию, для создания полноценного контракта.