Язык 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, сообщающие нам всю необходимую информацию, для создания полноценного контракта.

Ключ как раз не может быть любого типа. Значение — может.
А ключ должен быть фиксированного размера, то есть динамическиеСейчас строки уже можно использовать. Но ограничения на тип ключа всё равно есть:string
,bytes
(без указания размера) не подходят в вашей версииpragma solidity ^0.4.0
.@zxcat KeyType can be almost any type except for a mapping, a dynamically sized array, a contract, an enum and a struct. ValueType can actually be any type, including mappings.
Тип ключа может быть почти любым. Исключение составляют: mapping, массивы с динамическим размером, enum и struct (структуры). Тип значения может быть любым, включая mapping
Ага спасибо буду знать.)
mapping
— не похожи, а это и есть ассоциативные массивы. Также известны под именами хэш-таблица*, словарь, отображение.Ваш пост поддержали следующие Инвесторы Сообщества "Добрый кит":
t3ran13, chiliec, niiu, gryph0n, vika-teplo, vealis
Поэтому я тоже проголосовал за него!
Узнать подробности о сообществе можно тут:
Разрешите представиться - Кит Добрый
Правила
Инструкция по внесению Инвестиционного взноса
Вы тоже можете стать Инвестором и поддержать проект!!!
Если Вы хотите отказаться от поддержки Доброго Кита, то ответьте на этот комментарий командой "!нехочу"
@rusldv Поздравляю! Вы добились некоторого прогресса на Голосе и были награждены следующими новыми бейджами:
Вы можете нажать на любой бейдж, чтобы увидеть свою страницу на Доске Почета.
Чтобы увидеть больше информации о Доске Почета, нажмите здесь
Если вы больше не хотите получать уведомления, ответьте на этот комментарий словом
стоп
@rusldv Благодарю за статьи! Очень полезно!
Еще вопрос- как правильно организовать умный контракт, который обращается к внешнему сайту за информацией? Поместить блок запросов внутрь контракта или создать внешний ресурс, к которому обращается за информацией контракт, а уже этот ресурс получает информацию с сайта и обрабатывает ее?
@dosmm Есть специальные сервисы - оракулы. Oraclize как один из примеров.
так же вы можете и сами написать такой сервис. По расписанию ваш сервер проходит по нужным источникам данных и подключаясь к сети ethereum через web3 (например) интерфейс выкладывает в сеть контракт с нужными вам данными.
А зачем контракту обращаться к внешнему сайту? Контракты обмениваются информацией между собой в блокчейне, а сайты с блокчейном общаются посредством web3 или RPC
С Вами где-то еще кроме этого чата пообщаться можно?
До программирования мне еще далеко. Хочу сначала понять сам принцип. Например, УК должен перевести со счета на счет Эфир при достижении некоторого события во внешнем мире. Например, приход посылки. Пришла посылка или нет надо смотреть на сайте доставки. Как правильно организовать это?
Добрый день! Все очень интересно! Какой пакет лучше скачать, чтобы хотя бы на локальном блокчейне это все в работе посмотреть? Из того что есть на официальном сайте https://ethereum.org/cli у меня ни один пакет не встает
Скоро будем тестовую ноду поднимать
можно на примере конкретной задачи?))
@rusldv Поздравляю! Вы добились некоторого прогресса на Голосе и были награждены следующими новыми бейджами:
Вы можете нажать на любой бейдж, чтобы увидеть свою страницу на Доске Почета.
Чтобы увидеть больше информации о Доске Почета, нажмите здесь
Если вы больше не хотите получать уведомления, ответьте на этот комментарий словом
стоп
В первом примере в последней функции более надежная проверка может быть такой: