При разработке интерактивных интерфейсов в 1С, таких как игровые механики или динамические формы, программисты часто сталкиваются с необходимостью перемешивания элементов (например, картинок). В процессе реализации этой задачи возникают две основные сложности: ошибки обращения к индексам и отсутствие видимых изменений на форме. В этой статье мы подробно разберем, почему возникают исключения, как правильно работать с коллекциями и какими способами можно добиться реального перемешивания объектов.
Разберем типичную ситуацию: программист создает СписокЗначений или Массив, добавляет туда 10 элементов и пытается получить случайный элемент. Основная ловушка кроется в системе нумерации. В 1С, как и во многих языках программирования, индексация коллекций начинается с нуля.
Проанализируем ошибочный код, который часто встречается у новичков:
СПЗ = Новый СписокЗначений;
// Добавляем 10 элементов...
ГСЧ = Новый ГенераторСлучайныхЧисел;
СЧ = ГСЧ.СлучайноеЧисло(1, СПЗ.Количество());
Элемент = СПЗ.Получить(СЧ); // Ошибка здесь!
Выясним причину падения программы: если в списке 10 элементов, их индексы — это числа от 0 до 9. Метод Количество() вернет число 10. Если генератор случайных чисел выдаст число 10, метод Получить(10) вызовет исключение, так как элемента с таким индексом не существует. Правильный диапазон для ГСЧ всегда должен быть: ГСЧ.СлучайноеЧисло(0, СПЗ.Количество() - 1).
Рассмотрим изящный способ перемешивания элементов, который предложили участники форума. Вместо того чтобы вручную менять элементы местами, мы можем использовать встроенный механизм сортировки СпискаЗначений.
Суть метода заключается в том, что в качестве Значения элемента мы сохраняем сам объект (например, декорацию формы), а в Представление записываем случайное число. После этого вызываем стандартный метод сортировки.
Разберем по шагам реализацию этого алгоритма:
ГенераторСлучайныхЧисел. Желательно инициализировать его один раз, чтобы последовательность была действительно случайной.СписокЗначений нашими объектами.Посмотрим на пример кода:
ГСЧ = Новый ГенераторСлучайныхЧисел;
СПЗ = Новый СписокЗначений;
// Добавляем ссылки на элементы формы (картинки)
СПЗ.Добавить(Элементы.Декорация2, Строка(ГСЧ.СлучайноеЧисло(1000, 9999)));
СПЗ.Добавить(Элементы.Декорация3, Строка(ГСЧ.СлучайноеЧисло(1000, 9999)));
СПЗ.Добавить(Элементы.Декорация4, Строка(ГСЧ.СлучайноеЧисло(1000, 9999)));
СПЗ.Добавить(Элементы.Декорация5, Строка(ГСЧ.SлучайноеЧисло(1000, 9999)));
// Сортируем по строковому представлению (по случайным числам)
СПЗ.СортироватьПоПредставлению();
// Теперь значения в списке расположены в случайном порядке
Результат = СПЗ.ВыгрузитьЗначения();
Если нам нужно перемешать уже существующий массив без использования промежуточных списков, рассмотрим классический алгоритм Фишера-Йетса. Он считается наиболее эффективным и исключает повторение элементов.
Проанализируем логику: мы проходим по массиву с конца к началу и на каждом шаге меняем текущий элемент местами со случайным элементом, стоящим перед ним.
Процедура ПеремешатьМассив(Массив)
ГСЧ = Новый ГенераторСлучайныхЧисел;
вв = Массив.Количество() - 1;
Пока вв > 0 Цикл
СлучайныйИндекс = ГСЧ.СлучайноеЧисло(0, вв);
// Меняем местами элементы
ВременнаяПеременная = Массив[вв];
Массив[вв] = Массив[СлучайныйИндекс];
Массив[СлучайныйИндекс] = ВременнаяПеременная;
вв = вв - 1;
КонецЦикла;
КонецПроцедуры
Этот метод гарантирует, что каждый элемент окажется на новом месте с равной вероятностью, и мы не выйдем за границы диапазона, так как индекс СлучайныйИндекс всегда ограничен текущим значением счетчика вв.
Важный момент, который часто упускают: перемешивание элементов в программном массиве никак не влияет на их расположение на форме. Если вы просто перемешали переменные в коде, декорации (картинки) останутся на своих местах в интерфейсе.
Выясним, как заставить картинки реально перемещаться. Рассмотрим два подхода:
Способ А: Программное перемещение элементов формы
У коллекции Элементы есть метод Переместить(). Мы можем программно изменить порядок подчиненных элементов в группе. Рассмотрим пример, где мы перемешиваем имена декораций и переставляем их:
// Предположим, СписокИмен содержит строки "Декорация2", "Декорация3" и т.д. в случайном порядке
ГруппаРодитель = Элементы.ГруппаКартинок;
Для Каждого ИмяЭлемента Из СписокИмен Цикл
ТекущийЭлемент = Элементы[ИмяЭлемента];
Элементы.Переместить(ТекущийЭлемент, ГруппаРодитель);
// Элемент будет перемещен в конец группы
КонецЦикла;
Способ Б: Подмена пути к данным
Вместо того чтобы двигать сами объекты декораций, мы можем создать на форме 10 реквизитов (например, ПутьККартинке1, ПутьККартинке2) и привязать каждую декорацию к своему реквизиту. В этом случае нам достаточно перемешать значения в этих реквизитах, и 1С сама обновит визуальное отображение.
В обсуждении на форуме упоминалась ошибка при использовании метода СПЗ.Удалить(СЧ). Рассмотрим, почему это опасно. Когда вы удаляете элемент из середины коллекции, все элементы, стоящие справа, сдвигаются влево. Их индексы уменьшаются на единицу.
Если вы используете цикл Для ч = 0 По Количество-1 и внутри удаляете элементы, то во второй половине цикла вы обязательно получите ошибку "Индекс находится за границами", так как общее количество элементов уменьшилось, а счетчик цикла продолжает расти до старого значения.
Правило: если необходимо удалять элементы в цикле по индексу, всегда делайте это с конца коллекции (обратный цикл):
Для ч = -(СПЗ.Количество() - 1) По 0 Цикл
Индекс = -ч;
// Логика проверки...
СПЗ.Удалить(Индекс);
КонецЦикла;
Для успешного решения задачи перемешивания в 1С необходимо соблюдать следующие правила:
0, а последний — это Количество() - 1.ГенераторСлучайныхЧисел корректно, задавая правильные границы.СписокЗначений с сортировкой по случайному представлению.Элементы.Переместить() или меняйте значения реквизитов, к которым привязаны визуальные элементы.Соблюдение этих принципов позволит вам создавать стабильный код без досадных ошибок обращения к памяти и обеспечит корректную работу интерфейса вашего приложения.