Здравствуйте.
Анализируя свой код подробнее, стремясь выяснить, что и как работает, хотел бы получить у уважаемых адептов JASS разъяснение важного для меня вопроса о том, как две конструкции JASS обходятся с памятью.
Есть блок кода, который можно впихнуть в любую бибилотеку или область
globals
    unit array UnitIndexer // Конечно, даже по меркам vJASS, если написать UnitIndexer[размер_массива_ниже_8191], это будет синтаксический сахар, так как массивы в WarCraft III статические и меньше 8192 ячеек в памяти инициализировать не станут.
    integer Indexer_Index = 0
endglobals

function GetUnitIndex takes unit whichUnit returns integer
    // Знаю, что не самая оптимальная реализация, но задача этого кода - показать суть дела.
    local integer i = 0

    loop
        exitwhen i > Indexer_Index
        if UnitIndexer[i] == whichUnit then
            return i
        endif
        set i = i + 1
    endloop

    return -1
endfunction

function IsUnitIndexed takes unit whichUnit returns boolean
    return GetUnitIndex(whichUnit) >= 0
endfunction

function IndexNewUnit takes unit whichUnit returns nothing
    // Не стану показывать проверку на дубликаты и "защиту от дураков", чтобы не усложнять код.
    set UnitIndexer[Indexer_Index] = whichUnit
    set Indexer_Index = Indexer_Index + 1
endfunction
И есть другой блок кода - иной подход к реализации того же самого
globals
    group UnitIndexer = null
endglobals

function IsUnitIndexed takes unit whichUnit returns boolean
    return IsUnitInGroup(whichUnit,UnitIndexer)
endfunction

function IndexNewUnit takes unit whichUnit returns nothing
    if UnitIndexer == null then
        set UnitIndexer = CreateGroup()
    endif
    call GroupAddUnit(UnitIndexer,whichUnit)
endfunction
Какой способ лучше с точки зрения памяти?
С массивами - понятно, создаётся 8191 экземпляр для хранения ссылки на объект unit.
Группа видится мне динамическим массивом, который аллоцирует и деаллоцирует память по необходимости.
Расскажите мне, как на самом деле обстоит дело.
В ожидании ответа,
Singularity, 29.06.2017

Принятый ответ

Странное понимание механики. Не бывает универсального лучшего способа, потому и существуют разные способы для конкретных ситуаций.
А экономить байты и такты процессора, заранее пользуясь интерпретируемым скриптовым языком, это вообще моветон.
Разве массив в WarCraft III не предынициализирует 8192 ячейки памяти (по Вашей формуле, в моём случае он потребляет 8192*4=32768 байт, то есть 32Кб)? Он ведь не динамический.
Нет, он динамический. Исходный размер при создании - 1024. И расширяется на 1024 ячейки по мере доступа вплоть до максимальных 8192.
`
ОЖИДАНИЕ РЕКЛАМЫ...

Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
0
16
7 лет назад
Отредактирован DracoL1ch
0
Ну вот из псевдокода по IsUnitInGroup:
v5 начинается с 0-го элемента списка
while ( *(_DWORD *)(v5 + 8) != a2 )
{
v5 = *(_DWORD *)(v5 + 4);
if ( v5 <= 0 )
goto LABEL_8;
}
Приведи задачу, где необходима будет именно рекурсия, я че-то не понимаю
0
4
7 лет назад
Отредактирован Singularity
0
Хеш-таблица для чего была придумана? В ней же можно хранить индекс боевой единицы, используя её хендл-идентификатор. А ещё есть Set\GetUnitUserData..., если конечно он не забронирован другой системой.
Стоит отдать хэш-таблице должное - она точно ещё тяжелее группы.
с чего вдруг рекурсивные? Обычные циклы-переборы. На Си скорость выходит такая, что любые костыли-попытки на жассе в пролете.
Собственно, моя задача в том, чтобы сделать быструю систему с максимальным взаимодействием с ядром WarCraft III, то есть плагин, а не костыль.
Вот и ищу способ.
В надежде на ответ,
Singularity, 29.06.2017
0
32
7 лет назад
0
Ну дык пиши весь код карты на асме или вовсе инжекти мемхаком дллку, на Си все будет ближе и быстрее.
9
26
7 лет назад
Отредактирован Hanabishi
9
Странное понимание механики. Не бывает универсального лучшего способа, потому и существуют разные способы для конкретных ситуаций.
А экономить байты и такты процессора, заранее пользуясь интерпретируемым скриптовым языком, это вообще моветон.
Разве массив в WarCraft III не предынициализирует 8192 ячейки памяти (по Вашей формуле, в моём случае он потребляет 8192*4=32768 байт, то есть 32Кб)? Он ведь не динамический.
Нет, он динамический. Исходный размер при создании - 1024. И расширяется на 1024 ячейки по мере доступа вплоть до максимальных 8192.
Принятый ответ
0
4
7 лет назад
Отредактирован Singularity
0
Нет, он динамический. Исходный размер при создании - 1024. И расширяется на 1024 ячейки по мере доступа вплоть до максимальных 8192.
Спасибо за пояснение, я этого не знал. То есть, сначала инициализируется 1024 ячейки, когда происходит доступ к следующим 1024-м, инициализируется ещё 1024 и это происходит до максимума в 8192, понял ли я это верно?
Мдамс, над статьёй работать ещё и работать...
Засучив рукава,
Singularity, 29.06.2017
0
30
7 лет назад
0
любую рекурсию можно превратить в цикл
Разумеется, да, только далеко не всегда целесообразно так делать.
DracoL1ch:
Ну вот из псевдокода по IsUnitInGroup
Так лол, сишные компиляторы всегда переводят хвостовую рекурсию в плоскую итерацию, ЕМНИП.

Минут через несколько вкину пример, в котором, на мой взгляд, применение рекурсии весьма оправдано.
Время программиста всегда дороже времени процессора \о/
0
26
7 лет назад
0
о есть, сначала инициализируется 1024 ячейки, когда происходит доступ к следующим 1024-м, инициализируется ещё 1024 и это происходит до максимума в 8192, понял ли я это верно?
Да.
Разумеется, да, только далеко не всегда целесообразно так делать.
Так лол, сишные компиляторы всегда переводят хвостовую рекурсию в плоскую итерацию, ЕМНИП.
Вроде как нет, от того в вещах типа быстрой сортировки и случается переполнение стека вызовов. Но в случае вара это вообще пофиг, так как ограничение на на количество операций.
0
30
7 лет назад
Отредактирован Clamp
0
пример
struct Foo
{
    thistype prev = 0;
    thistype next = 0;
    // ...

    thistype getFirst() {
        if (this.prev != 0) {
            return prev.getFirst();
        }
        return this;
    }
};

Foo getFirstOf(Foo given) {
    if (given.prev != 0) {
        return getFirstOf(given.prev);
    }
    return given;
}

Foo createListAndReturnLastObject(int listLenght) {
    Foo st_1 = Foo.create();
    Foo st_2;
    int i = 0;
    while (i++ < listLenght) {
        st_2 = Foo.create();
        st_1.next = st_2;
        st_2.prev = st_1;
        st_1 = st_2;
    }
    return st_2;
}

void Test() {
    Foo bar = getFirstOf(createListAndReturnLastObject(31));
}
То есть сам список как объект не обязательно описывать, ИМО. Вообще, я не вполне уверен в теме, так что буду рад исправлениям и пояснениям, если они уместны. Код на JASS, но логика вполне понятна и отлично переносится в цпп. Трахаться с указателями по памяти влом, нет IDE под рукой.

Разумеется, можно запариться и сделать через цикл, но это будет довольно-таки перегруженный и тяжело воспринимаемый код, как по мне.
1
28
7 лет назад
1
 thistype getFirst() { 
	if (this.prev != 0) { 
		return getFirst(); 
	} 
	return this; 
}
бесконечная рекурсия
0
30
7 лет назад
Отредактирован Clamp
0
nvc123, с чего бы это?

А, лол, да.

Подправил.
2
28
7 лет назад
2
твой вариант
Foo getFirstOf(Foo given) { 
	if (given.prev != 0) { 
		return getFirstOf(given.prev); 
	} 
	return given; 
} 
легко заменяется на
Foo getFirstOf(Foo given) { 
	Foo temp=given;
	while (temp.prev != 0) { 
			temp=temp.prev; 
	} 
	return temp; 
} 
что довольно легко воспринимается и не является перегруженным
рекурсия нужна в основном в операциях над деревьями и сетями
т.е. там где происходит ветвление
а список как и массив это "прямая дорога" и с ними лучше работать циклами
Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.