olety / cjass

Preserving cjass code from code.google.com/p/cjass
0 stars 0 forks source link

for loop for units in group #3

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
The syntax is:

for (unit u; UnitsInGroup) {}

But I have some problems with it:

Тут я пишу по русски, т.к. сложно нормально 
высказать мысль. Итак, Нам надо перебрать 
юнитов в группе.

Еще раз напомню, что цикл по группе с 
удалением юнитов быстрее, чем функции для 
работы с группа, создающие потоки, и в 
последних версиях они не вызывают утечек.

Предполагалось использовать вторую 
группу, в которую бы копировались юниты из 
первой, и потом по ней шел цикл, который был 
возвращал юнитов в исходную группу и делал 
действия над ними:

loop
   set u = FirstOfGroup(base)
   exitwhen (u == null)
   call GroupRemoveUnit(base, u)
   call GroupAddUnit(additional, u)
endloop

loop
   set u = FirstOfGroup(additional)
   exitwhen (u == null)
   call GroupRemoveUnit(additional, u)
   call GroupAddUnit(base, u)
   // actions
endloop

Проблема в том, что крайне не оптимальным в 
таком варианте будет удаление юнитов из 
базовой группы:

for (unit u; UnitsInGroup(gr) {
    if (b) {GroupRemoveUnit(gr, u)}
}

В данном случае получится удаление, и потом 
добавление ради удаления. Я не хочу 
включать явно не оптимизированные 
конструкции в код. Поэтому я думаю сделать 
какой либо аналог сохранения юнита:

for (unit u; UnitsInGroup(gr) { SaveUnit(u); /* action */ }

В данном варианте действия будут 
проводиться в первом цикле, а SaveUnit (назовем 
его пока так) будет превращаться в 
копирование юнита во временную группу:

loop 
   set u = FirstOfGroup(base)
   exitwhen (u == null)
   call GroupRemoveUnit(base, u)
   // actions
   call GroupAddUnit(additional, u) // there is
endloop
loop
   set u = FirstOfGroup(additional)
   exitwhen (u == null)
   call GroupRemoveUnit(additional, u)
   call GroupAddUnit(base, u)
endloop

В таком случае также необходимо проверять 
наличие отсутствия этого сохранения юнита 
и вообще не добовлять второй цикл.

Any ideas? 

Original issue reported on code.google.com by adi...@gmail.com on 6 Aug 2011 at 10:22

GoogleCodeExporter commented 9 years ago
Напрашивается три разных варианта:
1. "Разбор" даной группы без создания другой:
 loop
   set u = FirstOfGroup(base)
   exitwhen (u == null)
   call GroupRemoveUnit(base, u)
   // actions
 endloop
2. "Перекачка" данной группы во временную и 
назад:
 loop
   set u = FirstOfGroup(base)
   exitwhen (u == null)
   call GroupRemoveUnit(base, u)
   call GroupAddUnit(additional, u)
 endloop
 loop
   set u = FirstOfGroup(additional)
   exitwhen (u == null)
   call GroupRemoveUnit(additional, u)
   call GroupAddUnit(base, u)
   // actions
 endloop
3. "Копирование" данной группы ForGroup-ом во 
временную и циклом уже по ней:
 set bj_groupAddGroupDest = additional
 call ForGroup(base, function GroupAddGroupEnum)
 loop
   set u = FirstOfGroup(additional)
   exitwhen (u == null)
   call GroupRemoveUnit(additional, u)
   // actions
 endloop

Преимущества и недостатки:
1. "Разбор"      - максимальная скорость,    
уничтожает группу      - наверное, самый 
частый вариант
2. "Перекачка"   - скорость в 2 раза меньше, 
восстанавливает группу - если вдруг группа 
будет нужна
3. "Копирование" - скорость в 3 раза меньше, не 
изменяет группу     - если группа 
понадобится внезапно

Предлагаю разный синтаксис:
1. for (unit u; FromGroup(gr)) { /* action */ }                  //no units in 
gr after this
2. for (unit u; InGroup(gr))   { /* action */ }                  //all units 
are back
3. for (unit u; any_expression_return_group) { /* action */ }    //fail safe 
variant

Original comment by sbratchi...@gmail.com on 6 Aug 2011 at 12:18

GoogleCodeExporter commented 9 years ago
Вот у меня первый вопрос такой - возможно ли 
вообще определить, содержит ли 
пользовательское действие в себе чистку 
группы (с помощью GroupRemoveUnit), или же не 
содержит? 
Если можно, тогда нужно просто сделать два 
варианта обработки, один как адик сказал в 
начале, с копированием во вспомогательную 
группу. Другой - там где groupremoveunit, чтобы не 
было "добавления ради удаления".

Да, еще чистить исходную группу крайне 
нежелательно. "Исходные данные портить 
нехорошо" - из-за этого получис минус на 
контрольной по праку на 1м курсе =)
Например, у меня иногда после одного 
использования группы идет другое. Редко, но 
бывает. Но никто не знает, насколько часто 
это может быть в других картах.

Original comment by evgkoc...@gmail.com on 6 Aug 2011 at 2:46

GoogleCodeExporter commented 9 years ago
В принципе отследить GroupRemoveUnit() можно, хотя 
и ненадёжно.
А вот что делать, если GroupRemoveUnit() вызывается 
с условием?
По моему мнению чаще всего группа больше не 
нужна.
А третий вариант (параноидальный) - на 
всякий случай, вдруг какое событие 
активизируется и проверит "исходные 
данные".

Различать нужна/не нужна группа толком 
может только автор кода карты.
Поэтому и предложил три варианта.

Original comment by sbratchi...@gmail.com on 6 Aug 2011 at 4:56

GoogleCodeExporter commented 9 years ago
[deleted comment]
GoogleCodeExporter commented 9 years ago
[deleted comment]
GoogleCodeExporter commented 9 years ago
> 1. "Разбор" даной группы без создания 
другой:
Это самый простой вариант, и да, я 
планировал его сделать, но выделить как 
либо синтаксисом, т.е. что бы указывать то, 
что группа может быть очищена в заголовке 
цикла.
> 3. "Копирование" данной группы ForGroup-ом во 
временную и циклом уже по ней:
Тут вся фишка в том, что создание новых 
потоков в ForGroup и GroupEnum (последнему можно 
передать null в качестве фильтра и это не 
вызывает утечек в новых патчах вара) 
заметно быстрее этих же функции, если они 
создают потоки для каждого юнита.

Спасибо ShadowDemon за тесты:

{{{
min max
156.198 163.610

  // actions before script runs
  set i_temp = 0

  // your script here
  call GroupEnumUnitsInRange(gr, 0.0, 0.0, 2000.0, null)
  loop
    set u = FirstOfGroup(gr)
    exitwhen (u == null)
    call GroupRemoveUnit(gr, u)
    call GroupAddUnit(gr2, u)

    set i_temp = i_temp + 1
  endloop

  loop
    set u = FirstOfGroup(gr2)
    exitwhen (u == null)
    call GroupRemoveUnit(gr2, u)
    call GroupAddUnit(gr, u)
  endloop

//===========================

min max
147.114 150.468

function CALLBACK takes nothing returns nothing
  set u_temp = GetEnumUnit()
  set i_temp = i_temp + 1
endfunction

  // actions before script runs
  set i_temp = 0
  call GroupEnumUnitsInRange(gr, 0.0, 0.0, 2000.0, null)

  // your script here
  call ForGroup(gr, function CALLBACK)
}}}

Ну не быстрее, но не медленнее) Одним словом 
если изначально пройдемся ForGroup для 
копирования - потеряем все быстродействие.

Есть еще вариант сделать ход конем - а 
именно расшарить статические переменные (я 
о них еще не говорил? static int i = 0 - пистаь 
внутри функции) что бы они работали внутри 
lambda функций - и не делать фор для группы 
вообще.

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

Да, трогать группу не охота, но что 
поделать? Короче пока думаем, я работаю над 
другими фишками.

Original comment by adi...@gmail.com on 6 Aug 2011 at 5:15

GoogleCodeExporter commented 9 years ago
GroupEnum... здесь ни при чём, поскольку 
разбираем случай, когда группа уже есть.
В тех вариантах, когда группа создаётся 
GroupEnum-ом, имеем право её "разобрать".

Именно различный синтаксис я и предлагаю, 
как показано выше.
Самый медленный вариант failsafe.
Если автор укажет как ускорить - молодец.

Original comment by sbratchi...@gmail.com on 6 Aug 2011 at 5:33

GoogleCodeExporter commented 9 years ago
Я не гонюсь за быстродействием, и мне нужен 
всего лишь цикл для групп. Неважно, что он в 
2 раза медленнее на каком-то очень узком 
подмножестве задач. Так что мне кажется, 
что нужно просто реализовать самый простой 
вариант, где используются две группы (и 
который не запарывает исходную группу). 
Потом в последующих релизах можно 
оптимизировать сколько влезет.

Original comment by evgkoc...@gmail.com on 7 Aug 2011 at 6:50

GoogleCodeExporter commented 9 years ago
> Потом в последующих релизах можно 
оптимизировать сколько влезет.
Иногда из-за кривого базового дизайна 
потом что то оптимизануть может быть 
невозможно.

Ну в целом я согласен, можно сделать 
базовый "стабильный" вариант, и написать 
большими и красными буквами, что он 
медленный, но безопасный. И все равно, я 
пока не вижу "правильного" синтаксиса, 
например это:

1. for (unit u; FromGroup(gr)) { /* action */ }                  //no units in 
gr after this
2. for (unit u; InGroup(gr))   { /* action */ }                  //all units 
are back
3. for (unit u; any_expression_return_group) { /* action */ }    //fail safe 
variant

Будет приводить к путанице.

Original comment by adi...@gmail.com on 7 Aug 2011 at 7:46

GoogleCodeExporter commented 9 years ago
Как насчет forg(unit u; g)

Original comment by evgkoc...@gmail.com on 7 Aug 2011 at 9:07

GoogleCodeExporter commented 9 years ago
> Как насчет forg(unit u; g)
Точно нет, т.к. не наглядно. Вот если взять 
фор (не тот что по юнитам, вообще), он 
читается как "для каждого Х, который 
удовлетворяет условиям У", т.е. for (unit u; 
UnitIn***()) - куда логичнее, и опять же потом 
можно сделать новый вариант перебора, не 
ломая логику старого.

Original comment by adi...@gmail.com on 8 Aug 2011 at 10:47

GoogleCodeExporter commented 9 years ago
Есть еще идея место временной группы 
складывать юнитов в массив, что то вроде 
стека - будет быстрее... при расчете, что 8190 
юнитов должно хватить всем.

Original comment by adi...@gmail.com on 8 Aug 2011 at 12:26

GoogleCodeExporter commented 9 years ago
Один универсальный самый лучший вариант 
всё равно не существует.
Если группа больше не нужна, глупо тратить 
ресурсы на её восстановление.
Если группа будет ещё нужна, её 
восстановление необходимо.
Это уже два принципиально разных варианта. 
Третий параноидальный не настаиваю.
Массив сгладит разницу, но принципиально 
не уберёт.
Хочешь сделать качественно и 
оптимизированно - делай разные варианты.

Original comment by sbratchi...@gmail.com on 8 Aug 2011 at 2:18

GoogleCodeExporter commented 9 years ago
Да, согласен. Тогда по синтаксису у нас 
выходит:

for (unit u; UnitsInGroup(...)) {} - копируем юнитов из 
основной во временную через ForGroup(), и далее 
берем из временной.

for (unit u; UnitsInGroup(...); GroupRemovePickedUnit()) {} - Просто 
цикл по основной группе, GroupRemovePickedUnit() - 
некая псевдофункция, просто указывает 
парсеру тип перебора. Почему не GroupRemoveUnit(g, u) 
? Тут есть сложность, например проверить в 
таком случае юнита можно, а группу сложно - 
for (unit u; UnitsInGroup(GetGroup()); GroupRemoveUnit(GetGroup(), u) {} - 
где group GetGroup () {return myGroup[GetRandomInt(0, 255)]} - думая 
суть ясна. Вопрос только, как назвать эту 
псевдофункцию? GroupRemovePickedUnit() - первое, что 
пришло на ум.

Original comment by adi...@gmail.com on 9 Aug 2011 at 6:51

GoogleCodeExporter commented 9 years ago
А чем тебе 
for (unit u; UnitsFromGroup(g)) {}
for (unit u; UnitsInGroup(g)) {}
for (unit u; (g)) {}
не нравится? Главное - только одна ";". Две 
для нормального фора.

Кстати нужен способ цепляние строк внутри 
фора:

for (unit u=FirstOfGroup(g); not(g==null); 
(GroupRemoveUnit(g,u);u=FirstOfGroup(g)) ) {}

Original comment by sbratchi...@gmail.com on 9 Aug 2011 at 2:05

GoogleCodeExporter commented 9 years ago
> не нравится?
Возможной путаницей. Читается фор так:
Для каждого юнита У, удовлетворяющего 
условие - ЮнитвВГруппе(Г), применить 
дейтсвие.
В си в обычном форе есть также в скобочках 
дополнительный аргумент, что надо сделать 
потом, вот туда мы и суем это. Одним словом я 
считаю это решение правильным, и уже 
реализовал его)

> for (unit u=FirstOfGroup(g); not(g==null); 
(GroupRemoveUnit(g,u);u=FirstOfGroup(g)) ) {}
Сомнительно.

Итого, реализовал пока так:

for (unit u; UnitsInGroup(expr)) {}
for (unit u; UnitsInGroup(expr); GroupRemovePickedUnit()) {}

Имя последней можно заменить. Ну что, 
эпичный был баг, но думаю можно скоро крыть 
его)

Всем спасибо за идеи и моральную поддержку.

Original comment by adi...@gmail.com on 9 Aug 2011 at 7:12

GoogleCodeExporter commented 9 years ago
Хозяин-барин, конечно.

Я предполагал, что for с двумя ";" можно 
раскладывать в цикл loop практически без 
разбора, а с одной ";" использовать для 
групповых операций.
for (unit u; UnitsInGroup(expr); GroupRemovePickedUnit()) {} - пол 
текста - названия псевдофункций. Много.

В си в обычном форе при необходимости 
уместить две операции можно использовать 
запятую.
Тут запятой нет, поэтому я предложил такой 
синтаксис:

for ( (int x=0;unit u) ; (u=ar[x];(not u==null)) ; (ar[x]=null;x++) ) {}

В заголовке фора точки с запятой делят 
"аргументы". Каждый "аргумент" в скобках 
может быть поделен другими точками с 
запятыми на несколько частей. Последняя 
часть второго "аргумента" является 
условием.
Не хочешь - я настаивать не буду. Просто 
может быть полезным.

Original comment by sbratchi...@gmail.com on 10 Aug 2011 at 2:12