XGM Forum
Сайт - Статьи - Проекты - Ресурсы - Блоги

Форуме в режиме ТОЛЬКО ЧТЕНИЕ. Вы можете задать вопросы в Q/A на сайте, либо создать свой проект или ресурс.
Вернуться   XGM Forum > Warcraft> Академия: форум для вопросов> Jass
Ник
Пароль
Войти через VK в один клик
Сайт использует только имя.

Ответ
 
NCrashed

offline
Опыт: 13,553
Активность:
[cJass] Списки ака динамические массивы

Введение

Удивительно, что в джаззе изначально нету ни многомерных массивов ни динамических массивов. Я решил устранить второй недостаток. Реализовано на структурах и новых фичах cJass'а, поэтому многим будет просто полезно изучить систему для более полного представления, как можно пользоваться дефайном, условной трансляцией, сокращенной записью функций и т.п. Ввиду стандартных ограничений структур, для каждого типа списков макс. число элементов равно 8192. Выбор организации структурами связан с несовместимостью 23 и 24 версий вара и быстродействием структур.

Особенности

1. Динамичность (отсутствует строгое определение размера)
2. Множество стандартных операций
3. Использование cJass, что позволило сократить код втрое, если не больше

Области применения

1. Из списков прекрасно получаются стеки и очереди
2. Массивы с количеством элементов [2000,8192]
3. Нестандартные бд

А что такое список 0.o?

Список - структура для хранения информации, состоящий из элементарных ячеек. В каждой ячейке есть непосредственное полезное значение и указатель на следующую ячейку. Обычно хранят только "голову" списка.

Список функций

list.Create(int k) - создает список/первую ячейку со значением k
list.AddHead(type m) - добавление в голову списка, обязательно надо переназначить голову!! list = list.AddHead(m)
list.AddTail(type m) - Добавление в хвост списка
list.Head() - Возвращает значение головы списка
list.Last() - Возвращает значение последнего элемента списка
list.Begin()- Создает список, в который копируются все элементы, кроме последнего
list.Tail() - Создает список, в который копируются все элементы кроме первого
list.Copy() - Создает список, в который копируются все элементы из старого
list.Length() - Возвращает длину списка
list.Member(type m) - Возвращает тру, если элемент m есть в списке
list.DelElement(type m) - Удаляет первый элемент m, не удаляет голову, возвращает false при неудаче
list.DelHead() - Удаляет голову
list.DelTail() - Удаляет последний элемент
list.Destroy() - Уничтожение списка
» Код системы
/* 
    v.1.1
    Библиотека, описывающая списки - массивы с неопределенным размером (максимальный 8192, это ограничение массива структур)
    Список - последовательность элементов, каждый элемент имеет кокнкретное значение и ссылку на следующий.
    Головой называют первый элемент списка, хвостом все остальные. При операции удаления, добавления головы нужно не забывать
    переназначать переменную, в которой хранится голова. Пример использования:
    realList L = realList.Create(23)
    L.AddTail(42)
    L = L.AddHead(16) или ListAddHead(L,15)
    
    Для работы необходим cJass v1.4 или выше. 
    
    Особые благодарности проекту XGM. ©NCrashed 2009
*/
library ListLib { 

#include "cj_types_priv.j"

define {
        ListAddHead(list,m) = list = list.AddHead(m) // для тех кто забывает переназначить голову списка
        ListDelHead(list) = list = list.DelHead()
        
        DefineOfList(type) = { // общие функции для всех списков
            
            static type##List Create(type m) { // создает список с 1 элементом k
                type##List L = type##List.create()
                L.k = m
                return L
            }
            
            type##List AddHead(type m) { // добавление в голову списка, обязательно надо переназначить голову!! list = list.AddHead(m)
                type##List L = type##List.Create(m)
                L.next = this
                return L
            }
    
            void AddTail(type m) { // Добавление в хвост списка
                type##List L = type##List.Create(m)
                while ( this.next != null) {this = this.next}
                this.next = L
            }
    
            type Head() { // Возвращает значение головы списка
                return this.k
            }
        
            type Last() { // Возвращает значение последнего элемента списка
                while ( this.next != null) {this = this.next}
                return this.k
            }
    
            type##List Begin() { // Создает список, в который копируются все элементы кроме последнего
                if this.next == 0 {return 0} 
                type##List h = type##List.Create(this.k)
                this = this.next
                type##List Result = h
                while (this.next != null) {
                    type##List L = type##List.Create(this.k)
                    h.next = L
                    h = L
                    this = this.next
                }
                return Result
            }
    
            type##List Tail() { // Создает список, в который копируются все элементы кроме первого
                if this.next == 0 {return 0}
                type##List h = type##List.Create(this.next.k)
                this = this.next.next
                type##List Result = h
                while (this != null) {
                    type##List L = type##List.Create(this.k)
                    h.next = L
                    h = L
                    this = this.next
                }
                return Result 
            }
    
            type##List Copy() { // Создает список, в который копируются все элементы из старого
                type##List h = type##List.Create(this.k)
                this = this.next
                type##List Result = h
                while (this != null) {
                    type##List L = type##List.Create(this.k)
                    h.next = L
                    h = L
                    this = this.next
                }
                return Result 
            }
        
            int Length() { // Возвращает длину списка
                int Result = 0
                while (this != null) { this = this.next; Result++ }
                return Result
            }
        
            bool Member(type m) { // Возвращает тру, если элемент m есть в списке
                while (this != null) {if this.k == m {return true};this=this.next}
                return false
            }
            
            bool DelElement(type m) { // Удаляет первый элемент m
                if this.Head() == m {ListDelHead(this);return true}
                while (this.next != 0) {
                    if this.next.k == m {
                        type##List h = this.next
                        this.next = this.next.next
                        h.destroy()
                        return true
                    }
                    this=this.next
                }
                return false
            }
            
            type##List DelHead() { // Удаляет голову
                type##List L = this.next
                if this != 0 {this.destroy()}
                return L
            }
            
            void DelTail() { // Удаляет последний элемент
                while ( this.next.next != null) {this = this.next}
                if this != null {this.next.destroy();this.next = 0}
            }
            
            void Destroy() { //Уничтожение списка
                type##List L
                while (this != null) {
                        L = this; this = this.next
                        L.next = 0
                        #if type == int
                            L.k = 0
                        #elseif type == real 
                            L.k = 0.
                        #else
                            L.k = null
                        #endif
                        L.destroy()
                }
            }        
        }
}

// Общая схема создания списка для типа type
//struct typeList {
//    type k = 0
//    typeList next = 0
//
//    DefineOfList(type)
//
//    Далее ваши методы
//}

// Наиболее часто употребляемые списки
struct integerList {
    int k = 0 // значение элемента
    integerList next = 0 // ссылка на следующий элемент

    DefineOfList(int) // вызов макроса, который определяет стандартные действия
    
    #if DEBUG // Для дебага списка, вставляется только, когда стоит галка JassHelper->Debug Mode
        void DBPrint() {
            ClearTextMessages()
            while (this != null) {
                BJDebugMsg(I2S(this.k))
                this = this.next
            }
        }
    #endif
}

struct realList {
    real k = 0.
    realList next = 0

    DefineOfList(real)
    
    #if DEBUG 
        void DBPrint() {
            ClearTextMessages()
            while (this != null) {
                BJDebugMsg(R2S(this.k))
                this = this.next
            }
        }
    #endif
}

struct unitList {
    unit k = null
    unitList next = 0
    
    DefineOfList(unit)
    
    #if DEBUG 
        void DBPrint() {
            ClearTextMessages()
            while (this != null) {
                BJDebugMsg(GetUnitName(this.k)+" ("+R2S(GetUnitX(this.k))+","+R2S(GetUnitY(this.k))+")")
                this = this.next
            }
        }
    #endif
}

struct destructableList {
    destructable k = null
    destructableList next = 0
    
    DefineOfList(destructable)
}

struct timerList {
    timer k = null
    timerList next = 0
    
    DefineOfList(timer)
}


}

Особые благодарности

Проекту XGM и создателям cJass'а. Без дефайна получилось бы не так красиво.

История версий

1.1
  • Поправлена функция DelElement, теперь она удаляет и голову
1.0
  • Релиз
Прикрепленные файлы
Тип файла: rar ListLib.rar (1.9 Кбайт, 23 просмотров )
Тип файла: rar ListLib_1.1.rar (1.8 Кбайт, 18 просмотров )

Отредактировано NCrashed, 11.10.2009 в 12:52.
Старый 09.10.2009, 21:09
NCrashed

offline
Опыт: 13,553
Активность:
И молчание... ну хоть какую нибудь критику отписали.
Старый 10.10.2009, 14:22
ScorpioT1000
Работаем
online
Опыт: отключен
Мало кому пригодится.. вот avl-деревья было бы поинтересней)
Старый 10.10.2009, 14:27
adic3x

offline
Опыт: 108,439
Активность:
я бы с удовольствием отписался, но дико занят войной с вексом=) мб чуточку позже?
кстате, неплохобы с самой библиоткой представлять карту - демку. я же надеюсь это расчитано на подключение инклюдом?
Старый 10.10.2009, 14:41
JamesBlack
black mind
offline
Опыт: 6,595
Активность:
Списки это, конечно, очень хорошо... Только не думаю, что они так уж часто пригодятся. Хотя уже пару идей в голове есть, в каких случаях их можно использовать, но они довольно нетривиальны.
[off]Вот если бы кто-нибудь дал работать с памятью... указателями... Можно было б горы свернуть!..[/off]
Старый 10.10.2009, 14:57
adic3x

offline
Опыт: 108,439
Активность:
Вот если бы кто-нибудь дал работать с памятью... указателями... Можно было б горы свернуть!
Dark Dragon писал начто подобное (но понятно что средствами жасса), только вот где оно - хз
Старый 10.10.2009, 15:04
NCrashed

offline
Опыт: 13,553
Активность:
ScorpioT1000, а где в варе авл дерево может пригодиться?
ADOLF, подключается инклудом, собственно для наглядного теста нужно будет еще спелл экзотический написать, попробуем.
А с указателями, наверняка, под 23 версию написано с рб.
Старый 10.10.2009, 15:53
ZeToX2007

offline
Опыт: 7,009
Активность:
Мой любимый вопросс: Зачем это ?
вроде бы у векса есть статичные, динамические массивы, этого мало ? -_-
Старый 11.10.2009, 11:38
NCrashed

offline
Опыт: 13,553
Активность:
про динамические я не слышал, вот статичные у него точно есть.
Зачем это ?
В областях применения написано, а так чтобы просто было.
Главная идея - теоретическая неограниченность списка (из-за структур на каждый тип списков отводится 8196 ячеек), то есть можно использовать в моей же FDL, где для хранения тел используются массивы с ограниченным объемом, а со списками этот тонкий момент можно обойти.
+Прекрасный метод использования дефайна - легкая организация наследования методов.
Старый 11.10.2009, 11:47
ZeToX2007

offline
Опыт: 7,009
Активность:
NCrashed:
8196
А не 8192) плз выложи код в первом посте под катом )
Старый 11.10.2009, 12:08
Sebra

offline
Опыт: 5,603
Активность:
Я вот посмотрел и возник один вопрос:
Раньше типы "список" и "элемент списка" были разные.
Я так сильно устарел или сейчас так модно?
Старый 11.10.2009, 12:09
NCrashed

offline
Опыт: 13,553
Активность:
Хмм, а зачем делать отдельный тип для головы списка, если она сама является элементом списка, вообще за голову списка можно взять любой элемент из цепочки, только предыдущие элементы потеряются. Список определяется как последовательность элементов, в которых есть основная часть и ссыль на следующий элемент. У меня списки однонаправленные и некольцевые
ZeToX2007, да 8192, ошибся. сейчас выложу
Старый 11.10.2009, 12:17
Sebra

offline
Опыт: 5,603
Активность:
Собственно по большей части затем, чтобы избежать
list = list.AddHead(m)
и ещё
list.DelElement(type m) - Удаляет первый элемент m, не удаляет голову
Значит, чтобы удалить кого-то из списка я должен проверять, голова он или нет. Нехорошо.
Старый 11.10.2009, 12:33
NCrashed

offline
Опыт: 13,553
Активность:
То есть ты предлагаешь создать отдельную структуру - голова списка, в которой есть только ссыль на первый элемент? Тогда мы лишаемся уникальной возможности назначать головой любой из элементов в середине списка.
list = list.AddHead(m)
Специально определили процедуру (кстати процедуры с одним параметром реализуемы только в cJass), чтобы скрыть эту особенность.
list.DelElement(type m)
Согласен, что нехорошо. Исправляется одним движением руки, сейчас поправлю
NCrashed добавил:
оплошность исправлена 1 оператором, версия обновлена
            bool DelElement(type m) { // Удаляет первый элемент m
                if this.Head() == m {ListDelHead(this);return true}
                while (this.next != 0) {
                    if this.next.k == m {
                        type##List h = this.next
                        this.next = this.next.next
                        h.destroy()
                        return true
                    }
                    this=this.next
                }
                return false
            }
Старый 11.10.2009, 12:51
Sebra

offline
Опыт: 5,603
Активность:
Да, я забыл спросить ещё, почему ты не используешь thistype ?
А ты на практике проверил, что твоё исправление действительно убирает голову?
Очень сомневаюсь.
Тогда мы лишаемся уникальной возможности назначать головой любой из элементов в середине списка.
...теряя всё, что до головы или путая списки между собой.
Я и не говорю, что этот способ плохой, просто он имеет неприятные особенности.
Старый 11.10.2009, 13:01
NCrashed

offline
Опыт: 13,553
Активность:
А ты на практике проверил, что твоё исправление действительно убирает голову?
На практике проверена функция удаления головы и старая удаления из середины, поводов для багов в новой нет.
...теряя всё, что до головы или путая списки между собой.
А кто сказал, что мы переназначим при этом переменную , в которой хранилась голова? Можно завести указатель на середину списка, не теряя при этом первую половину
и не говорю, что этот способ плохой, просто он имеет неприятные особенности.
Списки не для новичков. Ответственность за утерю головы на совести программиста. Излишнее упрощение приводит только к потере возможностей и понимания истинного положения вещей (яркий пример VCL Delphi)
Старый 11.10.2009, 14:10
Sebra

offline
Опыт: 5,603
Активность:
NCrashed:
А ты на практике проверил, что твоё исправление действительно убирает голову?
На практике проверена функция удаления головы и старая удаления из середины, поводов для багов в новой нет.
В новую функцию указатель this передаётся по значению, соответственно указатель головы она поменять не может никак. Проверь.
...теряя всё, что до головы или путая списки между собой.
А кто сказал, что мы переназначим при этом переменную , в которой хранилась голова? Можно завести указатель на середину списка, не теряя при этом первую половину
И удаляя элемент из одного списка ты можешь удалить его и из дгугого.
А что будет, если ты удалишь у другого списка голову? 8-(
и не говорю, что этот способ плохой, просто он имеет неприятные особенности.
Списки не для новичков. Ответственность за утерю головы на совести программиста. Излишнее упрощение приводит только к потере возможностей и понимания истинного положения вещей (яркий пример VCL Delphi)
Да понятно, что всё на совести програмиста... Но тот, кто в твоей системе разберётся, и сам может такое сделать.
И в любом случае, молодец, что стараешься! :)
пс. В теме про движение ты мне так и не ответил.
Старый 11.10.2009, 16:05
Toadcop

offline
Опыт: 54,313
Активность:
Цитата:
Удивительно, что в джаззе изначально нету ни многомерных массивов ни динамических массивов.
их негде нету это тупо абстракцыя для диапазона памяти =О. фактичски в джасе всё есть. тока макс кусок памяти равен 8192*4 (~32K) байтам (по дефолту)
Старый 12.10.2009, 01:50
NETRAT

offline
Опыт: 83,712
Активность:
вообще-то между списком и массивом есть разница - список, в отличие от массива сам для себя выделяет память и копирует содержимое, но, вообще говоря, списки практически бесполезны, особенно в данном случае, ибо есть массивы, а вот обьекты типа map и hashmap куда полезнее и интересней с точки зрения программирования
Старый 12.10.2009, 11:45
ScorpioT1000
Работаем
online
Опыт: отключен
какой хэшмэп ? на что ты его повесишь, никакого побитового функционала близзы нам не предоставили..
Старый 12.10.2009, 19:18
Ответ

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы можете скачивать файлы

BB-коды Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход



Часовой пояс GMT +3, время: 14:39.