OberonCore

Библиотека  Wiki  Форум  BlackBox  Компоненты  Проекты
Текущее время: Вторник, 20 Апрель, 2021 13:26

Часовой пояс: UTC + 3 часа




Начать новую тему Ответить на тему  [ Сообщений: 19 ] 
Автор Сообщение
СообщениеДобавлено: Понедельник, 22 Март, 2021 01:41 

Зарегистрирован: Воскресенье, 28 Май, 2006 22:12
Сообщения: 1578
Предыстория:
... в одном проекте (как всегда, я - о НЕтривиальныхЪ эмбеддедЪ системахЪ :) ) понадобилось написать свой "класс" двухсвязного списка.
Написали.
Достаточно долго Использовали.
Всё было хорошо.
...
Пока, однажды, не уволился автор, написавший этот "класс"...
...А на его место, "на поддержку и сопровождение", не пришёл молодой программист.
Но - смышлёный и способный (без сарказма говорю!).
...
Далее - пойдёт не в духе "работает? НЕ ТРОГАЙ!".
Короче.
Первоначальным толчком к "лазанью и копанию в коде", послужила инициатива по "оптимизации".
Кому-то запала идея, что можно "ещё чуток" ускорить работу кода, использующего упомянутые двухсвязные списки, если принять во внимание, что, в большинстве случаев, не надо "всякие там предусловия проверять". Типа - программисты же - не совсем дурачки и не будут в качестве "this" NULL в "метод" списка подсовывать... Или удалять элемент NULL из списка...
...Или удалять уже ранее удалённый элемент...
Ведь - правда же?
...
Короче, ПРЕДУСЛОВИЯ были в "методах" этого "класса" двухсвязного списка пересмотрены ОЧЕНЬ ТЩАТЕЛЬНО.
... И честно проверены не одной парой глаз...
... и "на пальцах" "мозговым штурмом" совместным обсуждением утверждена правильность логики работы каждого из "методов"...

А тесты стали выдавать "непрохождение".

Что вызвало некоторое замешательство у народа. Типа, "где там можно было напортачить"?

Естественно, что головы всех работников повернулись одновременно в сторону нового сотрудника и все дружно и хором спросили: "Ты шо-то менял в предусловиях и основной логике методов?"
Тот, естественно (и - ЧЕСТНО!) ответил: "НЕТ!"

Не поверили.

Стали в шесть пар глаз на проекционном экране смотреть и, чуть ли, не пальцем по строчкам водить и вслух хором прочитывать строки кода и, останавливаясь, переглядываясь и хором утверждаясь в верности понятого, убеждаться, что "всё правильно".
Далее, не смотря на то, что diff, В ЭТИХ ЧАСТЯХ МЕТОДОВ, никаких расхождений не нашёл (а в возвращаемых результатах методов И ТАК ВСЁ ОЧЕВИДНО(sic!!!)), прогнали сборку и запуск тестов.

Тесты не прошли.

Все хором почесали затылки и уставились на список соответствия ожидаемых и фактических результатов в тестах.

И тут меня, как "торкнуло". Я смотрю на код и спрашиваю: "А почему ты возвращаемые значения (логического типа) поменял в некоторых "методах" на противоположные?"
Человек отвечает: "Дык - неправильно же по смыслу было!"
"- Почему?! Объясняй."

И тут выяснилось полное соответствие одному из законов Мерфи. То есть, когда уже кажется, что всё разжёвано до конца и ничего не может быть понято НЕ ВЕРНО, обязательно найдётся хотя бы один и работников, кто поймёт всё совершенно не так. :)

Проиллюстрирую на примере операций добавления и удаления элемента в/из списка.

Все операции возвращали булевское значение, как успех/неуспех операции. То есть, если элемент успешно добавлялся/удалялся в/из списка - истина, если что-то пошло не так - ложь.
Что могло помешать успешности? Понятно, что проверялись аргументы (список и указатель на элемент - на непустоту). Кроме того, была ещё проверка на уже содержание данного элемента в данном списке. Это было "место изменения сознания" номер раз.
При удалении элемента - так же - проверялись аргументы и НАЛИЧИЕ этого элемента в данном списке. Это было место номер два.
В указанных двух местах человек поменял значение возвращаемого логического значения на противоположное.

То есть, автор первоначального варианта методов списка и молодой программист подразумевали РАЗНЫЙ СМЫСЛ УСПЕШНОСТИ ВЫПОЛНЕНИЯ ОПЕРАЦИЙ:

На добавлении элемента в список:
Автор: элемент, который пытаются добавить в список - уже содержится в нём. Значит, была какая-то ошибка во внешнем коде, потому, что такого случиться не должно было (там есть метод для проверки принадлежности элементам списка, его нужно было использовать) - ВОЗВРАТИТЬ "ЛОЖЬ"!
Молодой программист: элемент, который пытаются добавить в список - уже содержится в нём - ВОЗВРАТИТЬ "ИСТИНА"!

На удалении элемента из списка:
Автор: элемент, который пытаются удалить из списка, не содержится в нём. Значит, была какая-то ошибка во внешнем коде, потому, что такого случиться не должно было (там есть метод для проверки принадлежности элементам списка, его нужно было использовать) - ВОЗВРАТИТЬ "ЛОЖЬ"!
Молодой программист: элемент, который пытаются удалить из списка, не содержится в нём. Значит, работа уже сделана - ВОЗВРАТИТЬ "ИСТИНА"!

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

А - действительно, КУДА включать проверку того, содержит ли данный список данный объект?
Вызывать снаружи?
Или - организовывать в виде части предусловия внутри метода списка?
Или такой выбор - таки - будет частью проектного решения для конкретной системы?

Как вы думаете?

(Меня не интересует конкретность реализаций в разных системах и библиотеках, используемых вами в ваших программах! Меня интересует, как бы ВЫ решили эту "маленькую и незначительную проблемку"? ;) )


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Понедельник, 22 Март, 2021 08:30 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9402
Откуда: Россия, Орёл
Я обычно делал в таких случаях и операцию проверки наличия самостоятельную, и возврат "уже наличия" при попытке добавления. И иногда вариант-обёртку для последнего, где сразу и ASSERT добавлен, что элемента ранее не было.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Понедельник, 22 Март, 2021 09:47 

Зарегистрирован: Пятница, 24 Апрель, 2009 16:28
Сообщения: 559
Откуда: Москва
Двусвязный список - не лучшая структура для проверки дублей. Поэтому отдельно.
А в вашей реализации результат можно сделать более подробным. Типа, 0 -успех, далее со всеми...


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Понедельник, 22 Март, 2021 12:27 

Зарегистрирован: Воскресенье, 28 Май, 2006 22:12
Сообщения: 1578
Peter Almazov писал(а):
Двусвязный список - не лучшая структура для проверки дублей. Поэтому отдельно.
А в вашей реализации результат можно сделать более подробным. Типа, 0 -успех, далее со всеми...

Его основная задача не в выявлении дублей была.... :)
Да, и - постоянно держите в памяти, что работа идёт на системах с ОЧЕНЬ ограниченными ресурсами (в подавляющем большинстве случаев - БЕЗ ДИНАМИЧЕСКОГО ВЫДЕЛЕНИЯ ПАМЯТИ).


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Понедельник, 22 Март, 2021 13:56 
Аватара пользователя

Зарегистрирован: Суббота, 16 Февраль, 2008 02:47
Сообщения: 459
В ББ, как я понимаю, решается вот так:

PROCEDURE (s: List) Insert (e: Elem): BOOLEAN
Pre
e # NIL 20
~s.Includes(e) 21
Post
res => s.Includes(e)
~res => ~s.Includes(e)

Глядя на такое определение, молодой и смышленый сможет сделать вывод, что нельзя вставлять уже вставленный элемент.

Но это - вопрос не языка, а культуры.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Понедельник, 22 Март, 2021 14:30 

Зарегистрирован: Пятница, 13 Март, 2009 16:36
Сообщения: 971
Откуда: Казань
И так, и так можно. Бывают алгоритмы оптимистичные и пессимистичные. Надо выбрать один из вариантов и его придерживаться.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Понедельник, 22 Март, 2021 15:44 
Аватара пользователя

Зарегистрирован: Вторник, 19 Сентябрь, 2006 21:54
Сообщения: 2402
Откуда: Россия, Томск
Wlad писал(а):
А - действительно, КУДА включать проверку того, содержит ли данный список данный объект?
Вызывать снаружи?
Или - организовывать в виде части предусловия внутри метода списка?
Или такой выбор - таки - будет частью проектного решения для конкретной системы?

Как вы думаете?
Очень здорово вы рассказали про проблему проектирования!

Я бы ответил так, что в зависимости от применения этого "класса" я бы делал по-разному.
  • Если повторное удаление элемента указывает на ошибку удаляющего - например, освобождение уже освобождённой памяти, - однозначно сигнализировал бы ошибку.
  • Если добавление повторного элемента не является ошибкой - например, при добавлении уже существующего элемента в множество (set), - то возвращал бы успех.
  • Если же "класс" пишется универсальный, то есть два пути:
    • можно сделать свойство "класса" наподобие AllowDuplicates: Boolean,
    • либо можно сделать два набора методов Add/AddUnique, Del/DelExisting, etc., где Add добавляет без проверки наличия, а AddUnique делает проверку и вызывает Add.
    Во втором походе ответственность за вызов правильного метода ложится полностью на клиентский код, то есть размазывается, так что я бы предпочёл вариант со свойством AllowDuplicates, желательно доступным только в конструкторе.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Вторник, 23 Март, 2021 11:17 

Зарегистрирован: Суббота, 07 Март, 2009 15:39
Сообщения: 3252
Откуда: Астрахань
Спасибо за поучительную историю! Буду рассказывать студентам!
Очевидно, что неправильное понимание вызвано тотальным отсутствием комментов в коде... :))))))


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Вторник, 23 Март, 2021 12:05 

Зарегистрирован: Понедельник, 11 Сентябрь, 2017 13:23
Сообщения: 1202
Валерий меня опередил. Проблема в документировании, точнее, в его отсутствии. Тут уже ситуация, когда у проблемы есть фамилия и имя - либо это автор кода, либо молодой специалист (хотя его вряд ли стоит сразу бить по рукам), либо руководитель автора кода. А может быть и руководитель разработки, который не установил нормы по документированию кода и не отслеживал их (и тут я, как руководитель разработки, задрожал и упал в обморок).


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Вторник, 23 Март, 2021 18:55 

Зарегистрирован: Воскресенье, 28 Май, 2006 22:12
Сообщения: 1578
budden писал(а):
...

Валерий Лаптев писал(а):
...

Нисколько не умаляя необходимости документирования/комментирования исходников...
Но, вы знаете, тот случай заставил полностью пересмотреть мой подход к проектированию "базисных алгоритмов и структур данных" в подобного рода проектах.
Для меня (лично для меня :) ) наиболее приемлемым решением стала "параметризация через конструктор/инициализатор".
То есть, подобного рода поведение обобщено и настраивается через аргументы инициализирующего алгоритма.
Обычно туда включается два флага по разобранному случаю и флаг "владения" элементами, ЕСЛИ "элементы поддержки связности списка" являются частью структур данных, сохраняемых в списке.
Когда же связность (пресловутые поля prev/next) не входят в структуру данных, то есть ещё флаг, показывающий, берутся ли "объекты связности" из кучи или пула "объектов связности" (для эмбеддинга, часто, надо ОЧЕНЬ тонкие настройки с данными производить).


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Вторник, 23 Март, 2021 20:33 

Зарегистрирован: Понедельник, 11 Сентябрь, 2017 13:23
Сообщения: 1202
Но были тесты, и они вас спасли. По сути темы - я хотел сделать наименования типов контейнеров, как марочник сталей, даже чуть ли ни на этом форуме тему про это заводил. Потому что у контейнера есть масса аспектов, и может понадобиться любое их сочетание. И далее нужно как-то их генерировать с помощью метапрограммирования под конкретное применение. Тогда в зависимости от ситуации можно будет просто взять/сгенерировать готовый. Параметризовать же такие вещи динамически - несовместимо ни с простотой (которая в эмбедщине нужна по всем причинам), ни с надёжностью, ни с производительностью.

Вот на ЛОРе, оказывается, тема была: https://www.linux.org.ru/forum/development/16082380


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Вторник, 23 Март, 2021 23:42 

Зарегистрирован: Воскресенье, 28 Май, 2006 22:12
Сообщения: 1578
budden писал(а):
нужно как-то их генерировать с помощью метапрограммирования под конкретное применение. Тогда в зависимости от ситуации можно будет просто взять/сгенерировать готовый. Параметризовать же такие вещи динамически - несовместимо ни с простотой (которая в эмбедщине нужна по всем причинам), ни с надёжностью, ни с производительностью.

Смотря, что понимать под "простототой". :)
Уже не первый год вижу один и тот же сюжет: приходит в проект новый человече с очень бойким мозгом и уверенностью, что его знание шаблонов Си++ поможет ему, уж если, по крайней мере, не "решить все проблемы", то сделать так, что "все прозреют и оценят мощь языка и подхода!"...
А потом, при очередном "выпиливании" из очередного проекта только одного <string> (часто - в совокупности с <sstream>) уменьшает код (при той же функциональности) во флэшке микроконтроллера с 897кб (с -Os) до 121кб (с -O0)... И, в команде, на одного "сходившего порезвиться в Си++, опытного программиста" становится больше... :)


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 24 Март, 2021 01:20 

Зарегистрирован: Понедельник, 11 Сентябрь, 2017 13:23
Сообщения: 1202
Статическая параметризация тоже имеет свою цену. Но параметризовать в коде программы (динамически ли, статически ли) то, что можно зафиксировать на этапе проектирования - идея не супер. Насчёт генерации метапрограммами - это может выглядеть так, что генерируется только спецификация, а сам код пишется руками. Такое вот ручное метапрограммирование, оно же "путь Оберона".


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 24 Март, 2021 02:25 

Зарегистрирован: Воскресенье, 28 Май, 2006 22:12
Сообщения: 1578
budden писал(а):
Статическая параметризация тоже имеет свою цену.
Надёжность - вполне приемлемая цена.
budden писал(а):
Но параметризовать в коде программы (динамически ли, статически ли) то, что можно зафиксировать на этапе проектирования - идея не супер.
Ну - вот тебе и - "здрасьте-приехали"! КОД ПРОГРАММЫ и есть тот самый формализм, который, вроде как, должен позволять фиксировать ПРОЕКТНЫЕ РЕШЕНИЯ. А за счёт формальностей, мы сможем эти решения проверить на этапе компиляции. И/или управлять ими.
budden писал(а):
Насчёт генерации метапрограммами - это может выглядеть так, что генерируется только спецификация, а сам код пишется руками. Такое вот ручное метапрограммирование, оно же "путь Оберона".
А что такое "генерация метапрограммами"? Что имеется в виду? "с помощью" или "в виде"?


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 24 Март, 2021 10:27 

Зарегистрирован: Понедельник, 11 Сентябрь, 2017 13:23
Сообщения: 1202
Ну а к чему был пример про <string>? Ведь шаблоны в С++ - это тоже вид статического метапрограммирования. Я думал, Вы так выступили против него.
Цитата:
КОД ПРОГРАММЫ и есть тот самый формализм, который, вроде как, должен позволять фиксировать ПРОЕКТНЫЕ РЕШЕНИЯ. А за счёт формальностей, мы сможем эти решения проверить на этапе компиляции.

Ну это только в идеальном мире. В реальном код лишь воплощает проектные решения, а зафиксированы они где-то в другом месте. Если мы оптимизируем под объём, то раздувать нельзя. Современные ЯП (включая даже мой любимый лисп) недостаточно совершенны, чтобы можно было писать на высоком уровне, в проектных спецификациях, а потом компилятор сам генерировал на основании этого оптимизированный и надёжный код, соответствующий им. Поэтому на этапе воплощения неизбежна ручная работа. Конечно, если gcс уже настолько умён, что сумеет ваши динамические инициализаторы вычислить во время компиляции, то вам может повезти, но Вы тогда становитесь заложником алгоритмов gcc, а они не слишком уж прозрачны. Впрочем, я не сишник, могу чего-то не знать, но в целом обычно оптимизирующий компилятор оптимизирован и по времени компиляции. Он действует на основе эвристик. И, чем сложнее случай, тем менее тщательно он будет оптимизировать (чтобы избежать комбинаторного взрыва времени компиляции). Поэтому нет гарантии, что после изменения одной строчки какой-то конкретный случай применения оптимизации не отвалится.

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


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 24 Март, 2021 14:25 

Зарегистрирован: Воскресенье, 28 Май, 2006 22:12
Сообщения: 1578
budden писал(а):
...

Да речь вообще - не об этом была.
Когда я создаю/проектирую архитектуру, меня совершенно не интересуют реализации или особенности библиотек (контейнеров).
Они и НЕ ДОЛЖНЫ никак трогать.
Потому, что если только на этапе описания архитектуры, вдруг зазвучали слова "мэп", "лист", эррэй" - всё, сушите веслы, архитектор НЕ компетентен.
А вот описание свойств или отношений между сущностями предметной области ДОЛЖНЫ быть представлены синтаксисом и семантикой языка.
Конкретный вид структур, поддерживающих такие отношения - "всплывёт" значительно позднее. А пока - всё, что с операциями над структурой или отношениями связано, ДОЛЖНО описываться в терминах самой же модели.
Поэтому мне - СОВЕРШЕННО ПО БАРАБАНУ, НА ЧЁМ ПРОЕКТИРОВАТЬ. Главное, что бы означенные выше предметно-модельные артефакты были выразимы через язык.
Поэтому для меня совершенно непонятно, зачем вы заговорили о деревьях ЛИСПа и ключах и "эвристиках" gcc...


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 24 Март, 2021 14:52 

Зарегистрирован: Воскресенье, 28 Май, 2006 22:12
Сообщения: 1578
budden писал(а):
Ну а к чему был пример про <string>? Ведь шаблоны в С++ - это тоже вид статического метапрограммирования. Я думал, Вы так выступили против него.

Меня оно - НЕ интересует до определённого момента.
Набор моих архитектурных решений НЕ: опирается на наличие или отсутствия шаблонизации, наследования или - вообще - поддержки ООП в конкретной языке.
Меня язык НЕ интересует вообще.
Поэтому я и на асме могу в ООП-стиле писать (и - писАл! например - ДАЖЕ - на асме ADSP2192 :))) ).
ЕСТЕСТВЕННО, что если язык не поддерживает чего-то, то нужно приложить некоторые усилия, что бы это или добавить, или добавить ЭМУЛЯЦИЮ этого чего-то.
В некоторых случаях добавленное даже лучше работает, чем если бы использовалось "родное" средство другого языка. Например, для меня сейчас идеалом проектировать в ОО, но реализовывать в простом Си (без классов). Просто потому, что я сам реализую ту модель объектов и исполняющую её среду времени исполнения, и - не ограничен уже заданной в том же Си++.
А МЕТА* часто мне и - не нужно. Обычно они и не нужны при проектировании. Потому, что очень часто оказывается, что реализация конкретных множеств (не в смысле контейнеров! :) ) не требует чего-то уж очень навороченного.
Да оно и - не должно требовать!
Если я вижу в проекте "выпирание из всех щелей" шаблонных контейнеров, этот кода, для меня начинает не то, что "плохо пАхнуть", а от него - прёт гниением. Для меня архитектура, в таком случае, представляется "вывернута кишками наружу". НЕ ДОЛЖЕН архитектор "поднимать" в знания предметной области особенности имплементации контейнеров и структур.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 24 Март, 2021 15:13 

Зарегистрирован: Понедельник, 11 Сентябрь, 2017 13:23
Сообщения: 1202
Извините, нет времени. Я отметил то, на что мой опыт среагировал. Если не было полезным - извините.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 24 Март, 2021 17:17 

Зарегистрирован: Воскресенье, 28 Май, 2006 22:12
Сообщения: 1578
budden писал(а):
Извините, нет времени. Я отметил то, на что мой опыт среагировал. Если не было полезным - извините.

Всё полезно.
Из мира ЛИСПА я взял "скобочную" форму записи для конфигурационных файлов своих систем.
В то же время, из Смолтока - запись его выражений в сообщениях объектам, для описания формата сообщений в моих системах. :)
Описатели взаимодействия с точки зрения объектов-"серверов" - на чём-то подобном РБНФ из Зоннона.
Короче - всяк что-то полезное ВСЕГДА дать может. :)


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 19 ] 

Часовой пояс: UTC + 3 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 2


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Вся информация, размещаемая участниками на конференции (тексты сообщений, вложения и пр.) © 2005-2021, участники конференции «OberonCore», если специально не оговорено иное.
Администрация не несет ответственности за мнения, стиль и достоверность высказываний участников, равно как и за безопасность материалов, предоставляемых участниками во вложениях.
Без разрешения участников и ссылки на конференцию «OberonCore» любое воспроизведение и/или копирование высказываний полностью и/или по частям запрещено.
Powered by phpBB® Forum Software © phpBB Group
Русская поддержка phpBB