Речь вот об этом - 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. Он правда пока ничего не делает - лишь создаёт соответствующий контейнер. Чтобы хотя бы проверить, что он компилируется. Тест - потом конечно же дополню.
Вот вкратце всё. Если что-то непонятно - задавайте вопросы. С радостью отвечу. Ну или продолжу пояснения.
Говорят - "что-то ты мало объясняешь". Не понимаю правда чтобы такого объяснить. Но попробую.
Тем более, что движок блога так устроен, что сложно перемешивать "картинки", текст и код. Пусть "там" будет код и "картинки", а тут будет "объяснение".
Итак. Что собственно хотелось?
Хотелось нескольких вещей:
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. Он правда пока ничего не делает - лишь создаёт соответствующий контейнер. Чтобы хотя бы проверить, что он компилируется. Тест - потом конечно же дополню.
Вот вкратце всё. Если что-то непонятно - задавайте вопросы. С радостью отвечу. Ну или продолжу пояснения.
Почему бы просто не использовать Generic'и? Как выглядит использование получившегося контейнера?
ОтветитьУдалитьПочему бы просто не использовать Generic'и?
ОтветитьУдалить1. Delphi 7 или FPC например.
2. Судя по QC - не всё хорошо с Generic'ами.
3. Примеси - гибче.
Как выглядит использование получившегося контейнера?
Там же тесты есть.
Ну или:
l_List := TIntegerList.Create;
l_LIst.Add(1000);
etc.
Я не совсем это имел ввиду. Хотел посмотреть на код вместе с инстанцированием. Но сейчас уже разобрался сам. Громозко, конечно. Но полезно, если других вариантов нет. По мне так вполне интересная идея, а вы говорите "программирование не творческий процесс". =)
УдалитьПо-моему - ничего громоздкого. И уж тем более - ничего творческого. Чисто "шаблоны".
УдалитьНадо просто знать что взять и с чем смешать. Чисто по "рецепту".
type
_ItemType_ = Integer;
{$include StandardAtomicList.imp.pas}
TInterfaceList = class(_StandardAtomicList_);
end;
...
implementation
...
{$include StandardAtomicList.imp.pas}
- всё.. По-моему - проще некуда.
А по моему громоздко. Это смотря с чем сравнивать. =)
УдалитьПо сравнению с C++ или Generic'ами - да - наверное.. Хотя - далеко не всё именно количеством строк кода меряется...
УдалитьВыразительность очень важна для языка программирования. Повышает читаемость и уменьшает вероятность ошибок.
УдалитьВо-первых - это очень спорный вопрос.
УдалитьВо-вторых - что считать выразительностью? Циклы в "стиле C"?
В-третьих - выразительность != количеству строк кода.
В-четвёртых - там не зря диаграммы нарисованы. По мне - они более, чем выразительны.
УдалитьНасчет FPC - http://wiki.freepascal.org/Generics
УдалитьНасчет FPC - http://wiki.freepascal.org/Generics
УдалитьВыразительность по отношению к языку - возможность кратко(речь о количестве описываемых действий(операций, терминов), а не размерах литералов) описать на нём программу. Попробуйте описать f x = x на своих шаблонах. Или можете предложить что-то более сложное для сравнения выразительности.
Удалить"Выразительность по отношению к языку - возможность кратко(речь о количестве описываемых действий(операций, терминов), а не размерах литералов) описать на нём программу."
УдалитьВ таких критериях - UML'ю - нет равных :-)
"Попробуйте описать f x = x на своих шаблонах."
Что ИМЕННО предлагается описать - я не понял.
Но по существу. "Не учите дедушку кашлять" :-) Простите уж. Если я публично описываю эту тему, то наверное - я на ней "собаку съел". У меня на "шаблонах" описаны и гораздо более сложные сущности. Но всему - своё время.
А то, что generic'и - лаконичнее - я и не спорил. Да - они более - "синтаксический сахар". Но у всего есть свои плюсы и минусы.
generic'и позволят написать например MyGeneric = class()?
Ну и непоправленные ошибки - удручают.
Кстати - было бы конечно приятнее видеть имя собеседника.
УдалитьВы слишком серьёзно воспринимаете =)
Удалитьf x = x это не о сложности, только об выразительности. Суть в том что есть языки на которых оно так и выглядит, причём со статической типизацией.
Поясняю: функция принимающая и возвращающая x.
Удалить"id", если угодно.
И опять я не понял, что вы имеете в виду :-( к сожалению.
УдалитьМожете попробовать ещё раз пояснить?
Хм. Вы о "синтаксисе"?
Ну я тоже так могу :-) На DSL - : F IN X X ; устроит?
Наверное и : F ( X ) = X ; - можно сваять. Только вывод какой?
Вдогонку...
УдалитьМне лично синтаксис - не так важен - как возможности конструирования, композиции и декомпозиции. Пусть даже и на "китайском" языке. Мне важно работать ИМЕННО с конструированием сущностей.
Я о том, что монструазность описания не позволит вам пользоваться многими простыми вещами, а так же значительно затруднит описание сложных. В качестве примера могу привести оператор(функцию) композиции (".", "|>", ">>>", итд). Суть её проста: она берёт две функции (a -> b) и (b -> c), и возвращает функцию (a -> c). Мелочь, а приятно. Такое довольно редко увидишь в классических императивных языках, а в функциональщине оно сплошь и рядом. По аналогии можно говорить и о монадах, функторах, стрелках. Часто вы встречали "комбинаторы парсеров" в классических языках? Просто их выразительность уже не тянет такие "сложные" концепции. Т.е даже дженерики это не особенно выразительно. =)
УдалитьИ что? Мы ведь про 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 и прочие "возможности развития языков".
Вдогонку.
УдалитьСинтаксис - мне практически "по-барабану". Я и на ассемблерах разных писал.
Главное - конструкционный подход. ИМХО.
А так я и вот такое писал - http://18delphi.blogspot.com/2013/04/dsl_18.html
Или вот - http://18delphi.blogspot.com/2013/04/dsl.html
Вдогонку.
Удалитьhttp://18delphi.blogspot.ru/2013/03/forth.html
Просто раз речь зашла о выразительности, и я постарался описать почему именно считаю её важной. =)
УдалитьНасчёт "теории" - я применяю эти вещи и на практике, для себя. По тому и пишу об этом. Многое теряет современная "императивная классика".
Про DSL было бы интересно почитать и порассуждать, но это уже вам решать.=)
Вдогонку.
УдалитьЕсли кстати есть что написать - на тему тех же функциональных языков или "как превратить Delphi в функциональный язык". Или что-то ещё. Могу позвать в соавторы. Приходите - пишите.
Я только - за.
"Про DSL было бы интересно почитать и порассуждать, но это уже вам решать"
УдалитьНу а что решать? Напишу. Со временем. Не всё сразу. Тем уж слишком много.
"Просто раз речь зашла о выразительности, и я постарался описать почему именно считаю её важной. =)"
УдалитьЯ знаю. И про Haskel знаю. И про остальных.
Стрелку я кстати не считаю такой уж "выразительной".
Функциональный подход - да силён. Не отрицаю. И наверное был бы рад видеть какие-то элементы функциональности в Delphi. Может быть.
Там где надо функционально - я пишу функционально.
Там где надо императивно - пишу императивно.
А больше всего - вообще люблю декларативный подход. Т.е. когда пишешь "что надо", а не "как это надо".
Выразительность сильно повышает повышение уровня абстракций, нежели синтаксис. То есть те же DSL и UML к примеру.
УдалитьА скажем RAD - не повышает. Он повышает скорость работы. Но не выразительность.
Чисто функциональный подход считается разновидностью декларативного, только редко он "чистым" бывает. =)
УдалитьА дельфи я превратить в функциональный язык не пытался. Переносил для забавы некоторые концепции на шаблоны С++, но результат предсказуем. Всё это получается слишком огромным и уродливым, что бы пользоваться. Не думаю что на дельфи или жабе было бы лучше. ФП здесь подходит только для самого примитива, уровня калбэков. =(
Впрочем если вы надумаете таки написать о DSL и ЯП вообще, там и порассуждаем. =)
Не вижу ничего особенно уродливого в C++. Есть тот же boost. Есть C++ 11. Грядёт C++ 14.
УдалитьКалбэки (как вы выражаетесь) != замыканиям.
Про DSL - я вам дал ссылки. Например про ParseMDATemplates. Лямбды - там вовсю.
Монады я кстати еще только "не вкурил".
А так - всегда рад :-)
А вот про "чистоту" - это вы верно подметили :-)
О! Про монады!
УдалитьНе хотите статью написать?
Или может быть мне ссылок накидаете, чтобы я вдумчиво прочитал?
Идею-то я понимаю. Но хочется технические тонкости.
wikipedia - я конечно же читал.
С++. Скажем так: те концепции, которые я переносил выглядили на нём довольно уродливо. Не предназначен он для них.
УдалитьЛямбды и замыкания это только верхушка, проникающая в языки как синтаксический сахар. Это скорее "программирование со вкусом функциональщины", чем действительно ФП. =)
Про статью - я не против попробовать. =)
А ссылок у меня самого нет. Кое где читал, но в основном понимание пришло с пониманием концепций языка. После этого достаточно взглянуть на описание класса монад, что бы понять что к чему.
"Про статью - я не против попробовать. =)"
Удалить-- и? и... где статья?
можно ссылку?
TInterfaceList => TIntegerList конечно же
ОтветитьУдалить"По мне так вполне интересная идея" - ОЧЕРЕДНОЙ раз подчёркиваю - НЕ МОЯ.. я лишь довёл идею до кода.
ОтветитьУдалить