На главную
ПРАВИЛА FAQ Помощь Участники Календарь Избранное DigiMania RSS
msm.ru
Страницы: (17) [1] 2 3 ...  16 17 все  ( Перейти к последнему сообщению )  
    > Мастер-класс виртуозного программирования, откровения потомственного хакера
      Этот блог я посвящаю светлой памяти своего научного руководителя Зацепина П.М., одного из самых выдающихся IT-специалистов современности, глубоко осведомлённого во всех IT-вопросах и прежде всего, в вопросах программирования.
      user posted image

      Доброго времени суток, уважаемые форумчане! Я, как бы это громко ни звучало – потомственный хакер. В этом смысле мне в жизни повезло, как с родственниками, так и с учителями. Виртуозное программирование – это одна из составляющих моего хакерского бытия.

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

      Сюда, на форум, пришёл, чтобы пообщаться с тремя категориями коллег: равными, младшими и старшими:
      - младшим (тем, кто хочет научиться писать такие же программы как я) окажу посильную помощь,
      - у старших (те, кто смогут найти в моих программах баги или предложат более лаконичные решения) поучусь,
      - а с равными (те, кто пишет программы на том же уровне, что и я) – просто обменяюсь рукопожатиями.

      На панацею конечно не претендую, т.к. могут существовать разные стили программирования, вместе с тем, мой подход к программингу, объективно весьма интересен. Вот им я и собираюсь делиться в этом блоге. Больше всего внимания будет уделено:
      - вопросам математической модели, алгоритмам и блок-схемам,
      - философским и культурным аспектам программирования,
      - модульности и повторному использованию кода.

      Ну вот, довольно много громких слов сказал в свой адрес, теперь их надо как-то подтвердить. Лично для меня знакомство с программистом начинается со знакомства с его типичной программы: «Покажи мне свою программу, и я скажу, кто ты», – поэтому приведу пример своей. Так и хочется сказать, что написана special for sources.ru, но нет, её я написал в далёком 2002-м году.

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

      Чаще всего в своих мини-статьях я для иллюстрации разных концепций буду ссылаться на эту программу. Если администрация форума милостиво предоставит мне доступ к редактированию этого первого поста, то я по мере добавления новых статей, буду вставлять в него ссылки на них – для более удобной навигации по теме.

      ExpandedWrap disabled
                      {*********************************************}
                      {**           Программа Graph.pas           **}
                      {**      Эта программа рисует графики       **}
                      {**Составлена Серохвостовым Антоном 10.03.02**}
                      {**       Последняя доработка 22.05.02      **}
                      {*********************************************}
         
        {Краткое описание программы:
          Данная программа рисует график введеной в строковом
          виде функции, при вводе используются оператры такие
          же  как  и  в паскале. Вместо переменного параметра
          следует вводить z}
         
        Program MatLab;
        {*************}
         
        Uses Crt, Graph;
        {*************}
         
        Const Mode: Integer = 4;   {Графический режим 1024x780}
              Way  = 'c:\Bp\Bgi';  {Путь к графическому драйверу}
              Author: String = 'Серохвостов Антон Анатольевич';
        {*************}
         
        Var Result: Boolean;       {Флаг коректности операции}
        {*********************Подпрограммы*********************}
         
        Function Mark(S: String): String;
         {Коректирует дублирующиеся знаки}
          var i: Integer; {Позиция очередных дублирующихся знаков}
              b: Boolean; {Флаг наличия дублирующихся знаков}
         
          begin
            b := True;
            while b do
              if Pos('*-', S) or Pos('/-', S) <> 0
                then begin
                       if Pos('*-', S) <> 0
                         then begin
                                i :=  Pos('*-', S);
                                Delete(S, Pos('*-', S)+1, 1)
                              end {if Pos('*- ...}
                         else begin
                                i :=  Pos('/-', S);
                                Delete(S, Pos('/-', S)+1, 1)
                              end; {else ...}
                       while (s[i] <> '+') and (s[i] <> '-') and (i >= 1) do
                         Dec(i);
                       Insert('-', S, i+1)
                     end {if Pos('*-' ...}
                else if Pos('+-', S) <> 0
                       then Delete(S, Pos('+-', S), 1)
                       else if Pos('--', S) <> 0
                              then begin
                                     Insert('+', S, Pos('--', S));
                                     Delete(S, Pos('--', S), 2)
                                   end {if Pos('--' ...}
                              else b := False;
            Mark := S
          end; {Mark}
        {************}
         
        Function ElFunc(S: String): Real;
         {Вычисляет линейные функции, т.е. самые внутренний скобки}
          label 1;
         
          var Rez,             {Значение выражения}
              x   : Real;      {Строка S1 в числовом виде}
              s1  : String;    {Очередное   число  строки}
              Oper: Char;      {Очередной  знак  операции}
              o,               {Позиция операции в строке}
              j,               {Определение первого вхождения в условие}
              i,               {Счетчик}
              Code: Integer;   {Номер   ошибочного   символа  в  строке}
         
          begin
           {***Выделение умножения и деления***}
            S := Mark(S);                  {Корекция  дублирующихся  знаков}
            while (Pos('/', S) <> 0) or (Pos('*', S) <> 0) do
              begin
                if ((Pos('/', S) < Pos('*', S))) and (Pos('/', S) <> 0)
                                                       or (Pos('*', S) = 0)
                  then begin
                         Oper := '/'; o := Pos('/', S)
                       end {if ((Pos('/' ...}
                  else if Pos('*', S) <> 0
                         then begin
                                Oper := '*'; o := Pos('*', S)
                              end; {else if Pos('*' ...}
           {---Выделение числа после знака---}
                s1 := ''; i := o+1;
                while (Pos(S[i], '+-*/)') = 0) and (i <> Length(S)+1) do
                  begin
                    s1 := s1+S[i]; Inc(i)
                  end; {while (Pos(S[ ...}
                Val(s1, Rez, Code);
           {---Удаление этого числа из строки---}
                Delete(S, o+1, Length(s1));
           {---Выделение числа до знака---}
                s1 := ''; i := o-1;
                while (Pos(S[i], '+-*/') = 0) and (i <> 0) do
                  begin
                    s1 := S[i]+s1; Dec(i)
                  end; {while (S[ ...}
                Val(s1, x, Code);
           {---Удаление этого числа из строки---}
                Delete(S, i+1, Length(s1)+1);
           {---Умножение и деление чисел---}
                case Oper of
                  '/': if Rez <> 0 then Rez := x/Rez
                                   else Result := False;
                  '*': Rez := x*Rez
                end; {case}
           {---Запись числа в строку S---}
                Str(Rez: 5: 3, s1);
                Insert(s1, S, i+1); if S[i+1] = ' ' then Delete(S, i+1, 1)
              end; {while (Pos('/' ...}
           {***Вычисление операций сложения и вычитания***}
            j := 1; s1 := '';
            S := Mark(S);                  {Корекция  дублирующихся  знаков}
            for i := 1 to Length(S) do
           {---Выделение операции---}
              if Pos(S[i], '+-') <> 0
                then begin
                       1:                       {Метка   безусловного   перехода}
                       Val(S1, x, Code);        {Преобразование строки  в  число}
                       if j = 1                 {Если в условие входили один раз}
                         then begin             {то    идти   в   это    условие}
                                Rez := x; j := 0;
                                Oper := S[i]; s1 := ''
                              end {if j = 1 ...}
                         else begin
                 {---Выбор операции над числом---}
                                case Oper OF
                                  '+': Rez := Rez+x;  {Сложение  чисел}
                                  '-': Rez := Rez-x   {Вычитание чисел}
                                end; {case ...}
                                Oper := S[i]; s1 := ''{Выделение очередной операции}
                              end  {else ...}
                     end {if (S[i] = '*') ...}
                else begin                 {Если очередной символ строки}
                       s1 := s1+S[i];      {не равен знаку операции, то }
                       if i = Length(S)    {изменить   числовую   строку}
                         then Goto 1
                     end; {else ...}
            ElFunc := Rez
          end; {ElFunc}
        {************************Объекты***********************}
         
        Type
         {---Меню---}
          TMenu = object
            Num: Byte;            {Номер текущего пункта меню}
            e, ex: Boolean;    {Флаги <F1>, <Enter> и <Esc> соответственно}
            x, y  : Integer;      {Координаты верхнего левого угла}
            Kol   : Integer;      {Количество пунктов меню}
            txt   : array [1..50] of String; {Текст в пункте меню}
            rColor: Word;         {Цвет пунктов меню}
            function Menu: Byte;  {Осуществляет работу с меню}
             {Возвращает номер выбраного пункта}
          end; {TMenu}
         
         {---Оформдение меню---}
          TForm = object (TMenu)
            Title : String;       {Заголовок меню}
            tColor: WORD;         {Цвет текста}
            procedure Form;       {Рисует меню}
            procedure Standart;   {Ставит установки по умолчанию}
          end; {TForm}
         
         {---Вычисление функции---}
          TFunc = object
            XMax, YMax: Real;     {Максимальные значения переменных по осям}
            dx, dy: Real;         {Размер единицы по осям}
            Fun: String;          {Функция в строковом виде}
         
            function Translate(S: String; var a: Real): Boolean;
             {Вычисляет функцию представленую в строковом виде}
            function WithFunc(S: String): Real;
             {Вычисляет выражения со сложными функциями}
            procedure Param(var S: String; a: Real);
             {Заменяет параметр в строке числом}
            Function EnterFunc(x, y: Integer): Boolean;
              {Вводит функцию в графическом режиме в заданном месте}
          end; {TFunc}
         
         {---Построитель графика---}
          TGraph = object (TFunc)
            procedure GraphInit;
             {Инициализирует графику с адаптером SVGA}
            procedure Fon;
             {Рисует заставку}
            procedure SysKoord;
             {Рисует систему координат}
            procedure Go;     {0 - 500, 380}
             {Строит график}  {960 = 480*2, 700 = 350*2- количество пикселов на осях}
            function SetY(y: Real): Real;
             {Устанавливают стандартную систему координат}
            function SetX(x: Real): Real;
             {Устанавливает сиандартную систему координат}
          end; {TGraph}
        {************************TForm*************************}
         
        Procedure TForm.Form;
          var i: Integer;
         
          begin
            SetColor(tColor); SetFillStyle(1, rColor);
            i := 1;
           {---Вывод заголовка меню---}
            SetTextStyle(4, 0, 10);
            OutTextXY(x-Length(Title)*20+170, y-100, Title);
           {---Вывод заголовков пунктов меню---}
            SetTextStyle(2, 0, 10);
            repeat
              Bar(x-3, i*60+y-4, x+563, i*60+y+42);
              OutTextXY(x+10, i*60+y-1, txt[i]);
              Inc(i)
            until i > Kol
          end; {TForm.Form}
        {************}
         
        Procedure TForm.Standart;
          begin
             x := (GetMaxX-500) div 2;
             y := (GetMaxY-Round(60*(Kol+1))) div 2;
             Title := 'MENU'; Num := 1;
             rColor := Blue; tColor := Yellow
          end; {TForm.Standart}
        {************************TMenu*************************}
         
        Function TMenu.Menu;
          var Ch: Char;
         
          begin
            repeat
             {---Прорисовка курсора---}
              SetFillStyle(1, Green);
              Bar(x, Num*60+y-1, x+560, Num*60+y+39);
              OutTextXY(x+10, Num*60+y-1, txt[Num]);
             {---Ожидание нажатия клавиши---}
              Ch := ReadKey;
              if Ch = #0 then Ch := ReadKey;
             {---Стирание курсора---}
              SetFillStyle(1, rColor);
              Bar(x, Num*60+y-1, x+560, Num*60+y+39);
              OutTextXY(x+10, Num*60+y-1, txt[Num]);
             {---Проверка нажатой клавиши---}
              e := False; ex := False;
              case Ch of
                #72: {Код клавиши <Стрелка вверх>}
                  if Num > 1 then Dec(Num);
                #80: {Код клавиши <Стрелка вниз>}
                  if Num < Kol then Inc(Num);
                #13: {Код клавиши <Enter>}
                  e := True;
                #27: {Код клавиши <Esc>}
                  ex := True
              end {case ...}
            until Pos(Ch, #13#27#59) <> 0;
           {---Если не нажата отмена, то выбрать пункт меню---}
            if Ch <> #27 then Menu := Num
                         else Menu := 0
          end; {TMenu.Menu}
        {*************************TFunc************************}
         
        Function TFunc.Translate(S: String; var a: Real): Boolean;
         {Вычисляет функцию представленую в строковом виде}
          var i: Byte;
         
          begin
            Translate := True;
            for i := 1 to Length(S) do
              S[i] := UpCase(S[i]);
            if S = 'SIN'
              then a := sin(a)         else
            if S = 'COS'
              then a := cos(a)         else
            if (S = 'TAN') and (cos(a) <> 0)
              then a := sin(a)/cos(a)  else
            if (S = 'TAN') and (cos(a) = 0)
              then Result := False     else
            if (S = 'CTAN') and (sin(a) <> 0)
              then a := cos(a)/sin(a)  else
            if (S = 'CTAN') and (sin(a) = 0)
              then Result := False     else
            if (S = 'LN') and (a > 0)
              then a := ln(a)          else
            if (S = 'LN') and (a <= 0)
              then Result := False     else
            if (S = 'EXP') and (abs(a) >= 88)
              then Result := False     else
            if (S = 'EXP') and (abs(a) < 88)
              then a := exp(a)         else
            if S = 'SQR'
              then a := sqr(a)         else
            if (S = 'SQRT') and (a >= 0)
              then a := sqrt(a)        else
            if (S = 'SQRT') and (a < 0)
              then Result := False     else
            if S = 'ABS'
              then a := abs(a)         else
            if S = 'T'
              then a := a              else
            if S = 'ARCTAN'
              then a := arctan(a)      else
            if S = 'FRAC'
              then a := frac(a)        else
            if S = 'INT'
              then a := int(a)         else
            if S = 'ROUND'
              then a :=  Round(a)
              else Translate := False
          end; {Translate}
        {************}
         
        Function TFunc.WithFunc(S: String): Real;
         {Вычисляет выражения со сложными функциями}
          var i: Byte;           {Счетчик}
              Func,              {Очередная функция}
              Sr: String;        {Результат в строковом виде}
              Rez: Real;         {Результат вычислений}
         
          begin
            while Pos(')', S) <> 0 do
              begin
           {---Выделение очередных скобок---}
                i := Pos(')', S); Sr := '';
                while S[i-1] <> '(' do
                  begin
                    Dec(i); Sr := S[i]+Sr
                  end; {while S[ ...}
           {---Вычисление скобок---}
                while S[i] <> ')' do Delete(S, i, 1);
                Rez := ElFunc(Sr); Str(Rez: 5: 3, Sr);
                Insert(Sr, S, i);
           {---Проверка наличия функции---}
                Func := '';
                while (Pos(S[i-2], '+-/*(') = 0) and (i > 2) do
                  begin
                    Func := S[i-2]+Func; Dec(i)
                  end; {while (S[i-1 ...}
           {---Вычисление функции---}
                if Translate(Func, Rez) {Если перед скобкой функция,}
                  then begin            {то выполнить ее}
                         Str(Rez: 5: 3, Sr);
                         while S[i-1] <> ')' DO Delete(S, i-1, 1);
                         Delete(S, i-1, 1); Insert(Sr, S, i-1);
                       end {Translate( ...}
                  else begin            {иначе удалить скобки}
                         Delete(S, i-1, 1); Delete(S, i+Length(Sr)-1, 1)
                       end {else ...}
              end; {while Pos ...}
            WithFunc := ElFunc(S)
          end; {WithFunc}
        {************}
         
        Procedure TFunc.Param(var S: String; a: Real);
         {Заменяет параметр в строке числом}
          var i: Byte;    {Позиция параметра в строке}
              Sa: String; {Значение параметра в строковом виде}
         
          begin
           {---Преобразование строчных букв в прописные---}
            for i := 1 to Length(S) do
              S[i] := UpCase(S[i]);
           {---Замена переменной на число---}
            i := Pos('Z', S); Str(a: 5: 2, Sa);
            while i <> 0 do
              begin
                Delete(S, i, 1);
                Insert(Sa, S, i);
                i := Pos('Z', S)
              end {while i ...}
          end; {TFunc.Param}
        {************}
         
        Function TFunc.EnterFunc(x, y: Integer): Boolean;
         {Вводит функцию в графическом режиме в заданном месте}
          var Ch: Char;  {Очередной введеный символ}
         
          begin
            Fun := '';
            repeat
           {---Сканирование нажатой клавиши---}
              Ch := ReadKey;
              if Ch = #0 then Ch := ReadKey;
           {---Выбор действия---}
              case Ch of
                '0'..'9', 'a'..'z', 'A'..'Z', '.',
                '(', ')', '+', '-', '*', '/': if Length(Fun) < 70
                                                then begin
                                                       Fun := Fun+Ch;
                                                       Bar(x, y, x+810, y+20);
                                                       OutTextXY(x, y, Fun)
                                                     end; {if Length ...}
                #8: begin               {Код клавиши <BackSpace>}
                      Delete(Fun, Length(Fun), 1);
                      Bar(x, y, x+810, y+20);
                      OutTextXY(x, y, Fun)
                    end; {#8 ...}
                #13: EnterFunc := True; {Код клавиши <Enter>}
                #27: EnterFunc := False {Код клавиши <Esc>}
              end {case Ch ...}
            until (Ch = #13) or (Ch = #27)
          end; {EnterFunc}
        {***********************TGraph************************}
         
        Procedure TGraph.GraphInit;
         {Инициализирует графику с адаптером SVGA}
          var Driver: Integer;
         
          begin
           {$F+}
            ChDir(Way);
            Driver := InstallUserDriver('SVGA256', nil);
           {$F-}
            InitGraph(Driver, Mode, Way)
          end; {TGraph.GraphInit}
        {************}
         
        Procedure TGraph.Fon;
         {Рисует заставку}
          var i, j: Integer; {Счетчики}
         
          begin
            Randomize;
            SetColor(White);
            SetTextStyle(1, 0, 10);
            OutTextXY(310, 260, 'МатЛаб');
         {***************}
            SetTextStyle(1, 0, 2);
            OutTextXY(630, 700, 'Автор: ');
            i := 1;
           {---Рисование падающих букв---}
            repeat
              j := 600;
              repeat
                SetColor(Black);  OutTextXY(700+i*10, j-1, Author[i]);
                SetColor(Yellow); OutTextXY(700+i*10, j, Author[i]);
                Delay(2); Inc(j)
              until (j = 700) or KeyPressed;
              Sound(800); Delay(2);
              NoSound; Inc(i)
            until (i = 30) or KeyPressed;
           {---Рисование элипса меняющего цвет---}
            repeat
              SetColor(Random(256));
              for i := 1 to 30 do
                Ellipse(520, 325, 0, 360, 300+i*2, 100+i*3)
            until KeyPressed;
            ReadKey
          end; {TGraph.Fon}
        {************}
         
        Procedure TGraph.SysKoord;
         {Рисует систему координат}
          var i: Integer; {Счетчик}
              s: String;  {Очередная координата в строковом виде}
         
          begin
            SetTextStyle(0, 0, 0); SetColor(Green);
           {---Координатные оси---}
            Line(500, 20, 500, 747);   Line(0, 380, 1003, 380);
           {---Стрелки---}
            Line(1003, 380, 995, 382); Line(1003, 380, 995, 378);
            Line(500, 20, 502, 28);    Line(500, 20, 498, 28);
           {---Деления на оси абсцисс---}
            i := -480;
            repeat
              SetColor(Green);
              if abs(SetX(i*dx/2)) < 1024
                then if Odd(i)
                       then Line(Round(SetX(i*dx/2)), 380,
                                 Round(SetX(i*dx/2)), 375)
                       else begin
                              Line(Round(SetX(i*dx/2)), 380,
                                   Round(SetX(i*dx/2)), 370);
                              Str(i/2: 1: 0, s); SetColor(White);
                              OutTextXY(Round(SetX(i*dx/2))-10, 385, s)
                            end;
              Inc(i)
            until i*dx/2 > 480;
           {---Деления на оси ординат---}
            i := -350;
            repeat
              SetColor(Green);
              if abs(SetY(i*dy/2)) < 780
                then if Odd(i)
                       then Line(500, Round(SetY(i*dy/2)),
                                 505, Round(SetY(i*dy/2)))
                       else begin
                              Line(500, Round(SetY(i*dy/2)),
                                   510,Round(SetY(i*dy/2)));
                              Str(i/2: 1: 0, s); SetColor(White);
                              OutTextXY(470, Round(SetY(i*dy/2)), s)
                            end;
              Inc(i); if i = 0 then Inc(i)
            until i*dy/2 > 350;
          end; {TGraph.SysKoord}
        {************}
         
        Procedure TGraph.Go;
          var z,                       {Очередное значение аргумента}
              OldX, OldY, x, y: Real;  {Старые и новые координаты точки}
              b: Boolean;              {Флаг первого вхождения в цикл}
              s: String;               {Функция с вставленными параметрами}
              Ch: Char;                {Детектор нажатой клавиши}
         
          begin
            SetFillStyle(1, Black); SetColor(White);
            repeat
           {---Очистка экрана от старого графика---}
              Bar(0, 0, GetMaxX, GetMaxY);
           {---Рисование сетки---}
              SetColor(DarkGray); z := -480;
              repeat
                if abs(SetX(z*dx)) < 1024
                  then Line(Round(SetX(z*dx)), 0, Round(SetX(z*dx)), 780);
                if abs(SetY(z*dx)) < 780
                  then Line(0, Round(SetY(z*dx)), 1024, Round(SetY(z*dx)));
                z := z+1
              until z > 480;
           {---Рисование осей координат---}
              SysKoord; b := False;
              SetColor(Green);
           {---Рисование нового графика---}
              z := -480/dx;
              while z < 480/dx do
                begin
                  Result := True;
                  s := Fun; Param(s, z); {Вставка в функцию очередного значения}
                  x := SetX(z*dx); y := SetY(WithFunc(s)*dy);
                  if Result and b and (abs(x) < 1024) and (abs(y) < 780)
                    then Line(Round(x), Round(y), Round(OldX), Round(OldY))
                    else if Result and not b
                           then b := True          {Разрешение рисование линии}
                           else if not Result
                                  then b := False; {Запрет рисования линии}
                  OldX := x; OldY := y;            {Сохранение старых координат}
                  z := z+1/dx
                end; {while z ...}
           {---Изменение масштаба---}
              Ch := ReadKey;     {Чтение нажатой клавиши}
              if Ch = #0 then Ch := ReadKey;
              case Ch of
                #43: begin       {Увеличение масштаба при нажатии <+>}
                       dx := dx*2;
                       dy := dy*2
                     end; {#43 ...}
                #45: begin       {Уменьшение масштаба при нажатии <->}
                       dx := dx/2;
                       dy := dy/2
                     end {#45 ...}
              end {case Ch ...}
            until Ch = #27
          end; {TGraph.Go}
        {************}
         
        Function TGraph.SetY(y: Real): Real;
         {Устанавливает привычную систему координат}
          begin
            SetY := GetMaxY-387-y
          end; {TGraph.SetY}
        {************}
         
        Function TGraph.SetX(x: Real): Real;
         {Устанавливает привычную систему координат}
          begin
            SetX := GetMaxX-523+x
          end; {TGraph.SetX}
        {************}
         
        Var s : String;
            Gr: TGraph;
            MainMenu, Men: TForm;
        {*******************Главная программа*****************}
         
        Begin
          Gr.GraphInit; Gr.Fon;
         {---Установка опций главного меню---}
          with MainMenu do Begin
                             Standart;   {Установка стандартного состояния}
                             Kol := 3;   {Установка количества пунктов}
                             txt[1] := '         Меню';
                             txt[2] := '         Автор';
                             txt[3] := '         Выход'
                           End; {with MainMenu ...}
         {---Установка опций меню---}
          with Men do Begin
                        Standart;        {Установка стандартного состояния}
                        Kol := 4;        {Установка количества пунктов}
                        txt[1] := '  Построение графиков';
                        txt[2] := 'Вычисление производных';
                        txt[3] := ' Вычисление интегралов';
                        txt[4] := '         Выход'
                      End; {with Men ...}
         {---Работа с меню---}
          repeat
            SetFillStyle(1, Black);
            Bar(0, 0, GetMaxX, GetMaxY);
            MainMenu.Form; MainMenu.Menu;{Рисование меню и работа с ним}
            SetFillStyle(1, Black);
            Bar(0, 0, GetMaxX, GetMaxY); {Очистка экрана}
            if MainMenu.e
              then case MainMenu.Num of
                     1: Begin
                          repeat
                            SetFillStyle(1, Bl
                            Bar(0, 0, GetMaxX, GetMaxY);
                            Men.Form; Men.Menu;    {Рисование меню и работа с ним}
                            SetFillStyle(1, Black);
                            Bar(0, 0, GetMaxX, GetMaxY);          {Очистка экрана}
                            if Men.e
                              then case Men.Num of
                                     1: Begin
                                         {---Ввод закона---}
                                          Gr.dx := 10; Gr.dy := 10;
                                          Result := True;
                                          SetTextStyle(2, 0, 7); SetColor(White);
                                          OutTextXY(10, 100, 'Введите функцию: ');
                                          if Gr.EnterFunc(220, 100)
                                         {------}
                                            then Gr.Go {построение графика}
                                        End; {1 ...}
                                     2, 3: Begin
                                            SetTextStyle(3, 0, 10);
                                            OutTextXY(150, 100, 'Эта функция');
                                            OutTextXY(300, 250, 'пока не');
                                            OutTextXY(260, 400, 'доступна');
                                            ReadKey
                                           End {2, ...}
                                   End {case Men ...}
                          until (Men.Num = 4) or Men.ex
                        End; {1 ...}
                     2: Begin
                          Gr.Fon
                        End {2 ...}
                   End {case MainMenu ...}
          until (MainMenu.Num = 3) or MainMenu.ex;
         {------}
          TextMode(1)
        End. {of program}
        {ТЕСТОВЫЕ ДАННЫЕ:
          вход                       выход
          sin(z)                  |  синусоида
          sqr(sin(z))+sqr(cos(z)) |  прямая параллельная оси Ox
          1                       |  прямая параллельная оси Ox
          z                       |  прямая наклоненная под 45 градусов
          1/z                     |  гипербола
          sqr(cos(z))-cos(z)      |  периодическая функция
          sqr(cos(sin(z))+cos(z)  |  косинусоида
         
        АЛГОРИТМ:
        ****************Главная программа****************
          1. Ввод функции.
          2. Установка масштаба по умолчанию (с игнорированием ошибочных мест).
            1.1. Вычисление функции начиная с самой
                 внтуренней для очередного аргумента.
            1.2. Рисование сетки.
            1.3. Рисование осей координат.
            1.4. Рисование графика.
        ****************Функция Mark****************
        Эта функция корректирует дублирующиеся знаки
         
        ****************ElFunc****************
        Эта функция вычисляет выражение без скобок
          1.
        ****************Объект TMenu****************
        Этот объект осуществляет работу с меню
        ---------Процедура Menu---------
         Эта процедура осуществляет перемещение по меню
          1. Прорисовка курсора.
          2. Ожидание нажатия клавиши.
          3. Стирание курсора.
          4. Проверка нажатой клавиши и выбор соответствующего пункта меню
         
        ****************Объект TForm****************
        Этот объект оформляет меню
        ---------Процедура Form---------
         Эта процедура оформляет меню
          1. Вывод заголовка меню
          2. Вывод заголовков пунктов меню
        ---------Процедура Standart-----
        Эта процедура устанавливает опции меню по умолчанию
          1. Поиск умножения или деления.
          2. Если не найдены умножение и деление, то искать сложение и вычитание.
          3. Выделение чисел до и после операции и их удаление.
          4. Выполнение операции и записть результата в строку.
          5. Повторять, пока не будут выполнены все операции.
        ****************Объект TFunc****************
        Этот бъект вычисляет функцию в строковом виде
        ---------Функция Translate---------
        Эта функция вычисляет сложную функцию.
          1. Проверка переданной строки на функциональность
          2. Если строка является функцией и передаваемое значение есть
             в области определения функции, то вычислить эту функцию.
          3. Иначе возвратить признак ошибки.
        ---------Функция WithFunc---------
        Эта функция вычисляет строку со сложными функциями.
          1. Поиск очередной закрывающей скобки.
          2. Взятие содержимого этих скобок и вычисление его.
          3. Если перед открывающей скобка стоит сложная функция,
             то вычислить ее и повторить пункты 1 и 2.
          4. Иначе если перед открывающей скобкой нет сложной
             функции, то повторить 1 и 2.
          5. Повторять все действия, пока не будут выполнены все операции.
        ---------Процедура Param---------
        Эта процедура заменяет параметр в функции конкретным числом.
          1. Поиск параметра в функции.
          2. Если параметр найден, то заменить его на число.
        ---------Функция EnterFunc---------
        Эта процедцра осуществляет ввод функции.
          1. Сканирование нажатой клавиши.
          2. Если символ правильный, то вывести его.
        ****************Объект TGraph****************
        Этот объект рисует график.
        ---------Процедура GraphInit---------
        Эта процедура инициализирует графику с адаптером SVGA.
        ---------Процедура Fon---------
        Эта процедура рисует заставку.
        ---------Процедура SysKoord---------
        Эта процедура рисует систему координат.
          1. Прорисовка осей.
          2. Нанесение делений с учетом масштаба.
        ---------Процедура Go---------
        Эта процедура рисует график.
          1. Рисование сетки с учетом масштаба.
          2. Рисование осей координат.
          3. Рисование графика.
          3.1. Вычисление функции от очередного аргумента.
          3.2. Рисование отрезка от предыдущей координаты до текущей.
          3.3. Повторение пунктов 3.1 и 3.2 требуемое число раз.
          4. Изменение масштаба и повтор всех действий или выход
        ---------Функции SetX и SetY---------
        Эти функции устанавливают привычную систему координат


      PS. Если я слишком громким пинком ворвался в ваше дружное Сообщество, прошу прощения за это. Если никто не против, в скором времени напишу сюда что-нибудь ещё. Конструктивная обратная связь приветствуется.

      Прикреплённый файлПрикреплённый файлGrapher.pas (26 Кбайт, скачиваний: 195)
      Сообщение отредактировано: Серохвостов Антон -
      Подпись выключена.
        Потомственный компьютерщик знает, что такое блог?

        Имхо, тему надо из алгоритмов в блоги перенеси.
        Мои религиозные убеждения не позволяют мне комментировать код.
        Моё мировоззренье таково: в программе комментария ни одного!
          О, а тут разве есть раздел блогов? Не нашёл. Переносу не возражаю.
          Подпись выключена.
            Цитата Серохвостов Антон @
            Конструктивная обратная связь приветствуется

            - А turbo pascal на современных ОС вообще запускается? Насколько я помню, при попытке подключения Crt на Windows XP (в 2002 г. она уже была ведь?) весь процесс сыпался с Access Denied.
            - интуитивно понятные идентификаторы. Вместо того, чтобы писать рядом с объявлениями переменных комментарии в стиле Капитана Очевидность, лучше дать переменным нормальные имена (имена: b, x, s1, o и т.п. к нормальным не относятся).
            - грамотная разбивка на модули. Что-то модулей не видно :-?
            - хорошее документирование. Обычно "главный" комментарий помещают в начале файла, а не в конце. Описания функций я бы перенес поближе к самим функциям.
            - хорошая структура. Использование goto даже в 2002 г. считалось моветоном. Вложенные case тоже не производят хорошего впечатления. Я бы разбил код на кучу небольших функций со значимыми именами, чтение и понимание кода от этого только улучшится.

            Мелочи:
            Английский:
            ExpandedWrap disabled
              Way  = 'c:\Bp\Bgi';

            может все-таки Path?

            "Волшебные" числа:
            ExpandedWrap disabled
              case Ch of
                      #72: {Код клавиши <Стрелка вверх>}
                        if Num > 1 then Dec(Num);
                      #80: {Код клавиши <Стрелка вниз>}
                        if Num < Kol then Inc(Num);
                      #13: {Код клавиши <Enter>}
                        e := True;
                      #27: {Код клавиши <Esc>}
                        ex := True
              end {case ...}

            почему бы не создать константы со значащими именами?
              прекрасная тема, замер в ожидании продолжения
              Цитата usrjava @
              Технологии в основе, которых по-сути лежит javascript в расширенном так сказать виде
                ПОЧЕМУ ПРОГРАММЕРЫ НЕ ПИШУТ ВИРТУОЗНЫХ ПРОГРАММ

                user posted image

                Сейчас есть столько разных способов сделать всё что угодно, но куда меньше времени уделяется вопросу «зачем». Программирование – не исключение. Поэтому когда новичок или продвинутый программист сталкивается с широким ассортиментом инструментария, то испытывает с его стороны информационную агрессию. Потому как инструментов так много, а жизнь так коротка, – что все их изучить просто невозможно. А ведь хочется! И от осознания невозможности этого, человек впадает в депрессию.

                Причём продвинутый программист в куда более плачевном состоянии находится в этом смысле, потому как он, в отличие от новичка, «знает, что ничего не знает». Я например, когда загорелся стать хакером, – а это было аж ещё в прошлом тысячелетии, – имел свои представления о том, что должен уметь хакер. Думал: «Вот стану хакером и заживу!» Ан нет! Сейчас я знаю и умею как минимум в 50 раз больше тех своих первоначальных представлений о хакерстве, но зуд к познанию чего-то нового до сих пор не унят. Мой круг непознанного с тех пор как я загорелся стать хакером, стал как минимум в 500 раз больше. И похоже, он будет только расширяться.


                Цивилизация методов

                Нужно признать, что наша цивилизация – это цивилизация методов, а не целей. В нашей цивилизации люди, когда они видят какую-нибудь цель, они очень хорошо знают методы каким образом этой цели достичь. Проблема в том, что они не знают, ЗАЧЕМ в конце концов всё это нужно. В программинге это в частности выражается в том, что даже кулхацкеры, начиная писать программу, редко задумываются о «постановке задачи». Спроси сейчас любого студента-программиста об этом термине, и он вряд ли что-то вразумительное про него скажет. В лучшем случае, он подумает пару-тройку минут о математической модели и алгоритме, – но скорее всего это будет не от осознания практичной полезности данного действа, а в качестве дани почтения религиозным традициям, установленным родоначальниками программирования.

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

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


                Романтика программирования

                Тогда как программирование – это процесс, который должен удовольствие прежде всего приносить. Наверняка же, каждый из нас, на заре своей программерской карьеры рисовал романтические картины будущего. Но очень часто эти романтические картины размываются повседневной рутиной. Это происходит только потому, что зачастую мы считаем вопрос «зачем» не практичным, но вот вопросы «как» задаём на каждом шагу. Это выражается например в том, что я думаю:
                - «О! Паскаль хороший язык – надо его выучить».
                - «О! Руткиты это круто – надо научиться делать их».
                - «О! Вебсторительство это модно – надо обязательно реализоваться в этой области».
                - «О! Компиляторы это мощно, надо обязательно сделать свой».
                Этот список можно продолжать до бесконечности. Но курьёз в том, что если у нас нет внутри удовлетворённости, то даже изучив весь этот список, – автор статьи, например знает больше 10 языков программирования, – мы не сможем на практике пользоваться полученным знанием. Потому что нездоровый зуд информационного обжорства будет подсовывать нам всё новые и новые технологии, и мы так всю жизнь их и произучаем, не создав ничего сколь-нибудь стоящего в своей жизни. Поэтому и говорится, что «лучшее – враг хорошего».


                Лучшее – враг хорошего

                Да, предела совершенству нет, – в том числе и в программинге, – но если мы, стремясь к утопическому совершенству, не делаем реальных дел, то грош цена всем нашим знаниям. Да, образование это непрерывный процесс, но чтобы на дереве нашего знания появлялись плоды, – а не просто разрастались ветки, как у баньяна, – нужно знать, ЗАЧЕМ нам всё это. Имея ответ на этот вопрос, живя в соответствии с этим ответом и понимая, как те программы, которые мы пишем, связаны с этим ответом, мы будем спокойны и удовлетворены, потому как будем знать, что наша работа – это не бессмысленный ослиный труд, а нечто полезное, прежде всего для меня самого. Поэтому мы будем вкладываться в неё, и поэтому каждая наша программа будет по-настоящему выдающейся. Такой, что на неё даже самим приятно смотреть будет.

                Без этого, без внятного ответа на вопрос ЗАЧЕМ, у нас просто не будет времени на то, чтобы наслаждаться процессом программирования. Единственная наша цель при написании программ будет состоять в том, чтобы поскорее закончить эту программу и начать что-то другое. При таких обстоятельствах о виртуозном программировании не может быть и речи. В таком случае программирование для нас очень быстро превратится либо в ненавистную работу, либо мы вообще забросим его.

                Поэтому давайте поставим в своём сознании брейкпоинт на модуль отвечающий за наши цели – цели жизни вообще и цели программинга в частности – и в пошаговом режиме оттрассируем свои жизненные ценности. Ведь у каждого из нас есть какие-то свои понятия об Абсолютной Истине, поэтому если нам удастся увязать всю свою жизнь с этими понятиями, то она станет более наполненной. Так, пройдя науку самосознания, мы сможем получать больше удовольствия от жизни и от программирования. И тогда наши первоначальные романтические надежды о программинге – оправдаются. Для меня например таким брейкпоинтом стала книга «Наука самосознания». Может она поможет и вам. Почитайте!


                Благодарность Крэю

                Вышеприведённые размышления – это часть мыслей, навеянных ответом пользователя Kray74. Вот небольшой комментарий непосредственно к его посту. 1) Что касается запуска Turbo Pascal на современных ОС, то тут дело даже не столько в ОС, сколько в современных компьютерах. На моём старом компе Pascal в XP работал без проблем. Но на той же XP, но уже на новом железе, действительно ошибка выдаётся. Вместе с тем, при наличии желания и бубна в руках, можно всё что угодно сделать. В сети можно найти патч для turbo.tpl, который хоть и имеет свои побочные эффекты, позволяет паскалевским программам работать и сейчас. Но я согласен, что это весомый аргумент в пользу того, чтобы воздержаться от использования Паскаля, поэтому ниже приведу пример уже на Си. Вместе с тем, мне Pascal очень нравится, поэтому я очень часто буду иллюстрировать свои мысли – именно им. 2) Что касается интуитивно понятных идентификаторов и стандартных имён переменных – критика принимается; не зря говорится, «предела совершенству нет». 3) Что касается документирования, разбивки на функции и использования оператора goto, то это вещи в значительно степени субъективные. Ваш подход имеет столько же прав на существование, сколько и мой. Поэтому давайте позволим нашим подходам просто быть, воздержавшись от священных войн.

                Вместе с тем, отдавая дань почтения интересности вопроса о goto, приведу несколько ссылок на связанные с этим обсуждения: 1 и 2 (эти беседы были инициированы мной в 2007 году). Дискуссии о правомерности использования оператора goto ведутся ещё с 1968 года, но т.к. однозначного вердикта по этому вопросу нет до сих пор, данный оператор имеет место быть даже в современных компиляторах.

                На самом деле, goto – это не единственный объект священных войн и не единственная дань поговорке «если нельзя, но очень хочется, то можно». Взять например СИшные friend-функции, ассемблерные вставки или трюки с созданием самомодифицирующегося кода. Это происходит потому, что реальные задачи часто не вписываются в общепринятые лаконичные парадигмы. Да, когда мы решаем какую-то рафинированную школьную задачку, её можно сделать «по всем правилам», но при решении реальных задача бывает нужно учитывать много нюансов. Мне это известно не понаслышке, потому как в своей программерской жизни довелось участвовать в разработке одного из компиляторов (см. сюда).

                Помню на 5-м курсе универа, когда я уже мнил себя величайшим программистом, преподы начали ломать мои стереотипы. В частности и стереотипы относительно goto. Это они делали, чтобы я не был религиозным фанатиком, пренебрегающим оператором goto даже в тех местах, где это оправдано. Преподы давали мне ряд противоречивых заданий, от которых у меня просто взрывался мозг. Эти задания можно сравнить с армейскими приколами, когда тебя заставляют красить траву в зелёный цвет. Я не понимал, зачем преподы заставляли меня делать те глупые вещи, за которые на начальных курсах – ругали. Но сейчас я благодарен им за это, потому что это даёт мне возможность находить эффективные решения в соответствии со временем, местом и обстоятельствами.


                Вместо заключения

                Как уже говорилось в начале статьи, зачастую мы сосредотачиваемся на вопросах «как», пренебрегая вопросом «зачем». Одна из мыслей пользователя Kray74 заключалась в том, что хорошо бы разбить вложенные операторы case на несколько функций. Вполне разумное замечание, можно воспользоваться. А напоследок я приведу ещё один вариант реализации меню. Правда он – ну уже для самых извращенцев. Кстати, если кто-то сможет найти в этом решении баг, то от меня – бездонные респект и уважуха. Потому как данную программу тестировал человек, который для меня является большим авторитетом в программировании, и который просто виртуозно ошибки искать умеет. Бывало, что я тестирую программу по всем классическим канонам тестирования в течении недели, и не нахожу ошибок, но он с первого раза обваливает её. Вот эта программа – одна из немногих, где он не смог найти баг, поэтому если вы сможете здесь найти баг, то вы действительно ОЧЕНЬ КРУТОЙ специалист. Но только учтите, что я для лаконичности вырезал блок «защиты от дурака», поэтому в скрипте не должно быть ошибок.

                ExpandedWrap disabled
                            //Программа Popup.cpp
                            //Эта программа создает меню
                            //с неограниченным числом подпунктов
                   
                            //Составлена Серохвостовым Антоном 5.11.04
                            //Последняя доработка 18.11.04
                   
                  #include <conio.h>
                  #include <stdio.h>
                  #include <stdlib.h>
                  #include <string.h>
                  #include <iostream.h>
                  #define Exit 4                    //Хэндл пункта для выхода из меню
                   
                  char Figures[] = "0123456789",    //Допустимые цифры
                       fMnu[]    = "c:\\menu.mnu";  //Файл с меню//
                   
                  typedef enum BOOL {FALSE = 0, TRUE = 1};
                  //******Структура меню******/
                  typedef struct TMenu
                  {
                    struct TMenu *pFirst, //Указатель на первый элемент
                             *pLast,  //Указатель на последний элемент
                             *pNext,  //Указатель на следующий пункт
                             *pNxtL,  //Указатель на подменю
                             *pPrev,  //Указатель на предыдущий пункт
                             *pPrvL;  //Указатель на родительское меню
                    int  idItem;      //Хэндл пункта
                    char nmItem[20];  //Имя пункта
                    int  n;       //Номер попорядку
                  } PMenu;
                   
                  //******Информация о текущем элементе******//
                  typedef struct TItem
                  {
                    int  idItem;      //Хэндл пункта
                    char nmItem[20];  //Имя пункта
                  } ;
                  /*******************************/
                   
                  FILE *tMnu;     //Файл с меню
                  TItem Item;     //Очередной элемент файла
                  PMenu *pMnu;        //Меню в памяти
                  /*******************************/
                   
                  char* ReadStrFromFile(FILE *tMnu)
                  {
                    int  ch;
                    char s[80];
                    strcpy(s, "");
                    ch = fgetc(tMnu);
                    while (ch != 0xD && ch != 0xA && !feof(tMnu))
                    {
                      s[strlen(s)+1] = 0;
                      s[strlen(s)] = ch;
                      ch = fgetc(tMnu);
                    }
                    return s;
                  }
                   
                  void ReadNextItem(TItem &Item)
                   /*Читает очередной элемент меню из файла*/
                  {
                    char *s = new char,   //Очередная строка файла
                         id[10],      //Хэндл очередного пункта в строковом виде
                         tmp[1];      //Для совместимости символов и строк
                    unsigned char i;  //Номер символа в s
                   
                    s = ReadStrFromFile(tMnu);
                    strcpy(id, ""); strcpy(Item.nmItem, ""); i = 0;
                   /***Пропустить пробелы перед началом имени***/
                    while (s[i] == ' ') i++;
                   /***Выделить имя пункта***/
                    while (s[i] != ',' && i < strlen(s))
                    {
                      tmp[0] = s[i]; tmp[1] = 0;
                      strcat(Item.nmItem, tmp);
                      i++;
                    } //while s[i] ...
                   /***Удалить пробелы из конца имени***/
                    while (Item.nmItem[strlen(Item.nmItem)-1] == ' ')
                      Item.nmItem[strlen(Item.nmItem)-1] = 0;
                    if (Item.nmItem[strlen(Item.nmItem)-1] == '>')
                      Item.idItem = 0;
                    else
                    {
                   /***Пропустить пробелы перед хэндлом***/
                      i++;
                      while (s[i] == ' ') i++;
                   /***Выделить хэндл***/
                      while (strchr(Figures, s[i]) && i < strlen(s))
                      {
                        tmp[0] = s[i];
                        strcat(id, tmp);
                        i++;
                      } //while Pos( ...
                   /***Удалить пробелы из конца хэндла***/
                      while (id[strlen(id)-1] == ' ')
                        id[strlen(id)-1] = 0;
                   /***Сохранить хэндл в числовом виде***/
                        Item.idItem = atoi(id);
                    } //else ...
                  } //ReadNextItem
                  /*******************************/
                   
                  void CreateMenu(PMenu* &pMnu)
                   /*Создает меню (место для первого элемента)*/
                  {
                    pMnu =     new PMenu;
                    pMnu->pPrvL = NULL;
                    pMnu->pFirst = pMnu;
                  } //CreateMenu
                  /*******************************/
                   
                  void AddSub(PMenu* &pMnu)
                   /*Добавляет подменю*/
                  {
                    pMnu->pNxtL = new PMenu;
                    pMnu->pNxtL->pPrvL = pMnu;
                    pMnu = pMnu->pNxtL;
                    pMnu->pFirst = pMnu;
                    pMnu->pNxtL = NULL;
                  } //AddSub
                  /*******************************/
                   
                  void AddItm(PMenu* &pMnu)
                   /*Добавляет пункт*/
                  {
                    pMnu->pNext = new PMenu;
                    pMnu->pNext->pPrev = pMnu;
                    pMnu = pMnu->pNext;
                    pMnu->pPrvL = pMnu->pPrev->pPrvL;
                    pMnu->pFirst = pMnu->pPrev->pFirst;
                    pMnu->pNxtL = NULL;
                  } //AddItm
                  /*******************************/
                   
                  void MakeKolco(PMenu* &pMnu)
                   /*Обработка '<'*/
                  {
                    pMnu->pLast = pMnu;
                   /***Заполнить указатели на конец***/
                    while (pMnu != pMnu->pFirst)
                    {
                      pMnu = pMnu->pPrev;
                      pMnu->pLast = pMnu->pNext->pLast;
                    }
                   /***Сформировать "кольцо"***/
                    pMnu->pLast->pNext = pMnu->pFirst;
                    pMnu->pFirst->pPrev = pMnu->pLast;
                  } //MakeKolco
                  /*******************************/
                   
                  void AddItem(PMenu* &pMnu, TItem Item)
                   /*Добавляет элемент меню Item в позицию pMnu*/
                  {
                    if (pMnu == NULL && strcmp(Item.nmItem, "<"))
                    {
                      CreateMenu(pMnu);
                      strcpy(pMnu->nmItem, Item.nmItem);
                      pMnu->idItem = Item.idItem;
                      pMnu->pNxtL = NULL;
                      pMnu->n = 1;
                    } //if pMnu ...
                    else
                   /***Обработать служебные символы***/
                    if (!strcmp(Item.nmItem, "<")) //Стоит переход на верхний уровень?
                    {                              //Да - скоректировать меню и перейти на него
                      MakeKolco(pMnu);
                      pMnu = pMnu->pPrvL;
                    } //if pMnu-> ...
                    else if (!strcmp(Item.nmItem, "!"))
                       MakeKolco(pMnu);
                         else
                   /***Создать элемент меню***/
                         {
                       if (pMnu->idItem == 0 && pMnu->pNxtL == NULL)
                       {
                         AddSub(pMnu);        //Да - добавляем подменю
                         pMnu->n = 1;
                       } //if (pMnu-> ...
                       else
                       {
                         AddItm(pMnu);        //Нет - добавляем пункт
                         pMnu->n = pMnu->pPrev->n+1;
                       } //else ...
                       strcpy(pMnu->nmItem, Item.nmItem);
                       pMnu->idItem = Item.idItem;
                         } //else ...
                  } //AddItem
                  /*******************************/
                   
                  void BuildMenuInMemory(char* fMnu, PMenu* &pMnu)
                   /*Загружает меню из файла в память*/
                  {
                    tMnu = fopen(fMnu, "rt");
                    pMnu = NULL;
                    do {
                         ReadNextItem(Item);
                         AddItem(pMnu, Item);
                       } while (strcmp(Item.nmItem, "!"));
                    fclose(tMnu);
                  } //BuildMenuInMemory
                  /*******************************/
                   
                  void ShowMenu(PMenu* pMnu)
                   /*Отображает меню*/
                  {
                    pMnu = pMnu->pFirst;
                    do {
                         cout<<" "<<pMnu->nmItem<<"\n";
                         pMnu = pMnu->pNext;
                       } while (pMnu != pMnu->pFirst);
                  } //ShowMenu
                  /*******************************/
                   
                  void ReadItem(PMenu* &pMnu, TItem &Item, BOOL &UpLevel)
                   /*Позволяет выбрать пункт меню
                     UpLevel = TRUE - верхний уровень отсутствует*/
                  {
                    char ch;      //Для выбора пункта меню
                    unsigned char curPos; //Номер текущей строки меню
                    BOOL Selected;        //Флаг выбора пункта
                   
                    clrscr();
                    Selected = FALSE; UpLevel = FALSE;
                    curPos = 1;
                    ShowMenu(pMnu);
                    gotoxy(1, pMnu->n); cout<<"*";
                    do {
                        clrscr();
                        ShowMenu(pMnu);
                        gotoxy(1, curPos);  cout<<" ";
                        gotoxy(1, pMnu->n); cout<<"*";
                        curPos = pMnu->n;
                        ch = getch();
                        switch (ch)
                        {
                      case 71:     //Home, PgUp, Left
                      case 73:
                      case 75:
                        pMnu = pMnu->pFirst;
                        break;
                      case 77:     //Right, End, PgDn
                      case 79:
                      case 81:
                        pMnu = pMnu->pLast;
                        break;
                      case 72:     //Up
                        pMnu = pMnu->pPrev;
                        break;
                      case 80:     //Down
                        pMnu = pMnu->pNext;
                        break;
                      case 13:     //Enter
                        if (!pMnu->idItem)
                          pMnu = pMnu->pNxtL;
                        else
                        {
                          strcpy(Item.nmItem, pMnu->nmItem);
                          Item.idItem = pMnu->idItem;
                          Selected = TRUE;
                        } //else ...
                        break;
                      case 27:     //Esc
                        if (pMnu->pPrvL != NULL)
                          pMnu = pMnu->pPrvL;
                        else
                          UpLevel = TRUE;
                        break;
                        } //case ch ...
                       } while (!Selected && !UpLevel);
                  } //ReadItem
                  /*******************************/
                   
                  void main()
                  {
                    BOOL ex;  //Флаг отсутствия верхнего уровня
                    BuildMenuInMemory(fMnu, pMnu);
                    do {
                         clrscr();
                         ReadItem(pMnu, Item, ex);
                         clrscr();
                         if (!ex) //Есть верхний уровень?
                         {    //Да - перейти на него
                       cout<<"idItem = "<<Item.idItem<<"\n";
                       cout<<"Нажмите любую клавишу...\n";
                       getch();
                         }
                       } while (Item.idItem != Exit && !ex);
                  } //PopupMenu
                   
                  /*
                  Инструкция к скриптовому языку меню. Каждая строка - это
                  1) либо пункт меню, 2) либо подменю, 3) либо спецсимвол.
                  В каждой строке должно быть только одно имя - не больше и не меньше.
                  В качестве разделителей используются только пробелы.
                  Их для удобства можно ставить хоть где.
                   
                  СИНТАКСИС "ПУНКТА МЕНЮ": "nnn, ddd", где nnn - имя пункта меню
                  без пробелов и без служебных символов,
                  ddd - уникальный числовой идентификатор пункта меню.
                   
                  СИНТАКСИС "ПОДМЕНЮ": "nnn> : <". Nnn - уникальный числовой идентификатор
                  пункта меню. Символ ">" - указание на то, что это не пункт меню,
                  а подменю. ":" - это, расположенные в отдельных строках, элементы подменю.
                  "<" - указатель на конец подменю.
                   
                  СИНТАКСИС "СПЕЦСИМВОЛОВ". Один спецсимвол (конец подменю) уже был рассмотрен.
                  Остался последний - "!". Это символ конца меню, который обязательно
                  должен быть в конце файла.
                   
                  ПРИМЕР файйла menu.mnu - прилагается.
                  */


                Но опять же, без грамотного «зачем», даже такой красивый код и яйца выеденного не стоит.

                Прикреплённый файлПрикреплённый файлpopup.zip (3,42 Кбайт, скачиваний: 136)
                Подпись выключена.
                  Цитата Серохвостов Антон @
                  ExpandedWrap disabled
                    char fMnu[]    = "c:\\menu.mnu";  //Файл с меню//

                  высочайший профессионализм топикстартера виден в этой строчке
                  Цитата usrjava @
                  Технологии в основе, которых по-сути лежит javascript в расширенном так сказать виде
                    Мне больше понравилось MakeKolco.
                    Сюда бы еще того методолога, помешанного на scrum и ООП, и много попкорна.
                      Цитата Kray74 @
                      Мне больше понравилось MakeKolco.

                      да много прекрасного, например
                      Цитата
                      СИНТАКСИС "ПУНКТА МЕНЮ": "nnn, ddd", где nnn - имя пункта меню
                      без пробелов и без служебных символов,
                      ddd - уникальный числовой идентификатор пункта меню.

                      с такими входными данными постановка задачи восхищает.

                      Добавлено
                      кстати, а в с++ нет naming conversion с указанием по формированию названий методов? дескать с строчной буквы и все такое
                      Цитата usrjava @
                      Технологии в основе, которых по-сути лежит javascript в расширенном так сказать виде
                        Цитата Серохвостов Антон @
                        ExpandedWrap disabled
                          while (ch != 0xD && ch != 0xA && !feof(tMnu))
                            {
                              s[strlen(s)+1] = 0;
                              s[strlen(s)] = ch;
                              ch = fgetc(tMnu);
                            }

                        Спасибо, но такого виртуозного программирования нам не надо :hunter:
                          Скока раз ходил по роще и не знал, что это - баньяновый лес. Thanks for the tip!
                          Мои религиозные убеждения не позволяют мне комментировать код.
                          Моё мировоззренье таково: в программе комментария ни одного!
                            я догадался, программа приведена с целью показать, что программеры не пишут виртуозных программ.
                            так что если бы, не дай бог, мы приняли автора за программера, то приложенная программа как раз послужила бы отличным подтверждением. увы, план не сработал.
                            Цитата usrjava @
                            Технологии в основе, которых по-сути лежит javascript в расширенном так сказать виде
                              Детские выпады, продиктованные завистью и неподтверждённые более красивыми решениями, чем предложенные мной, я воспринимаю – банальным ламерством. Если вы крутые профи – покажите себя в деле. Раскритиковать можно любой код. И если это делается в конструктивном ключе – честь и хвала. Я с радостью перейму ваш опыт. Но если вам жалко им делиться или вам интересно только плеваться, тогда попрошу вас больше не спамить в моём блоге.

                              PS. Данное сообщение относится не ко всем участникам, отписавшимся в моём блоге, но пальцем я тыкать не буду – пусть каждый разговаривает со своей совестью сам.
                              Подпись выключена.
                                Этот тред - не блог и не подчиняется блоговым правилам.
                                Мои религиозные убеждения не позволяют мне комментировать код.
                                Моё мировоззренье таково: в программе комментария ни одного!
                                  конечно мы просто завидуем, это же очевидно. но на всякий случай уточно, а почему ветер дует вы не в курсе, случаем?
                                  зы а вообще это же прекрасно, навалить кучу и требовать критики в конструктивном ключе. хотя даже при этом мой конкруктивный комментарий про захардкоженые пути вы проигнорировали.
                                  Цитата usrjava @
                                  Технологии в основе, которых по-сути лежит javascript в расширенном так сказать виде
                                  0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                  0 пользователей:
                                  Страницы: (17) [1] 2 3 ...  16 17 все


                                  Рейтинг@Mail.ru
                                  [ Script Execution time: 0,2519 ]   [ 20 queries used ]   [ Generated: 14.10.19, 01:01 GMT ]