OberonCore
https://forum.oberoncore.ru/

Стандартная библиотека контейнеров и алгоритмов
https://forum.oberoncore.ru/viewtopic.php?f=47&t=4246
Страница 2 из 3

Автор:  Ярослав Романченко [ Суббота, 09 Февраль, 2013 00:38 ]
Заголовок сообщения:  Re: Стандартная библиотека контейнеров и алгоритмов

В той-же Delphi, это всё сделано в виде довольно лаконичной и наглядной библиотечки, код которой можно смотреть-изучать.
Ну, естественно, в языке дополнительным смыслом нагрузили "<" и ">".
Я что-то подобное конструировал http://sage.com.ua/ru.shtml?e1l5
И когда делал это, не покидало чувство, что приходится изголяться, что-бы реализовать такую простую вобщем-то идею... И то что получается всё-равно ещё не совсем то что нужно.

viewtopic.php?f=22&t=3863

Автор:  Пётр Кушнир [ Суббота, 09 Февраль, 2013 14:58 ]
Заголовок сообщения:  Re: Стандартная библиотека контейнеров и алгоритмов

Ярослав Романченко писал(а):
Я что-то подобное конструировал http://sage.com.ua/ru.shtml?e1l5
А, понятно, у меня тоже часто возникали подобные структуры над списками из Lists, получается, вроде, решение для "сочетания" своего и чужого, то, о чём инфо21 сказал, что это мол, сложно.

Автор:  Ярослав Романченко [ Суббота, 09 Февраль, 2013 15:23 ]
Заголовок сообщения:  Re: Стандартная библиотека контейнеров и алгоритмов

Хм, а что сложного-то? Наоборот, повышение уровня абстракции.
Или повсюду в коде должны просматриваться кишки в виде операций по манипуляции указателями и приведению типов?
Вполне читабельный код получается :)
Код:
   PROCEDURE SubsetConstruct*(nfa: RegExpNFA.NFA): RegExpDFA.DFA;
   VAR
      dfa: RegExpDFA.DFA;
      trans: RegExpMaps.Transition;
      lstlstMarked, lstlstUnmarked: Lists.LongintListList;
      mapDFAStateNum: RegExpMaps.DFAStateNumMap;
      lstNFAInitial, lstFirst, lstAState, lstNext: Lists.LongintList;
      iNewState, iInput, i: LONGINT;
      
      PROCEDURE gen_new_state(): LONGINT;
      BEGIN
         INC(iNewState);
         RETURN iNewState
      END gen_new_state;
      
   BEGIN
      NEW(dfa);
      NEW(lstNFAInitial, {});
      NEW(lstlstMarked, {Lists.LIST_SORTED, Lists.LIST_NO_DUPLICATES});
      NEW(lstlstUnmarked, {Lists.LIST_SORTED, Lists.LIST_NO_DUPLICATES});
      NEW(mapDFAStateNum);
      iNewState := 0;
      lstNFAInitial.Add(nfa.iInitial);
      lstFirst := EpsClosure(nfa, lstNFAInitial);
      lstlstUnmarked.Add(lstFirst);
      dfa.iInitial := gen_new_state();
      mapDFAStateNum.Add(lstFirst, dfa.iInitial);
      WHILE ~lstlstUnmarked.IsEmpty() DO
         lstAState := lstlstUnmarked.GetItem(0);
         lstlstUnmarked.Remove(0);
         lstlstMarked.Add(lstAState);
         IF lstAState.IndexOf(nfa.iFinal) # -1 THEN
            i := mapDFAStateNum.IndexOf(lstAState);
            IF i # -1 THEN
               dfa.lstFinal.Add(mapDFAStateNum.GetItem(i).iState)
            END
         END;
         FOR iInput := 0 TO nfa.lstInputs.GetCount() - 1 DO
            lstNext := EpsClosure(nfa,
               Move(nfa, lstAState, nfa.lstInputs.GetItem(iInput)));
            IF (lstlstUnmarked.IndexOf(lstNext) = -1) &
               (lstlstMarked.IndexOf(lstNext) = -1)
            THEN
               lstlstUnmarked.Add(lstNext);
               mapDFAStateNum.Add(lstNext, gen_new_state())
            END;
            i := mapDFAStateNum.IndexOf(lstAState);
            IF i # -1 THEN
               trans := RegExpMaps.NewTransition(
                  mapDFAStateNum.GetItem(i).iState,
                  nfa.lstInputs.GetItem(iInput));
               i := mapDFAStateNum.IndexOf(lstNext);
               IF (i # -1) & (dfa.mapTransition.IndexOf(trans) = -1) THEN
                  dfa.mapTransition.Add(trans,
                     mapDFAStateNum.GetItem(i).iState)
               END
            END
         END
      END;
      dfa.nStates := mapDFAStateNum.GetCount();
      RETURN dfa
   END SubsetConstruct;

То-же на C++:
Код:
DFA subset_construct(NFA nfa)
{
   DFA dfa;

   // state_rep: a set of NFA states which is represented by some DFA state
   //
   typedef set<state> state_rep;
   set<state_rep> marked_states;
   set<state_rep> unmarked_states;
   
   // gives a number to each state in the DFA
   //
   map<state_rep, state> dfa_state_num;
   
   set<state> nfa_initial;
   nfa_initial.insert(nfa.initial);
      
   // initially, eps-closure(nfa.initial) is the only state in the DFAs states
   // and it's unmarked
   //
   state_rep first(build_eps_closure(nfa, nfa_initial));
   unmarked_states.insert(first);
   
   // the initial dfa state
   //
   state dfa_initial = gen_new_state();
   dfa_state_num[first] = dfa_initial;
   dfa.start = dfa_initial;
   
   while (!unmarked_states.empty())
   {
      // Take out one unmarked state and mark it (remove from the unmarked set,
      // insert into the marked set)
      //
      state_rep a_state = *(unmarked_states.begin());
      unmarked_states.erase(unmarked_states.begin());
      marked_states.insert(a_state);
      
      // If this state contains the NFA's final state, add it to the DFA's set
      // of final states
      //
      if (a_state.find(nfa.final) != a_state.end())
         dfa.final.insert(dfa_state_num[a_state]);
      
      // for each input symbol the nfa knows
      //
      for (set<input>::const_iterator inp_i = nfa.inputs.begin(); inp_i != nfa.inputs.end(); ++inp_i)
      {
         // next state
         //
         state_rep next = build_eps_closure(nfa, nfa.move(a_state, *inp_i));
         
         // if we haven't examined this state before, add it to the unmarked
         // states, and make up a new number for it
         //
         if (unmarked_states.find(next) == unmarked_states.end() &&
            marked_states.find(next) == marked_states.end())
         {
            unmarked_states.insert(next);
            dfa_state_num[next] = gen_new_state();
         }
         
         dfa.trans_table[make_pair(dfa_state_num[a_state], *inp_i)] = dfa_state_num[next];
      }
   }
   
   return dfa;
}


PS.
http://svn.assembla.com/svn/oberonru/tr ... /Lists.Mod
http://svn.assembla.com/svn/oberonru/tr ... es/RegExp/

Автор:  Пётр Кушнир [ Суббота, 09 Февраль, 2013 16:08 ]
Заголовок сообщения:  Re: Стандартная библиотека контейнеров и алгоритмов

Ну да, я тоже к этому пришёл не сразу, но из обычных средств, доступных в КП, это достаточно удобный вариант, плюс такую обёртку можно наращивать всякими важными в контексте проекта, штуками.
А вообще, раз уж мы не можем уйти от определения сущностей для списка в своём проекте, вот ещё вариант, определяем сразу контейнер для выполнения той или иной операции.
Код:
MODULE ListsOp;

   IMPORT
      Meta,
      Kernel, SYSTEM;
   
   CONST
      dataName* = 'data';
      
   TYPE
      List* = POINTER TO LIMITED RECORD
         root, last: Item;
      END;
      
      Item = POINTER TO RECORD
         data: ANYPTR;
         next: Item;
      END;
      
      AddOp* = ABSTRACT RECORD END;
      
      Host = RECORD END;
      
      MyData = POINTER TO RECORD END;
      
      MyAddOp* = RECORD (AddOp)
         data*: MyData;
      END;
      
   VAR
      host: Host;
   
   PROCEDURE (VAR h: Host) Lookup (VAR r: ANYREC; OUT i: Meta.Item), NEW;
      VAR type: Kernel.Type; mod: Kernel.Module; attr: Kernel.ItemAttr;
   BEGIN   (* create a meta item for a global variable passed as VAR parameter *)
      attr.obj := Meta.varObj;
      attr.typ := Meta.recTyp;
      attr.vis := Meta.exported;
      attr.adr := SYSTEM.ADR(r);
      attr.mod := NIL;
      attr.desc := SYSTEM.VAL(Kernel.Type, SYSTEM.TYP(r));
      attr.ptr := NIL;
      attr.ext := NIL;
      attr.mod := NIL;
      Meta.GetThisItem(attr, i)
   END Lookup;   
      
   PROCEDURE GetData(VAR rec: ANYREC): ANYPTR;
      VAR sc: Meta.Scanner; i: Meta.Item; vs: Meta.Name; data: ANYPTR;
   BEGIN
      host.Lookup(rec, i);
      sc.ConnectTo(i);
      WHILE ~sc.eos & (data=NIL) DO
         sc.Scan; sc.GetObjName(vs);
         IF (sc.this.obj#Meta.undef) & (vs$=dataName) THEN
            data:=sc.this.PtrVal();
         END
      END;
   RETURN data
   END GetData;
   
   PROCEDURE (l: List) Add(x: ANYPTR), NEW;
      VAR i: Item;
   BEGIN
      NEW(i); i.data:=x;
      IF l.last=NIL THEN l.root:=i; l.last:=i
      ELSE l.last.next:=i; l.last:=i END;
   END Add;
   
   PROCEDURE (l: List) Do (VAR op: ANYREC), NEW;
      VAR data: ANYPTR;
   BEGIN
      WITH op: AddOp DO
         data:=GetData(op);
         ASSERT(data#NIL, 40);
         l.Add(data)
      ELSE HALT(100) END;
   END Do;
      
   PROCEDURE New*(): List;
      VAR l: List;
   BEGIN
      NEW(l);
   RETURN l
   END New;
   
   PROCEDURE Test*;
      VAR i: INTEGER; ao: MyAddOp; l: List;
   BEGIN
      l:=New();
      i:=0;
      WHILE i<10 DO
         NEW(ao.data);
         l.Do(ao);
         INC(i);
      END;
      HALT(0);
   END Test;
   
BEGIN

END ListsOp.

(!)ListsOp.Test


На хост-часть можно не смотреть :D
Ну и, если таким же способом определить остальные операции, то мета поможет нам установить данные нашего типа в наш контейнер-операцию.

Автор:  Пётр Кушнир [ Воскресенье, 10 Февраль, 2013 19:20 ]
Заголовок сообщения:  Re: Стандартная библиотека контейнеров и алгоритмов

Пётр Кушнир писал(а):
определяем сразу контейнер для выполнения той или иной операции. <...>
Ну и, если таким же способом определить остальные операции, то мета поможет нам установить данные нашего типа в наш контейнер-операцию.

В общем, реализовал такой способ, в виде полноценного линейного списка, ничего страшного нет в нём, даже, некоторое однообразие привнесено.
Ещё раз поясню, первая идея была в том, чтобы в модуле Списка был определён специальный контейнер для расширения в клиентских модулях и агрегирования в контейнер поля нужного типа. А потом я понял, что в итоге все методы списка будут работать с этим контейнером, а при наличии WITH что методы, что хэндлер, всё едино. В итоге, получилось даже веселее, например, операция сортировки опирается на сравнение, соответственно, компаратор должен принимать два объекта из списка, так вот, оказалось, что можно эти два поля записать через Meta в поля расширения.
Скорее всего, конечно, упадёт производительность, ну да он и не для того был придуман, чтобы в гонках участвовать, а уж по сравнению с каким-нибудь Си на неуправляемой памяти, любой список в ББ врятли блеснёт скоростью.

Вложения:
Op.odc [12.58 КБ]
Скачиваний: 752

Автор:  Евгений Темиргалеев [ Понедельник, 11 Февраль, 2013 09:27 ]
Заголовок сообщения:  Re: Стандартная библиотека контейнеров и алгоритмов

Пётр Кушнир писал(а):
...модуле Списка ... получилось даже веселее, например, операция сортировки опирается на сравнение, соответственно, компаратор должен принимать два объекта из списка, так вот, оказалось, что можно эти два поля записать через Meta в поля расширения.
Скорее всего, конечно, упадёт производительность, ну да он и не для того был придуман, чтобы в гонках участвовать, а уж по сравнению с каким-нибудь Си на неуправляемой памяти, любой список в ББ врятли блеснёт скоростью.
1) Если делать по-простому, как в Си (где нету Meta), то и в константе (на работу через Meta) разницы не будет.
2) Метод управления памятью влияет только на время создания нового элемента. Вычислительная сложность при работе со списками от этого дела зависит константно; на сортировку же вообще не влияет, если использовать соотв. алгоритм.

Автор:  Пётр Кушнир [ Понедельник, 11 Февраль, 2013 09:51 ]
Заголовок сообщения:  Re: Стандартная библиотека контейнеров и алгоритмов

ну, в ListsLinear используется алгоритм Горячева из динамических строк, насколько я помню этот метод выделения памяти дал максимум производительности на ББ, там внутри есть NEW, это очевидно.

А сортировка не относилась к абзацу про скорость работы.

Автор:  Ярослав Романченко [ Четверг, 28 Февраль, 2013 14:34 ]
Заголовок сообщения:  Re: Стандартная библиотека контейнеров и алгоритмов

Внёс некоторые новшества и улучшения в свою библиотеку контейнеров.
List переименовал в Vector, поскольку, если рассуждать логически, это именно вектор на базе динамического массива. Настоящий List ещё необходимо будет реализовать на базе двухсвязного списка.
Реализовал Dictionary на базе хеш-таблицы. Хеш-таблица простейшая на базе динамического массива с простым механизмом разрешения коллизий и возможностью роста. Все возможные размеры таблицы заранее заданы табличкой простых чисел. При росте хеш-таблицы, позиции элементов пересчитываются с новым модулем, при этом хеш-коды элементов берутся из кэша, так-как операция взятия хеш-кода может быть достаточно медленной.
На базе Dictionary можно строить контейнеры типа Set и типа Map. В контейнере Set как-правило хранятся только ключи, и они-же являются и данными. В контейнере Map хранятся пары ключ:значение.
Реализовал двоичную кучу BinaryHeap.
Конейнер под любой тип данных описывается очень быстро, в библиотеке в качестве примера реализованы: вектор целых (LongintVector), вектор векторов целых (LongintVectorVector), вектор строк (StringVector), множество целых (LongintSet), множество множеств целых (LongintSetSet), куча целых (LongintHeap).
Dictionary получился достаточно быстрым. Добавление в LongintSetSet 100000 множеств LongintSet, содержащих в свою очередь до 21 случайного целого, с предварительной проверкой на вхождение в множество выполняется чуть менее чем за секунду. Для сравнения, заполнение LongintSet 100000 случайных целых с предварительной проверкой занимает порядка 70 мс. Без предварительной проверки будет просто останов по ASSERTу, поскольку Dictionary предполагает хранение лишь уникальных ключей.
http://svn.assembla.com/svn/oberonru/tr ... ainers.Mod

Автор:  Ярослав Романченко [ Четверг, 28 Февраль, 2013 14:52 ]
Заголовок сообщения:  Re: Стандартная библиотека контейнеров и алгоритмов

Код:
MODULE ContainersTest; (** AUTHOR ""; PURPOSE ""; *)

IMPORT
   Commands, Containers, Random, PreciseTimer;

PROCEDURE HashTest*(context: Commands.Context);
CONST
   SETS = 100000;
   TRIES = 20;
VAR
   r: Random.Generator;
   set: Containers.LongintSet;
   setset1, setset2: Containers.LongintSetSet;
   i, j, n, iTry: LONGINT;
   t: HUGEINT;
BEGIN
   context.out.Ln;

   NEW(r);

   t := PreciseTimer.GetTicks();
   
   FOR iTry := 0 TO TRIES - 1 DO
      NEW(set);
      FOR i := 0 TO SETS - 1 DO
         n := r.Dice(MAX(LONGINT));
         IF ~set.Contains(n) THEN
            set.Add(n)
         END
      END
   END;
   
   t := PreciseTimer.GetTicks() - t;
   
   context.out.String("Count: ");
   context.out.Int(set.GetCount(), 0);
   context.out.Ln;
   context.out.String("Collisions: ");
   context.out.Int(set.dictionary.nCollisions, 0);
   context.out.Ln;
   context.out.String("Time: ");
   context.out.Float(PreciseTimer.GetTime(t) / TRIES, 15);
   context.out.Ln;
   context.out.Update;
   
   NEW(setset1);
   FOR i := 0 TO SETS - 1 DO
      NEW(set);
      FOR j := 0 TO 20 DO
         n := r.Dice(MAX(LONGINT));
         IF ~set.Contains(n) THEN
            set.Add(n)
         END
      END;
      IF ~setset1.Contains(set) THEN
         setset1.Add(set)
      END
   END;
   
   context.out.String("Sets generated");
   context.out.Ln;
   context.out.Update;
   
   t := PreciseTimer.GetTicks();
   
   FOR iTry := 0 TO TRIES - 1 DO
      NEW(setset2);
      setset1.Reset;
      WHILE setset1.HasNext() DO
         set := setset1.GetNext();
         IF ~setset2.Contains(set) THEN
            setset2.Add(set)
         END
      END
   END;

   t := PreciseTimer.GetTicks() - t;

   context.out.String("Count: ");
   context.out.Int(setset2.GetCount(), 0);
   context.out.Ln;
   context.out.String("Collisions: ");
   context.out.Int(setset2.dictionary.nCollisions, 0);
   context.out.Ln;
   context.out.String("Time: ");
   context.out.Float(PreciseTimer.GetTime(t) / TRIES, 15);
   context.out.Ln;
   
END HashTest;

BEGIN
END ContainersTest.

ContainersTest.Test ~
ContainersTest.HashTest ~
SystemTools.Free ContainersTest Containers ~

Count: 99997
Collisions: 232111
Time:   7.291155E-002
Sets generated
Count: 100000
Collisions: 255930
Time:   9.049281E-001

Автор:  Пётр Кушнир [ Вторник, 11 Март, 2014 20:27 ]
Заголовок сообщения:  Re: Стандартная библиотека контейнеров и алгоритмов

Реализовывал ли кто-нибудь структуру хранения произвольных данных (рекордов с полями примитивных типов) в Stores.Store (ну или просто в массиве байтов) с интерфейсом, подобным дин.массиву?

Автор:  Роман М. [ Вторник, 11 Март, 2014 23:33 ]
Заголовок сообщения:  Re: Стандартная библиотека контейнеров и алгоритмов

Пётр Кушнир писал(а):
Реализовывал ли кто-нибудь структуру хранения произвольных данных (рекордов с полями примитивных типов) в Stores.Store (ну или просто в массиве байтов) с интерфейсом, подобным дин.массиву?
Нет, лично не пробовал. И смысла не вижу. Изобретаем свою БД?

Автор:  Пётр Кушнир [ Вторник, 11 Март, 2014 23:38 ]
Заголовок сообщения:  Re: Стандартная библиотека контейнеров и алгоритмов

Ну почему сразу БД, может быть изобретаем контейнер для IPC.

Автор:  Роман М. [ Вторник, 11 Март, 2014 23:44 ]
Заголовок сообщения:  Re: Стандартная библиотека контейнеров и алгоритмов

Пётр Кушнир писал(а):
Ну почему сразу БД, может быть изобретаем контейнер для IPC.
Насколько мне известно, Stores работает на файловой основе, что возможно излишне для выполнения сетевых операций (сокет, к примеру).

Автор:  Пётр Кушнир [ Вторник, 11 Март, 2014 23:50 ]
Заголовок сообщения:  Re: Стандартная библиотека контейнеров и алгоритмов

Для этого есть memory mapped files (ну, реализованы уже в ББ). В этом плане еще интересно попробовать что-то типа lock-free структур, даже в наивной реализации, имеющимися средствами.

Автор:  Роман М. [ Вторник, 11 Март, 2014 23:56 ]
Заголовок сообщения:  Re: Стандартная библиотека контейнеров и алгоритмов

Допустим так. Неясно как тогда происходит отправка сообщения. Ведь приёмник должен постоянно считывать файл.

Автор:  Пётр Кушнир [ Вторник, 11 Март, 2014 23:58 ]
Заголовок сообщения:  Re: Стандартная библиотека контейнеров и алгоритмов

Не, суть именно в том, что это структура типа массива. Нужно хранение в массиве всяких структур. Допустим оно будет.
А далее интересно, как там должно быть, например, с обработкой массива, который того и гляди изменится.

Автор:  Пётр Кушнир [ Среда, 12 Март, 2014 00:03 ]
Заголовок сообщения:  Re: Стандартная библиотека контейнеров и алгоритмов

Но пока интересно только запилить типа "массив".
Профит в том, что память у двух ББ общая. А значит запись-чтение будет происходить один раз, как будто этот список у нас в памяти.

Автор:  Роман М. [ Среда, 12 Март, 2014 00:06 ]
Заголовок сообщения:  Re: Стандартная библиотека контейнеров и алгоритмов

Пётр Кушнир писал(а):
Не, суть именно в том, что это структура типа массива. Нужно хранение в массиве всяких структур. Допустим оно будет.
А далее интересно, как там должно быть, например, с обработкой массива, который того и гляди изменится.

Тогда можно взять на вооружение метод наподобие тегирования структур в компиляторе КП.
Вместе с данными будут отправляться также служебные данные. Каждому тегу соответствует свой тип данных. Перед отправкой структуры данных подписчикам она сначала описывается с помощью тегов. Таким образом, приёмник будет знать в каком формате эти данные пришли и как их раскодировать.

Допускается ли применение вложенных структур?

Автор:  Роман М. [ Среда, 12 Март, 2014 00:10 ]
Заголовок сообщения:  Re: Стандартная библиотека контейнеров и алгоритмов

Причём, размер структуры перед самими данными отправлять не нужно. Это можно сделать в виде маркера окончания приёма данных.

Автор:  Илья Ермаков [ Среда, 12 Март, 2014 00:12 ]
Заголовок сообщения:  Re: Стандартная библиотека контейнеров и алгоритмов

Я когда-то делал тупо "двоичный дамп" записи в Files.File.
Где-то даже публиковал на форуме, но не ищется!!
Что несложно сделать через Kernel и Type.size.

Процедуры ReadRec/WriteRec, ReadTagged/WriteTagged.
Последние отличались тем, что до собственно данных записи клали 64-бит фингепринт типа.
При чтении проверялось соответствие фингерприта типу RECORD-а, в который пытаемся считывать.

Страница 2 из 3 Часовой пояс: UTC + 3 часа
Powered by phpBB® Forum Software © phpBB Group
https://www.phpbb.com/