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


В следующем уроке рассмотрим структуры и объединения


Comments 17


В первом примере в последней функции более надежная проверка может быть такой:

if(index >= 0 && index < lines.length) {
            return lines[index];
        }
05.09.2017 11:07
0

обычные индексные массивы и соответствия (похожи на ассоциативные массивы)

mapping — не похожи, а это и есть ассоциативные массивы. Также известны под именами хэш-таблица, словарь, отображение*.

05.09.2017 11:48
0

За то в место индекса (ключа) можно использовать значение любого из доступных типов Solidity.

Ключ как раз не может быть любого типа. Значение — может. А ключ должен быть фиксированного размера, то есть динамические string, bytes (без указания размера) не подходят в вашей версии pragma solidity ^0.4.0. Сейчас строки уже можно использовать. Но ограничения на тип ключа всё равно есть:

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.

05.09.2017 11:53
0

Ага спасибо буду знать.)

05.09.2017 13:00
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

25.08.2018 08:49
0

Добрый день! Все очень интересно! Какой пакет лучше скачать, чтобы хотя бы на локальном блокчейне это все в работе посмотреть? Из того что есть на официальном сайте https://ethereum.org/cli у меня ни один пакет не встает

14.09.2017 11:34
0

Скоро будем тестовую ноду поднимать

15.09.2017 15:50
0

можно на примере конкретной задачи?))

18.09.2017 05:17
0

Еще вопрос- как правильно организовать умный контракт, который обращается к внешнему сайту за информацией? Поместить блок запросов внутрь контракта или создать внешний ресурс, к которому обращается за информацией контракт, а уже этот ресурс получает информацию с сайта и обрабатывает ее?

15.09.2017 08:18
0

А зачем контракту обращаться к внешнему сайту? Контракты обмениваются информацией между собой в блокчейне, а сайты с блокчейном общаются посредством web3 или RPC

15.09.2017 15:52
0

До программирования мне еще далеко. Хочу сначала понять сам принцип. Например, УК должен перевести со счета на счет Эфир при достижении некоторого события во внешнем мире. Например, приход посылки. Пришла посылка или нет надо смотреть на сайте доставки. Как правильно организовать это?

18.09.2017 05:11
0

С Вами где-то еще кроме этого чата пообщаться можно?

18.09.2017 05:11
0

@dosmm Есть специальные сервисы - оракулы. Oraclize как один из примеров.
так же вы можете и сами написать такой сервис. По расписанию ваш сервер проходит по нужным источникам данных и подключаясь к сети ethereum через web3 (например) интерфейс выкладывает в сеть контракт с нужными вам данными.

25.08.2018 09:01
0

@rusldv Благодарю за статьи! Очень полезно!

25.08.2018 09:12
0