16+
ComputerPrice
НА ГЛАВНУЮ СТАТЬИ НОВОСТИ О НАС




Яндекс цитирования


Версия для печати

Модуль поиска не установлен.

Программирование за...1 (одну) минуту...

27.12.2010

Константин Вавилов (WaW)


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

Заранее выражаю признательность про-фессору Шалыто А.А. за теорию и Туккелю Н.И. за практические примеры в области автоматного ЛОГИЧЕСКОГО программирова-ния.

Проблема

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

Большинство программистов, мягко говоря, не любит алгоритмы, скорее всего потому, что все равно придется выдумывать что-то свое по ходу кодирования. И правда, зачем терять время на рисование каких-то прямоугольничков, ромбиков и стрелочек, легче сначала попрограммировать, а потом изобразить примерно похожий или очень общий алгоритм для документации. Все привыкли к этому - программисты, потому что так легче, а постановщики задач, потому что не всегда владеют знаниями по программированию в нужном объеме и никак не могут повлиять на то, что вытворяет программист. Удобные среды программирования (даже в DOSе, не говоря уж о визуальных под Windows) тоже способствуют уверенности именно в такой последовательности проектирования. Развитые средства отладки и наблюдения за значениями переменных позволяют надеяться, что можно выявить любую ошибку в логике. Таково сегодняшнее положение. Есть ли выход или хотя бы улучшение?

Программа

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

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

ВСЯ логика программы строится на основе селектора (switch в языке Си, case - в Паскале) и ВСПОМОГАТЕЛЬНО используется старый добрый оператор условия if-else.

Упрощенно ЛЮБАЯ логическая функция (автомат) представлена в примере 1 (пока не задумывайтесь над смыслом комментариев, оценивайте структуру).

// первая часть автомата -
// проверка условий перехода
switch (Y) // номер состояния автомата
{
  case 0:
  // Проверка условий на дугах и
  // петлях (условия проверяются
  // по приоритетам),
  // выполнение перехода
  // (изменить значение Y)
  // и действия на дуге или петле
  // (выполнить выходную функцию);
  // протоколирование переходов и действий при выполнении условия.
  break ;
  ...
  case n:
  // Проверка условий на дугах и
  // петлях (условия проверяются
  // по приоритетам),
  // выполнение перехода
  // (изменить значение Y)
  // и действия на дуге или петле
  // (выполнить выходную функцию);
  // протоколирование переходов и действий при выполнении условия.
  break ;
};

// вторая часть автомата -
// вызов вложенных автоматов
switch( Y )
{
case 0:
  // Вызов вложенных автоматов
  // (по умолчанию с текущего состояния
  // или с заданного состояния).
  break ;
  ...
  case n:
  // Вызов вложенных автоматов
  // (по умолчанию с текущего состояния
  // или с заданного состояния).
  break ;
};

Пример 1. Текст логической функции (автомата) на языке Си (общий принцип)

Конкретная реализация (программная имитация работы привода затвора) пред-ставлена в примере 2. Для непосвященных, затвор - это такое устройство, по действию напоминающее обычный водопроводный вентиль, только намного больше и с элек-троприводом, а также с возможностью дистанционного управления.

// Входные переменные
//
// X0 - есть режим ДУ
// Z1 - сигнал от АСУ
// "Включить пускатель на открытие"
// Z2 - сигнал от АСУ
// "Включить пускатель на закрытие"
// Z3 - сигнал от АСУ
// "Отключить пускатели"
// T1 - время включения/отключения
// пускателя истекло
// T2 - время открытия/закрытия
// затвора истекло
// N - номер имитируемой ситуации

// Выходные функции
//
// zX1 - запись признака
// "Затвор открыт"
// zX2 - запись признака
// "Затвор закрыт"
// zX3 - запись признака
// "Включен пускатель на открытие"
// zX4 - запись признака
// "Включен пускатель на закрытие"
// zNX1 - стирание признака
// "Затвор открыт"
// zNX2 - стирание признака
// "Затвор закрыт"
// zNX3 - стирание признака
// "Включен пускатель на открытие"
// zNX4 - стирание признака
// "Включен пускатель на закрытие"
// zt1 - фиксация времени начала
// включения/отключения пускателя
// zNt1 - сброс зафиксированного
// времени включения/
// отключения пускателя
// zt2 - фиксация времени начала
// открытия/закрытия затвора
// zNt2 - сброс зафиксированного
// времени открытия/закрытия затвора

// Автомат имитации работы
// привода затвора (AIPS).
// Проверка условий перехода

// объявление и определение признаков
// выполнения выходных функций
int8 zNX4 = 0, zNt1 = 0, zNX2 = 0,
  zX3 = 0, zt2 = 0, zt1 = 0,
  zX1 = 0, zNt2 = 0, zNX3 = 0,
  zX2 = 0, zNX1 = 0, zX4 = 0;

switch (AIPS) {
  case 201: if ((N!=6))
  {AIPS=1; zNX4=1;}
  break;
  case 106: if (T1)
  {AIPS=1; zNX4=1; zNt1=1;}
  else
  if ((N==6))
  {AIPS=201; zNt1=1;}
  break;
  case 101: if (T1)
  {AIPS=103; zNX2=1; zX3=1;
  zNt1=1; zt2=1;}
  else
  if ((!Z1 || !X0) && X2)
  {AIPS=1; zNt1=1;}
  else
  if ((!Z1 || !X0) && !X2)
  {AIPS=0; zNt1=1;}
  break;
  case 1: if ((N==9))
  {AIPS=103; zNX2=1;
  zX3=1; zt2 = 1;}
  else
  if (Z1 && X0)
  {AIPS=101; zt1=1;}
  break;
  case 103: if (T2 && (N!=7))
  {AIPS=105; zX1=1;
  zt1=1; zNt2=1;}
  else
  if (T2 && (N==7))
  {AIPS=0; zNX3=1; zNt2=1;}
  else
  if (Z3)
  {AIPS=107; zt1=1;}
  break;
  case 104: if (T2 && (N!=8))
  {AIPS=106; zX2=1;
  zt1=1; zNt2=1;}
  else
  if (T2 && (N==8))
  {AIPS=0; zNX4=1; zNt2=1;}
  else
  if (Z3)
  {AIPS=108; zt1=1;}
  break;
  case 102: if (T1)
  {AIPS=104; zNX1=1; zX4=1;
  zNt1=1; zt2=1;}
  else
  if ((!Z2 || !X0) && X1)
  {AIPS=2; zNt1=1;}
  else
  if ((!Z2 || !X0) && !X1)
  {AIPS=0; zNt1=1;}
  break;
  case 0: if ((N==9))
  {AIPS=103; zX3=1; zt2=1;}
  else
  if ((N==10))
  {AIPS=104; zX4=1; zt2=1;}
  else
  if (Z1)
  {AIPS=101; zt1=1;}
  else
  if (Z2)
  {AIPS=102; zt1=1;}
  break;
  case 105: if (T1)
  {AIPS=2; zNX3=1; zNt1=1;}
  else
  if ((N==5))
  {AIPS=202; zNt1=1;}
  break;
  case 202: if ((N != 5))
  {AIPS=2; zNX3=1;}
  break;
  case 2: if ((N == 10))
  {AIPS=104; zNX1=1;
  zX4=1; zt2=1;}
  else
  if (Z2 && X0)
  {AIPS=102; zt1=1;}
  break;
  case 108: if (!Z3)
  {AIPS=104; zNt1=1;}
  else
  if (T1)
  {AIPS=0; zNX4=1;
  zNt1=1; zNt2 = 1;}
  break;
  case 107: if (!Z3)
  {AIPS=103; zNt1=1;}
  else
  if (T1)
  {AIPS=0; zNX3=1;
  zNt1=1; zNt2=1;}
  break;
  default: break;
};

// Автомат имитации работы
// привода затвора (AIPS).
// Вызов подАвтоматов в Состоянии

switch (AIPS) {
  default: break;
};

Пример 2. Реализация логической функции имитации работы привода затвора на языке Си для пакета LabVIEW 6i (для классического Си можно сделать вызов выходных функций непосредственно и сразу без использования признаков выполнения).

Как видите, пример конкретной реализации достаточно сложен логически.

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

Внимательно прочитайте следующее предложение. ВЕСЬ текст программы (включая обеспечение "читабельности"), приведенный в примере 2, получен АВТОМАТИЧЕСКИ из схемы алгоритма, нарисованной в редакторе MS Word' 97.

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

Алгоритм

Как видно из вышесказанного, главная роль при проектировании и создании логической части программы отводится алгоритму. Слова ЛОГИЧЕСКАЯ ЧАСТЬ являются ключевыми, это надо сразу понять. Смысл последнего утверждения в том, что описы-вать тривиальные нелогические операции (типа записи/стирания признака) нет необ-ходимости, а для более сложных выходных функций (например, с использованием вы-зова специфичных для системы и языка процедур) наверняка достаточно будет только текстового описания.

А теперь, наконец, о главном: Об алгоритме. Нетрудно предположить, что он тоже будет однороден и, надеемся, прост для разработки.

Основой всего служит нечто - СОСТОЯНИЕ. Необходимо добавить еще только одно слово - ОЖИДАНИЕ. Получаем, на мой взгляд, вполне достаточное определение "СОСТОЯНИЕ ОЖИДАНИЯ". Ожидают в состоянии появления признаков, значений или событий. Ожидание может быть ограниченным по времени и неограниченным (в принципе бесконечным). Или по-другому (классификация Шалыто А.А.) - состояния бывают не-устойчивыми и устойчивыми.

Первой особенностью состояния является то, что в нем ожидают ОПРЕДЕЛЕННЫЙ ОГРАНИЧЕННЫЙ набор признаков, значений или событий. Важность этого обстоятельст-ва станет понятна позже.

Любой алгоритм (и программа, естественно) имеет входную и выходную информацию. Выходные функции, формирующие выходную информацию, можно разделить на два вида (далее чисто авторские определения): изменяющие (операции над значениями переменных и свойствами объектов) и генерирующие (например, открытие окна, запуск приложения, выдача сообщения в окне диалога, т.е. операции, не связанные с изменением). Изменяющие выходные функции определяют вторую особенность состояния - выдача набора ТОЧНО ОПРЕДЕЛЕННЫХ значений выходной информации (переменных и свойств объектов). Это означает очень простое и одновременно необычайно важное обстоятельство - В ЛЮБОЙ МОМЕНТ ВРЕМЕНИ ИЗВЕСТНЫ ЗНАЧЕНИЯ ВСЕХ ВЫХОДНЫХ ПЕРЕМЕННЫХ, так как в любой момент времени алгоритм (программа) находится в каком-то состоянии, а число состояний ограниченно и число наборов значений выходных переменных, соответственно, тоже. Функция протоколирования переходов является органически встроенной в функцию автомата и, следовательно, ВСЕГДА можно определить логическую последовательность выдачи выходной информации.

Программа - 2

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

Первая особенность программы, использующей автоматы, - обязательное наличие цикла while : do (или do ... while). Тут вроде бы ничего нового не сказано, но самое главное, что этот цикл будет для логической части всей программы первым и единственным! Еще может встречаться не логический, а скорее вычислительный цикл for. Автоматов (напомню - это функции, подпрограммы логики) может быть сколько угодно, но нигде больше в автоматах не будет встречаться ни цикла while, ни тем более оператора goto.

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

Главными и единственными логическими переменными являются номера состояний автоматов (можно придумать даже еще более крутое выражение "Автомат является ,по существу, специфической переменной"). В последнее утверждение трудно поверить, ведь все мы привыкли без всяких особых размышлений применять множество глобаль-ных и локальных переменных. А куда же без них?!!! Чаще всего это признаки, "сигнализирующие" программе о выполнении какого-либо условия. Признак записывается (принимает значение TRUE) в нужный по мнению программиста момент, а потом (чаще всего только после того, как этот признак вызывает нежелательные последствия своим вечным TRUE) начинается мучительный поиск момента и места в программе для сброса признака в значение "FALSE". Знакомо, не правда ли? Так вот теперь сообразите, глядя на текст примера 1, - ведь здесь не применяется никаких дополнительных переменных, а просто изменяется значение номера состояния, и изменяется оно при выполнении логического условия, чем не аналог признака?!

А как же переменные - аргументы условий? Отвечаю совершенно спокойно - в качестве аргументов в условии выступают переменные, которые являются глобальными ВХОДНЫМИ переменными для ВСЕЙ программы, их не надо выдумывать, они есть всегда и сразу и изменяют их значения не внутренние процессы, а внешние, никак не зави-сящие от логики программы. Единственным исключением являются признаки окончания временных (от слова "время") интервалов, формируемые элементарной логической операцией, кстати, тесно связанной с номером состояния, место и время установки и сброса однозначно известны. Аргументами условий, естественно, могут быть и номера состояний (текущие и предыдущие).

Самое интересное, что последние два абзаца можно смело применить и в разговоре об алгоритме (замените слово "программа" на "алгоритм" и прочитайте еще раз).

Алгоритм - 2

Как же определить состояния? На что опираться?

Автор статьи участвует в разработке АСУ ТП (автоматизированных систем управления технологическим процессом). Поэтому и пример 2 об имитации технологического объекта - затвора. Здесь все просто - "поведение" этого механического чуда техники известно и определить основные устойчивые и неустойчивые состояния несложно. Далее добавляются всевозможные "нехорошие" условия, переходы и, если необходимо, "нехорошие" состояния. Полный алгоритм приведен в примере 3 (перечень входных переменных и выходных функций - в примере 2).

Рассмотрим теперь задачу более близкую для пользователей и программистов - программирование интерфейса с оператором. Пускай необходимо обеспечить выбор ка-кого-либо пункта (темы) из ComboBox и возможность сброса выбранной темы по нажа-тию кнопки "Отмена выбора" (с установкой ComboBox в положение "Ничего не выбра-но").

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

Входные переменные - номер выбранного пункта объекта ComboBox (первый по счету - "Ничего не выбрано") и признак "Кнопка "Отмена выбора" нажата" (как этот признак формируется - не будем принимать во внимание).

Выходные функции - "Установить первый по счету пункт - "Ничего не выбрано", "Заблокировать возможность выбора первого пункта", "Разблокировать возможность выбора первого пункта", "Сделать кнопку "Отмена выбора" недоступной", "Сделать кнопку "Отмена выбора" доступной".

Выходной переменной, значение которой точно определяет состояние, является свойство кнопки "Отмена выбора" Disable.

Первое состояние - "Пункт (тема) не выбрана". Свойство кнопки "Отмена выбора" Disable = TRUE. Ожидается выбор не первого пункта. При этом изменяется "значение" свойства кнопки "Отмена выбора" Disable на FALSE, выполняется функция "Заблокировать возможность выбора первого пункта", и происходит переход во второе состояние автомата.

Второе состояние - "Пункт (тема) выбрана". Свойство кнопки "Отмена выбора" Disable = FALSE. Ожидается нажатие кнопки "Отмена выбора". При этом изменяется "значение" свойства кнопки "Отмена выбора" Disable на TRUE, выполняются функции "Разблокировать возможность выбора первого пункта" и "Установить первый по счету пункт - "Ничего не выбрано" и происходит переход в первое состояние.

Принципы

Минуточку, но ведь это не Windows-решение, где же обработчики событий, все эти процедуры делаются там и без всяких автоматов, зачем применять их в таком простом примере?!!

Возвратитесь к началу статьи, к примеру 2, где достаточно непростая задача. В статье предложен только принцип (успешно реализованный, кстати, в пакете графического программирования Windows-приложений LabVIEW). А принцип прост - ОТДЕЛЕНИЕ ЛОГИКИ ОТ ПРОГРАММИСТА-ИСПОЛНИТЕЛЯ (специалиста, понимающего особенности системы и языка и ответственного, в первую очередь, за программную корректность), АВТОМАТИЧЕСКОЕ ПОЛУЧЕНИЕ ТЕКСТА ЛОГИЧЕСКОЙ ЧАСТИ ПРОГРАММЫ ИЗ СХЕМЫ АЛГОРИТМА (программирование за одну минуту) и, идеально, сосредоточение логики в одном месте программы. Я уверен, что эти "плюсы" автоматного программирования неоспоримы. Уважаемые программисты, вы же такие умные, крутые и все такое, ну неужели вам не придумать, как применить в Windows такие замечательные решения? Теоретиками (Шалыто А.А. и Туккель Н.И.) предложен один из путей ("Мир ПК" © 8, 9 за 2001 год), как мне кажется, не единственный.

А теперь KNOW-HOW. Читатель уже, наверное, изнемогает узнать, а как же из схемы примера 3 получается текст, да еще на Си, да еще и структурированный? Может быть, это блеф?

Все дело в:

1) однородности схемы алгоритма;

2) особом, но простом написании текста автофигур;

3) особой, но простой группировке автофигур и линий;

4) однородности текста программы, реализующей автомат.

Подумайте немного, и все получится. У меня, считающего себя даже не средним программистом, а, скорее, логиком, ушло на создание приложения по автополучению текста программы из документа MS Word 2 дня. Правда, программировал я в LabVIEW, но можно и нужно, конечно, использовать VBA, VC, Delphi и т.п. Текст программы в принципе генерируется на любом языке: Си, Паскаль, Ассемблер и т.д. То есть везде, где есть аналог оператора SWITCH (возможно, естественно, обойтись применением самого простого оператора условия).

Предвижу шквал обвинений в некомпетентности, в слишком поверхностном под-ходе к программированию логики в Windows-приложениях. Не буду отрицать, работаю пока с пакетом LabVIEW, а там создатели умудрились скрыть многие особенности Windows (допустим, обработчики событий), но, имея лишь опыт программирования под DOS, вполне успешно постиг азы ООП, многопоточности и т.д. и т.п. Не старайтесь выискивать мои ошибки и незнания. Принципы SWITCH-технологии логического программирования все равно будут действовать. Логикой будут заниматься те, кто четко понимает, ЧТО должно быть, а программисты должны понимать только, КАК реализовать это в программной среде. Постановщик разрабатывает алгоритм, рисует его, получает текст логической части программы, а программист корректно, профессионально интегрирует этот текст в программу и делает все вспомогательные функции (не тратя время на логику).

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

Тестирование

Что же дальше? Программа написана, надо тестировать, убедиться в том, что то, что задумывалось, успешно разрешено и реализовано. Вспомним одно из свойств состояния автомата, а именно первое - в состоянии ожидают ОПРЕДЕЛЕННЫЙ ОГРАНИЧЕННЫЙ набор признаков, значений или событий и проверяется ОГРАНИЧЕННОЕ количество условий. Также вспомним об ОГРАНИЧЕННОМ количестве состояний. Таким образом, получается пусть не маленькое (в случае большого алгоритма), но явно конечное число вариантов для проверки. Главное состоит в том, что и тестирование логики будет однородным и, следовательно, его можно наверняка автоматизировать! В реальной работе я лично всегда опирался на протоколы переходов и выполнения выходных функций. В них отражены все логические "пути" программы, причем можно организовать просмотр протокола в реальном времени. Ни с чем не сравнимое чувство возникает, когда ты точно и сразу знаешь место и условия возникновения логической ошибки. А по мере набирания опыта начинаешь уже в алгоритме определять "узкие" места и учитывать всевозможные проблемы.

Ну вот, пожалуй, и все вкратце. Советую напоследок обязательно почитать классиков (труды Шалыто А.А. и Туккеля Н.И. представлены также и в Интернете). Там все будет научно и не так коряво изложено. Успехов !!!



статьи
статьи
 / 
новости
новости
 / 
контакты
контакты