суббота, 20 июля 2013 г.

Пояснение про контейнеры

Речь вот об этом - http://18delphi.blogspot.com/2013/07/2_18.html и http://18delphi.blogspot.com/2013/07/2.html

Говорят - "что-то ты мало объясняешь". Не понимаю правда чтобы такого объяснить. Но попробую.

Тем более, что движок блога так устроен, что сложно перемешивать "картинки", текст и код. Пусть "там" будет код и "картинки", а тут будет "объяснение".

Итак. Что собственно хотелось?

Хотелось нескольких вещей:
1. Проиллюстрировать реальное использование UML для "хардкорной" конечной разработки.
2. Проиллюстрировать возможность создания на Delphi параметризуемых контейнеров в стиле STL (http://ru.wikipedia.org/wiki/%D0%A1%D1%82%D0%B0%D0%BD%D0%B4%D0%B0%D1%80%D1%82%D0%BD%D0%B0%D1%8F_%D0%B1%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA%D0%B0_%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD%D0%BE%D0%B2).
3. Проиллюстрировать создание "атомарных" тестов для DUnit.
4. Чисто "технической". Поскольку всё так или иначе построено на контейнерах, то просто надо "набрать базу". Чтобы при дальнейшем повествовании к этому вопросу больше не возвращаться.

Итак. Что там написано:
1. Инкапсуляция работы с памятью во вспомогательный объект/record. Зачем? Да для начала просто для того, чтобы не "размазывать" GetMem/FreeMem etc по коду контейнера.
2. List.imp.pas - собственно абстрактный контейнер. Опирающийся на ЧЕТЫРЕ абстракции - _ItemType_ (тип элемента) и FreeItem (очистка элемента), FillItem (заполнение элементом ячейки памяти), IsSame (сравнение элементов на тождество). Собтвенно List "предполагает", что эти абстракции где-то определены.
3. Далее на основе указанных абстракций и объекта Tl3PtrLoc List выводит "стандартный" интерфейс контейнера. Такой как:
3.1 Count.
3.2 Capacity
3.3. Items[]
3.4 Insert
3.5 Delete
3.6 Add
3.7 IndexOf
Ну и несколько "косметических" дополнений:
3.8 First
3.9 Last
3.10 Clear
4. Далее там введён AtomicList - контейнер который умеет работать с натуральными числами. Он определяет абстракции IsSame, FreeItem, FillItem.
5. Далее (для примера) введено ТРИ контейнера с разными типами чисел - TIntegerList, TByteList, TInt64List. Они определяют абстракцию _ItemType_ в Integer, Byte, Int64, соответственно.
6. Далее введён "абстрактный" тест - ListTest. Который опирается на абстракцию _ListType_. Он умеет создавать контейнеры для тестирования.
7. Далее введён абстрактный тест AtomicListTest. О иллюстрирует тестирование контейнеров натуральных чисел.
8. Ну и далее введены конкретные тесты - IntegerListTest, ByteListTest, Int64ListTest. ни тестируют соответствующие конкретные контейнеры.
9. Отдельно введён абстрактный контейнер InterfaceRefList - список интерфейсов, управляющий временем жизни своих элементов через счётчик ссылок.
10. Далее для примера введён конкретный контейнер - TIUnknownRefList. Он на самом деле не самоцель, а ПРИМЕР. Для реальной жизни гораздо правильнее инстанцировать конкретные контейнеры параметризованные конкретными интерфейсами. Статическая проверка типов компилятором. Без всяких "шаманств", приведений типов и тем более QueryInterface.
11. Ну и ещё там есть "тест" - IUnknownRefListTest. Он правда пока ничего не делает - лишь создаёт соответствующий контейнер. Чтобы хотя бы проверить, что он компилируется. Тест - потом конечно же дополню.

Вот вкратце всё. Если что-то непонятно - задавайте вопросы. С радостью отвечу. Ну или продолжу пояснения.

34 комментария:

  1. Почему бы просто не использовать Generic'и? Как выглядит использование получившегося контейнера?

    ОтветитьУдалить
  2. Почему бы просто не использовать Generic'и?

    1. Delphi 7 или FPC например.
    2. Судя по QC - не всё хорошо с Generic'ами.
    3. Примеси - гибче.

    Как выглядит использование получившегося контейнера?

    Там же тесты есть.

    Ну или:
    l_List := TIntegerList.Create;
    l_LIst.Add(1000);

    etc.

    ОтветитьУдалить
    Ответы
    1. Я не совсем это имел ввиду. Хотел посмотреть на код вместе с инстанцированием. Но сейчас уже разобрался сам. Громозко, конечно. Но полезно, если других вариантов нет. По мне так вполне интересная идея, а вы говорите "программирование не творческий процесс". =)

      Удалить
    2. По-моему - ничего громоздкого. И уж тем более - ничего творческого. Чисто "шаблоны".

      Надо просто знать что взять и с чем смешать. Чисто по "рецепту".

      type
      _ItemType_ = Integer;
      {$include StandardAtomicList.imp.pas}
      TInterfaceList = class(_StandardAtomicList_);
      end;

      ...

      implementation
      ...

      {$include StandardAtomicList.imp.pas}

      - всё.. По-моему - проще некуда.

      Удалить
    3. А по моему громоздко. Это смотря с чем сравнивать. =)

      Удалить
    4. По сравнению с C++ или Generic'ами - да - наверное.. Хотя - далеко не всё именно количеством строк кода меряется...

      Удалить
    5. Выразительность очень важна для языка программирования. Повышает читаемость и уменьшает вероятность ошибок.

      Удалить
    6. Во-первых - это очень спорный вопрос.
      Во-вторых - что считать выразительностью? Циклы в "стиле C"?
      В-третьих - выразительность != количеству строк кода.

      Удалить
    7. В-четвёртых - там не зря диаграммы нарисованы. По мне - они более, чем выразительны.

      Удалить
    8. Насчет FPC - http://wiki.freepascal.org/Generics

      Удалить
    9. Насчет FPC - http://wiki.freepascal.org/Generics

      Удалить
    10. Выразительность по отношению к языку - возможность кратко(речь о количестве описываемых действий(операций, терминов), а не размерах литералов) описать на нём программу. Попробуйте описать f x = x на своих шаблонах. Или можете предложить что-то более сложное для сравнения выразительности.

      Удалить
    11. "Выразительность по отношению к языку - возможность кратко(речь о количестве описываемых действий(операций, терминов), а не размерах литералов) описать на нём программу."

      В таких критериях - UML'ю - нет равных :-)

      "Попробуйте описать f x = x на своих шаблонах."

      Что ИМЕННО предлагается описать - я не понял.

      Но по существу. "Не учите дедушку кашлять" :-) Простите уж. Если я публично описываю эту тему, то наверное - я на ней "собаку съел". У меня на "шаблонах" описаны и гораздо более сложные сущности. Но всему - своё время.

      А то, что generic'и - лаконичнее - я и не спорил. Да - они более - "синтаксический сахар". Но у всего есть свои плюсы и минусы.

      generic'и позволят написать например MyGeneric = class()?

      Ну и непоправленные ошибки - удручают.

      Удалить
    12. Кстати - было бы конечно приятнее видеть имя собеседника.

      Удалить
    13. Вы слишком серьёзно воспринимаете =)
      f x = x это не о сложности, только об выразительности. Суть в том что есть языки на которых оно так и выглядит, причём со статической типизацией.

      Удалить
    14. Поясняю: функция принимающая и возвращающая x.
      "id", если угодно.

      Удалить
    15. И опять я не понял, что вы имеете в виду :-( к сожалению.

      Можете попробовать ещё раз пояснить?

      Хм. Вы о "синтаксисе"?

      Ну я тоже так могу :-) На DSL - : F IN X X ; устроит?

      Наверное и : F ( X ) = X ; - можно сваять. Только вывод какой?

      Удалить
    16. Вдогонку...
      Мне лично синтаксис - не так важен - как возможности конструирования, композиции и декомпозиции. Пусть даже и на "китайском" языке. Мне важно работать ИМЕННО с конструированием сущностей.

      Удалить
    17. Я о том, что монструазность описания не позволит вам пользоваться многими простыми вещами, а так же значительно затруднит описание сложных. В качестве примера могу привести оператор(функцию) композиции (".", "|>", ">>>", итд). Суть её проста: она берёт две функции (a -> b) и (b -> c), и возвращает функцию (a -> c). Мелочь, а приятно. Такое довольно редко увидишь в классических императивных языках, а в функциональщине оно сплошь и рядом. По аналогии можно говорить и о монадах, функторах, стрелках. Часто вы встречали "комбинаторы парсеров" в классических языках? Просто их выразительность уже не тянет такие "сложные" концепции. Т.е даже дженерики это не особенно выразительно. =)

      Удалить
    18. И что? Мы ведь про Delphi говорим. А не про "языки оленьи".

      Я описываю - как сделать на практике сложные вещи простыми. А не как это в теории могло бы быть.

      Я вот так например могу - http://18delphi.blogspot.com/2013/07/blog-post_196.html
      : K183337891

      "Открываем НК"
      75 раз ( "Перейти на параграф вниз" )
      "Дождаться переключения вкладок"
      СР
      "Вернуться в текст документа"
      "Выделить {(1)} параграфов"
      "Сравнить выделенный текст текущего редактора с эталоном"
      ;

      Только и что?

      Сравнительным анализом языков в теме про конструирование и уж тем более про контейнеры - по-моему заниматься не стоит.

      У меня например есть сравнение Delphi и Objective-C - http://18delphi.blogspot.com/2013/03/objective-c-delphi.html

      Можем кстати отдельную тему поднять. Если интересно :-) Про DSL и прочие "возможности развития языков".

      Удалить
    19. Вдогонку.

      Синтаксис - мне практически "по-барабану". Я и на ассемблерах разных писал.

      Главное - конструкционный подход. ИМХО.

      А так я и вот такое писал - http://18delphi.blogspot.com/2013/04/dsl_18.html

      Или вот - http://18delphi.blogspot.com/2013/04/dsl.html

      Удалить
    20. Вдогонку.
      http://18delphi.blogspot.ru/2013/03/forth.html

      Удалить
    21. Просто раз речь зашла о выразительности, и я постарался описать почему именно считаю её важной. =)
      Насчёт "теории" - я применяю эти вещи и на практике, для себя. По тому и пишу об этом. Многое теряет современная "императивная классика".
      Про DSL было бы интересно почитать и порассуждать, но это уже вам решать.=)

      Удалить
    22. Вдогонку.

      Если кстати есть что написать - на тему тех же функциональных языков или "как превратить Delphi в функциональный язык". Или что-то ещё. Могу позвать в соавторы. Приходите - пишите.

      Я только - за.

      Удалить
    23. "Про DSL было бы интересно почитать и порассуждать, но это уже вам решать"

      Ну а что решать? Напишу. Со временем. Не всё сразу. Тем уж слишком много.

      Удалить
    24. "Просто раз речь зашла о выразительности, и я постарался описать почему именно считаю её важной. =)"

      Я знаю. И про Haskel знаю. И про остальных.

      Стрелку я кстати не считаю такой уж "выразительной".

      Функциональный подход - да силён. Не отрицаю. И наверное был бы рад видеть какие-то элементы функциональности в Delphi. Может быть.

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

      Удалить
    25. Выразительность сильно повышает повышение уровня абстракций, нежели синтаксис. То есть те же DSL и UML к примеру.

      А скажем RAD - не повышает. Он повышает скорость работы. Но не выразительность.

      Удалить
    26. Чисто функциональный подход считается разновидностью декларативного, только редко он "чистым" бывает. =)
      А дельфи я превратить в функциональный язык не пытался. Переносил для забавы некоторые концепции на шаблоны С++, но результат предсказуем. Всё это получается слишком огромным и уродливым, что бы пользоваться. Не думаю что на дельфи или жабе было бы лучше. ФП здесь подходит только для самого примитива, уровня калбэков. =(
      Впрочем если вы надумаете таки написать о DSL и ЯП вообще, там и порассуждаем. =)

      Удалить
    27. Не вижу ничего особенно уродливого в C++. Есть тот же boost. Есть C++ 11. Грядёт C++ 14.

      Калбэки (как вы выражаетесь) != замыканиям.

      Про DSL - я вам дал ссылки. Например про ParseMDATemplates. Лямбды - там вовсю.

      Монады я кстати еще только "не вкурил".

      А так - всегда рад :-)

      А вот про "чистоту" - это вы верно подметили :-)

      Удалить
    28. О! Про монады!

      Не хотите статью написать?

      Или может быть мне ссылок накидаете, чтобы я вдумчиво прочитал?

      Идею-то я понимаю. Но хочется технические тонкости.

      wikipedia - я конечно же читал.

      Удалить
    29. С++. Скажем так: те концепции, которые я переносил выглядили на нём довольно уродливо. Не предназначен он для них.
      Лямбды и замыкания это только верхушка, проникающая в языки как синтаксический сахар. Это скорее "программирование со вкусом функциональщины", чем действительно ФП. =)
      Про статью - я не против попробовать. =)
      А ссылок у меня самого нет. Кое где читал, но в основном понимание пришло с пониманием концепций языка. После этого достаточно взглянуть на описание класса монад, что бы понять что к чему.

      Удалить
    30. "Про статью - я не против попробовать. =)"
      -- и? и... где статья?

      можно ссылку?

      Удалить
  3. TInterfaceList => TIntegerList конечно же

    ОтветитьУдалить
  4. "По мне так вполне интересная идея" - ОЧЕРЕДНОЙ раз подчёркиваю - НЕ МОЯ.. я лишь довёл идею до кода.

    ОтветитьУдалить