на главную страницу
визитка
темы

Семинары доктора Марцинкевича
(занятие девятое)

029.11 Марцинкевич недоволен

"Что" и "как"

    Действительно, возникает вопрос: "что сделать" и "как сделать". Этот вопрос сразу же возвращает нас к вопросу "из чего сделать", то есть к вопросу о том, что мы имеем.
    А имеем мы записанные в массивах данные о маршруте. То, что у нас в ячейках - это средство контроля, или убеждения в том, что мы делаем то и получаем то, что хотим.
   Значит, адрес первой ячейки маршрута содержится в первых ячейках массивов masI[0], masJ[0], второй ячейки - в masI[1], masJ[1] и т.д. Отсюда следует: можно вызывать последовательно ячейки из  masI[i], masJ[i], где i пробегает по значениям ячеек массива. Затем, желательно, чтобы содержание ячеек маршрута соответствовало "номеру станции" маршрута". Здесь также можно применять i или i+1, в зависимости от того, начинаем мы счёт станций с ноля или единицы. И тогда получаем такую вещь: Table3->Cells[masI[i]][masJ[i]]=StrToInt(i) Т.о. мы получаем связь между ячейками таблицы и массивами.

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

   Так что "звиняйте, дядьку", наметившийся разговор о PTable3 и даже PTable4 пришлось отложить до следующего раза.

Доктор Марцинкевич делает выговор

    -Кстати, что касается твоих программ, позволь у тебя спросить, ты законченный эгоист или непреодолимо ленив?   
    -Это еще что?  
     -А я так понимаю, что ты нескончаемый эгоист и думаешь только о себе.   
    -Как это?
    -А так, ты подумал о том, что пользоваться твоими программами никто не сможет?   
    -Это еще почему?   
    -Потому что непонятно, как ими пользоваться. Я понимаю так, ты решаешь свои проблемы, и до твоих товарищей тебе никакого дела нет. И при этом делаешь вид, что делаешь это для всех.   
    -Ну...у   
    -Но я тебя, пожалуй, попробую вылечить. Ведь я знаю, что у тебя прошлого нет, и ты к сделанному не возвращаешься. Но ведь если тебе потребуется возвратиться к сделанной тобой программе, то ты и сам не сможешь ею пользоваться, во всяком случае, тебе в ней придется заново разбираться, потому что ты через самое короткое время забудешь, что там и к чему. Поэтому будь добр, раз уж ты настолько эгоистичен и ленив, то сделай это хотя бы ради себя, опиши, что делают и как пользоваться сделанными тобой программами. А если не знаешь, как это нужно делать, посмотри, как это делают другие.
    Такой или примерно такой разговор состоялся у меня с Марцинкевичем, который высказал т.о. мне своё решительное "Фе". Ну, что тут делать?! Если бы кто знал, как я не люблю во всём этом копаться. Да, пожалуй, и не могу. Кто бы только за меня это сделал?!   
    Рита: -У тебя проблемы, помочь?   
    -Обойдусь. -грубо бурчу я.
    Будут тут еще мне помогать.
   Но во всех моих действиях присутствовало, конечно, своего рода удовольствие: что-то сделать и  запутать сделанное так, чтобы в этом трудно было разобраться. Что-то в роде создания видимости того, что де чем труднее разобраться в вещи, тем она умнее. Ну, а кто совсем не сможет разобраться, тот, конечно, дурак, а я умник. Потому что на самом-то деле делаются элементарные вещи, которые "ничего не стоят". Ну, а так вроде поднимается цена. Доктору, конечно, до всех этих моих психологических изысков дела нет. Ему важна работа.  
     -Как знаешь - равнодушно сказала Рита.  
     Всё же здесь должна существовать какая-то технология. Разумеется, пользователя не может интересовать техническая сторона программы, но вот логика работы программы, знание того, какой логике подчиняются её действия, знание этого полезно. Ведь до тех пор, пока мы имеем дело просто с программой, мы имеем дело с описанием её работы по принципу "вход-выход", "действие-реакция", программа выступает для нас в виде черного ящика. Но если нам известна логика, которой подчиняется работа программы, то мы уже не имеем дела с собственно черным ящиком, как, впрочем, не имеем дела и с технической стороной реализации программы. Техническая реализация программы является не первичной, а вторичной, поскольку она также подчиняется логике, выполнение которой требует от неё человек.
   Когда мы создаём программу, нам нужно иметь представление о логике работы машины относительно всего того, что относится к порядку обработки последовательности программных кодов. Мы примем следующее представление. Представим себе круг, по которому в одном направлении вращается некая красная точка. Вместе с тем, в круге существует множество неподвижных черных точек, которых может быть больше или меньше, которые могут появляться и исчезать и которые могут устанавливаться и сбрасываться. Если красная точка натыкается на установленную черную точку, то она рассматривает её как вход в подпрограмму и входит в неё, её выполняя, а, выполнив, выходит и идёт по кругу дальше. Черные точки устанавливаются пользователем, а сбрасываются красной точкой. Еще раз. Красная точка, достигнув очередной черной точки, опрашивает её состояние. Если черная точка установлена, то красная точка входит во внутрь подпрограммы, входом в которую является черная точка  и выполняет её.
    Разумеется, внутри очередной подпрограммы могут быть другие подпрограммы и т.д.
   Входя, красная точка уже может  сбрасывать  черную точку, поскольку она начала выполнение подпрограммы, входом в которую черная точка является.
    Также черные точки могут устанавливаться не действиями пользователя, а программно.
   Примем, что программа выполняется в одном направлении,  нигде не возвращается назад. Подобного рода установка полезна в том отношении, что позволяет процесс выполнения программы рассматривать с точки зрения категории времени; ведь время тоже движется в одном направлении. При всей своей спорности подобного рода установка позволяет прочувствовать работу программы с точки зрения времени и пространства, то есть того, где, условно говоря, она находится в тот или иной момент времени. Само по себе выражение "программа находится" интуитивно понятно и имеет ввиду, что программа активна в той или иной своей части, выполняет или выполнила ту или иную свою часть. Хотя с формальной точки зрения подобного рода утверждение и не бесспорно.  
   Следующий вопрос. Выполнение программы есть процесс. Но мы не можем наблюдать процесс. Мы можем наблюдать только его результаты, связанные с выполнением тех или иных частей программы. Т.о. мы имеем исходное состояние программы, которое, может быть, сами специально формируем, имеем наше действие (событие), которое устанавливает какую-то часть программы и она её отрабатывает, и имеем результаты отработки. Т.о. мы имеем условия, действие, результат.
   А выведение всевозможных результатов действия программы  на экран позволяет судить о том, что и как делает программа.
   

Подпрограммы и управляющие программы одного уровня

    В Table3 программа состоит из множества подпрограмм, если под подпрограммой понимать функциональный блок, то есть часть программы, которая выполняет какую-то автономную функцию.
    Подпрограммы соединены между собой в последовательность.
   Этот приём нельзя признать в качестве хорошего, так как он привязывает подпрограмму к определенной программе, так что по сути это уже не подпрограмма, а функциональный блок программы. В общем случае подпрограмма может употребляться в разных программах, принадлежа в них разным уровням, разным субструктурам. Предпочтительным представляется способ, когда вход в подпрограмму осуществляется из общего всем подпрограммам управляющего блока, и подпрограмма, по завершении её выполнения, возвращает  свой адрес управляющей программе с признаком "выполнено". В этом случае, обращаясь к общему блоку из специализированных блоков, реализующих подпрограммы, мы получаем возможность использовать управляющий блок для реализации любых подпрограмм, которые могут быть реализованы на данном их множестве. У нас этот управляющий блок будет иметь индекс 12 в окне LEInst1
    -У тебя программа Table2 представляет собой множество подпрограмм, связанных между собой последовательными ссылками. В Table4, как ты выразился, у тебя есть управляющая программа, из которой ты обращаешься к подпрограммам. При этом выполненная программа даёт ссылку на управляющую программу. Есть в этой структуре одно "но": у тебя возврат - это возврат в определенную программу. 
   Схема у тебя такая: ты берешь окно и кнопку, переменную, в которую загружаются значения из окна, оператор множественного выбора switch и множество его условных ссылок case 1, 2 и т.д. С каждым из условий ты связываешь программу, которую называешь подпрограммой и которая представляет собой функциональный блок, то есть вещь, которая выполняет какую - то определенную функцию. В конце  каждой из подпрограмм ты записываешь в управляющее окно номер подпрограммы, которая должна выполняться следом и событием Button1->Click() переходишь к выполнению следующей программы. Этот способ от простого, который не имеет ввиду функциональных блоков как самостоятельных вещей и представляет собой просто последовательность операций, отличается тем, что с ним не связывается никакой жесткий порядок. Выделив функциональные блоки в отдельные подпрограммы, ты упростил процесс разработки и отладки каждой из частей программы. Ты как бы создаешь множество кубиков, из которых затем собираешь целое.
    А если у тебя будет множество подпрограмм, для которых существует множество управляющих программ, тогда что? Получается так, что управляющая программа, обращаясь к подпрограмме, должна также давать последней свой адрес возврата. В качестве промежуточного способа решения могло бы рассматриваться применение именованной константы. Это упрощает дело, но не решает проблемы, так как всякий раз при смене управляющей программы в этом случае требуется изменять значение именованной константы, то есть изменять текст программы, что может сделать программист, но не пользователь.
    Значит, всё-таки управляющая программа должна задавать адреса возврата для всякой подпрограммы, к которой она обращается.
   

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

   Теперь нам следует построить модель, реализующую эту идею. Для этого в switch сделаем три подпрограммы и две управляющие программы, которые не совершают ничего, кроме демонстрации принципа. Строим программу Pwork3

    "Пульт управления" "машиной" программы имеет вид:

   Приведем описание работы программы.
   При запуске программы состояние окон 4,4,0,0. Окно LEyp - окно установленной управляющей программы. Окно LEp - окно программы, которая отработает при нажатии на кнопку "Пуск". Окно LEpp-окно программы, которая отработала. LEresult - содержит метку отработавшей программы, которая соответствует номеру программы. Pwork3 содержит три подпрограммы 1,2,3 и две управляющие программы 4,5. Управляющие программы работают по принципу условия: следующей отрабатывает программа, условия отработки которой выполнены. Этим определяется последовательность работы программ
    Наконец, при определенных программистом условиях возможна передача управления программой от одной управляющей программы к другой.
   Вот эти принципы и иллюстрируются программой Pwork3. В ней сначала управляет управляющая программа 4, которой определяется последовательная отработка подпрограмм 1,2,3, затем управляющая программа 4 передаёт управление управляющей программе 5, которая выполняет аналогичные операции и снова передает управление программе 4.
   Запустим программу и последовательно осуществим в ней последовательность действий, представленных  таблицей 1.
 

Таблица1  LEp LEyp LEpp LEresult
1   4 4 0 0
2   1 4 0 4
3   4 4 1 1
4   2 4 1 4
5   4 4 2 2
6   3 4 2 4
7   4 4 3 3
8   5 5 0 0
9   1 5 0 5
10   5 5 1 1
11   2 5 1 5
12   5 5 2 5
13   3 2 5 5
14   5 5 3 3
15   4 4 0 0

    Проанализируем работу нескольких строк таблицы, характеризующих законченный цикл. Возьмём первую строку таблицы. В ней, условно говоря, условно, потому что реально программа находится не в ней, а "перед" ней. Итак, условно говоря, программа находится в управляющей программе 4. Нажатие на кнопку Пуск даёт  отработку программы, результаты которой приведены в строке 2, которая показывает то, что сделала на этом шаге программа: она занесла в LЕp единицу - адрес программы, которая будет отрабатывать при следующем нажатии на кн. Пуск. Одновременно она занесла свою метку 4 в LEresult.
   Очередное нажатие на Пуск даёт переход к третьей строке таблицы, которая показывает, что мы снова в управляющей программе, причем, в LЕpp записан номер (имя) отработавшей подпрограммы, и в LЕresult её метка 1 Очередное нажатие на кнопку даёт строку 4. В ней записана подпрограмма, которая будет выполняться следующей, а также записана метка управляющей программы.

   Программа Pwork3 рассматривается в качестве одного из возможных  образцов структуры построения  программ.

   


Адресации иерархий

   Вместе с тем, идея, заложенная в Pwork3,  приводит к следующей идее - адресации иерархических систем. Это уже принцип, в соответствии с которым построены адресации во всех языках, и когда мы имеем дело с проводником, например, windows, это как раз тот случай.
    Возникает вопрос, как это можно сделать логически и технически "на пальцах" .
   Относительно логической и технической сторон дела два слова. Человек начинает с логической стороны дела, ибо это - человеческая сторона. Человек - это всегда логика какого-то рода. В то же самое время техническая сторона дела отличается от логической и характеризуется совершенно иными закономерностями. Однако в результате работы технической стороны мы должны получить выполнение ею заданной ей логики.
   Если брать логическую сторону, то это выглядит т.о., что есть исходный узел дерева, который может рассматриваться в качестве константы. Ниже идут разветвления, которые образуют область значений некоторой переменной. В свою очередь, каждое из значений этой переменной может рассматриваться в качестве адреса нижестоящей переменной и т.д. "сколь угодно длинно". Т.о., всюду мы начинаем с адреса какой-то переменной. Адрес может рассматриваться в качестве указателя на соответствующую переменную. Представим себе дерево, бесконечное "вверх" и "вниз", выберем на этом дереве какой-то узел и разветвления от него, например, как на рис. 1. Что такое  "к"?  Это переменная с тремя значениями, скажем, 1,2,3.  Значение один указывает на переменную L со значениями-адресами переменных O,P,R. Значение 2... ну, и т.д.
   Отсюда мы получаем общую логику адресаций дерева: для того, чтобы оказаться в некоторой нижележащей точки дерева, мы должны начать с исходной ветви и обозначить все ветви, которые являются полным путем к конечной ветви (или узлу), к которой (-ому) мы должны перейти.

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

  Построенная нами  программа называется Pwork2

   На рис 3 представлена панель управления программой, реализующей дерево рисунка 2. Первоначально в каждом из окон Е1 - Е8 высвечиваются ноли. Вы можете выделить в любом окне цифру и заменить её другой. Нужно следить, чтобы в окнах стояли цифры. В настоящем случае правила такие: в окна заносятся цифры 0, 1, 2. Цифра 0 есть сброс информации.
   Каждое из окон есть, очевидно, переменная, поскольку в окно можно записывать разные значения чисел натурального ряда. На рис. 2 дерево начинается с 1. Это значит, что в Е1 в качестве переменной мы записываем 1, и она есть адрес вершины выделенной нами части  виртуального дерева. Е2 - это субпеременная, к которой мы переходим из адреса 1. Эта переменная  определена на двух значениях -1,2, что соответствует ветвям дерева 2.1, 2.2 рисунка 2.
   Дальнейшая логика рассуждений понятна.
   Я знаю за собой слабость: не люблю я никаких заданных логик, никаких потенциальных действий. Ты мне  покажи всё от начала до конца, чтобы я мог не думать, а только воспринимать. Мне так больше нравится. И так я чувствую себя более умным. Но на деле-то получается, что я - дурак, обезъяна. Так что приходится в этом отношении сокращаться. Дальнейшие соответствия совершенно на уровне автоматизма желающие сами проделают, а, проделав, "прочувствуют"логику, поскольку они это "сами сделали". Окна Е5-Е8 - это окна, в которые извлекается информация из соответствующих узлов дерева. В них может загружаться   всё, что угодно, важно только, чтобы эти "всё что угодно" разных узлов дерева различались друг от друга, и поэтому в разные узлы  записаны разные цифры, именно, в Е4.1 (сокращение для выражения: Е4=1; далее употребление аналогично) записано 5, которое выводится в окне Е5, в Е4.2 записано 6, которое выводится в Е5; В Е3.2 записано 3, которое выводится в Е6, в Е2.1 записано 9, которое выводится в окно Е9, а в Е2.2 записана 1, которая выводится в Е7.
    Как программа работает.
    Мы можем обратиться к любой  ячейке  только из непосредственно стоящей над ней ячейкой. Это значит, что на уровень узла номер четыре дерева можно перейти только с уровня 3, на уровень 3 - с уровня 2, на уровень 2- только с уровня 1. Поэтому если Е1.0, то мы не можем обратиться ни к одному из нижестоящих узлов дерева. Если Е1.1, то мы можем обратиться к Е2. У нас оно может принимать значения Е2.1 и Е2.2. Если Е2.2, то Е7=1, Если Е2.1, то Е8=9, и, кроме того, мы можем обратиться к уровню Е3. Если Е3.2, то Е6=3. Если Е3.1, то мы получаем разрешение обратиться к Е4. Если Е4.1, то Е5=5, если Е4.2, то Е5=6.

   Оформим работу программы в виде таблицы 2, имея ввиду, что всякий раз перед нажатием на кнопку Пуск вы восстанавливаете ноли в контрольных окнах Е5 - Е8 для того, чтобы видеть результаты  в чистом виде.

Таблица 2   Е1 Е2 Е3 Е4   Е5 Е6 Е7 Е8
1   0 0 0 0   0 0 0 0
2   1 0 0 0   0 0 0 0
3   1 1 0 0   0 0 0 9
4   1 2 0 0   0 0 1 0
5   1 1 1 0   0 0 0 9
6   1 1 2 0   0 3 0 9
7   1 1 1 1   5 0 0 9
8   1 1 1 2   6 0 0 9

   
Замечу, что 9 в Е8 вводится специально с целью показать, что можно сделать последовательно выполнение программы любого узла дерева по пути к целевому узлу.

   Теперь вы можете вызвать программу и путем экспериментов убедиться, что программа выполняет указанную логику.

   Настоящая программа является выполнением задания Доктора относительно создания программ, реализующих идею дерева, которая была дана нам Доктором перед Новым годом.


   27.01.09 г.