HOME ПРИМЕРЫ THANKS НОВИЧКАМ ДОКИ LINKS JavaScript Mail


 
В этот день много лет назад...
6 декабря. В 1917 году (108 лет назад) - В гавани Бедфорд, близ города Галифакс (Канада) из-за нарушения правил навигации в узком форватере сталкиваются 2 сухогруза: норвежский "Имо" и французский "Монблан", на борту которого находятся 2300 тонн пикриновой кислоты, 200 тонн тринитротолуола, 35 тонн бензола, 10 тонн порохового хлопка. :Ударная волна уничтожает все строения на обоих берегах пролива. Образовавшаяся от взрыва придонная волна выбрасывает на берег несколько судов. По официальным данным, погибли 1963 человека, более 2000 пропали без вести, около 9000 были ранены, 500 - лишились зрения, 25 тысяч - остались без крова. По другим сведениям погибших было более 3200.
 
 

Turbo Pascal Examples. Синтаксический анализатор.

Графика:
Построение графика функции
Прыгающий по экрану мячик.
Качание маятника.
Вложенные цветные круги.
Броуновское движение. Использование объектов.
Матрицы и массивы:
Сортировка элементов массива.
Удаление одинаковых элементов.
Простой пример на поворот матрицы.
Сортировка методом Шелла. +функции измерения временных интервалов.
Проверка выпуклости многоугольника.
Перемоножение матриц
Вычисление определителя матрицы. Рекурсия.
Нахождение обратной матрицы.
Задача об автостоянке.
Рекурсия. Подземелье сокровищ.
Численные методы:
Задачка на определение угла между стрелками часов.
Проверка на принадлежность точки многоугольнику.
Нахождение точки пересечения двух отрезков на плоскости.
Сортировка методом Шелла. +функции измерения временных интервалов.
Сортировка методом "пузырька". Пример на динамические структуры данных. Связанные списки.
Нахождение корня функции методом половинного деления.
Вычисление арккосинуса
Нахождение суммы цифр натурального числа.
Работа с фалами:
Рекурсивное сканирование директорий.
Работа со строками:
Работа со словами в предложении с разделителями.
Простейший синтаксический анализатор для распознавания и вычисления многчлена.
Синтаксический анализатор для распознавания и вычисления многчлена.
Работа со строками: смена кодировки, удаление тегов из HTML текста, обработка
Переименование файлов из кириллицы в латиницу.
Выдача контекстной подсказки.
Частотный словарь символов.
Подсчет повторяющихся символов в строке.
Ссылочные переменные:
Моделирование стека.
Пасьянс "Косынка".
Игры:
Пасьянс "Косынка".
Игра "Питон"
Игра "Анацефал". Пример использования объектов.
Игра "Минное поле"
Большие проекты:
Электронная картотека (без исходника)


 
Синтаксический анализатор.


В прошлом примере был разобран случай простейшего синтаксического анализатора реализующий разбор многочлена от одной переменной х представляющего конечную сумму элементов вида a*x**n. В приведенном ниже примере сделан шаг к усложнению функции, которая может быть преобразована из строкового представления в вид пригодный для выполнения вычислений при заданном значинии переменной х. Если обозначить за М многочлен одной переменной рассмотренный в прошлом примере, то здесь допустимы любые арифметические выражения с многочленами заключенными в скобки. То есть может быть вычислено выражение вида:

В = M0*(M1 +(M2)*(M3))/(M4 + (M6)/(M7))

Алгоритм вычислений таков.

  • Разбиваем исходное строковое выражение на части, каждая из которых содержит строковое представление функции х вида "многочлен". Записываем эти части в строковый массив.
  • Для вычисления каждой из таких частей используем объект mnogochlen из прошлого примера.

    Столь туманное объяснение пропробую прояснить на конкретном примере.

    Пусть есть функция

    F(x) = (x**3-5)*(x+5*(x-1))/(3*x+4)*x

    Обозначим:

    Часть1[x] = x**3-5;
    Часть2[x] = x-1;
    Часть3[x] = 3*x+4;

    Тогда исходное выражение можно записать в виде:

    F(x) = Часть1[x]*(x+5*Часть2[x])/Часть3[x]*x

    Можно еще обозначить:

    Часть4[x] = x+5*Часть2[x];

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

    F(x) = Часть1[x]*Часть4[x]/Часть3[x]*x

    Как видно, чтобы вычислить первые три части, можно передать строковое представление фукции объекту mnogochlen и воспользовшись его функцией value вычислить значения частей для каждого конкретного х.

    А после того как вычислены первые три части в выражении для части 4 можно подставить числовое значение выраженное в виде строки и полученную строку опять прогнать через объект многочлен. Например для х=3 часть 4 будет иметь вид: "x+5*2".

    И последним для этого примера шагом будет вычисление всей функции, которая для каждого конкретного х при вычисленных частях будет представлять строковое представление вида многочлен. Например для x=2

    Часть1[x] = 3;
    Часть2[x] = 1;
    Часть3[x] = 10;
    Часть4[x] = 7;
    И F(x) = "3*7/10*x"

    Как видно выражение для части 4 и для самой функции уже не попадают под прямое определение многочлена. Поэтому сам объект mnogochlen был также модифицирован так, чтобы он мог принимать соответствующие значения. В данном примере он способен распознавать функции представляющие из себя конечную сумму элементов, каждый из которых представляет из себя конечное произведение констант и переменныех х. Кроме того, допустима операция деления (косая черта) и возведение в степень для последнего сомножителя.

    Теперь как работает алгоритм распознавания частей. Написана функция setExpressionParts, которая возвращает обработанную (часть) строки. "Обработанную" означает "не содержащую парных (отрывающей/закрывающей) скобок внутри которых не содержится больше никаких скобок". Функция рекурсивная то есть обращается сама к себе при необходимости. Алгоритм таков. Берем первыую открывающую скобку и ищем закрывающую к ней. Если такие скобки найдены, то записываем эту строку в массив под очередным индексом k, а на место найденного записываем строку @@k#. Если же до закрывающей нашлась еще одна открывающая, то отстаток строки от второй открывающей до конца передаем в качестве параметра той же функции.


    function expression.setExpressionParts(s:string;var partNo:byte):string;
    { Функция разбирает выражение заданное строкой s на части. }
    var openBracketK1,openBracketK2,closeBracketK,t:byte;
        st,internalPart,partNoSt:string;
      begin
      openBracketK1 := Pos('(',s);
      while (openBracketK1 <>0) do
        begin
        { временно убираем все закрывающие скобки, которые стоят до открывающей }
        t:=Pos(')',s);
        while (t<openBracketK1) do
          begin
          s:=strReplaceFirst(')',']',s);
          t:=Pos(')',s);
          end;
        st := copy(s,openBracketK1+1,255); { часть исходной строки от
                                   первой открывающей скобки до конца }

        openBracketK2 := Pos('(',st);  { следующая открывающая скобка }
        if (openBracketK2>0) then
          openBracketK2:=openBracketK2 + openBracketK1; { позиция второй
                               открывающей скобки в исходной строке s }

        closeBracketK := Pos(')',s);
        if ((openBracketK2>closeBracketK)
          or (openBracketK2=0))
        then { вторая открывающая скобка стоит за первой закрывающей
               или нет больше открывающих скобок }

          begin  { найдена отдельная часть в скобках. Выделяем ее и
                   записываем в массив частей. На ее место в исходной
                   строке записываем указатель на данную часть в виде
                   строки @@N#, где N целое число. Пример @@4#. }

          inc(partNo);
          Str(partNo,partNoSt);
          internalPart:=copy(s,openBracketK1+1,closeBracketK-openBracketK1-1);
          s:=strReplaceFirst('('+internalPart+')','@@'+partNoSt+'#',s);
          expS[partNo]:=internalPart;
          end
        else { первая скобка не закрылась, а вторая уже открылась.
             Значит внутри есть еще одна часть, подлежащая обработке }

          begin
          st:=copy(s,openBracketK2,255); { кусок от второй скобки до конца }
          st:=setExpressionParts(st,partNo); { обрабатываем этот кусок }
          s:=copy(s,1,openBracketK2-1)+st;  { соединяем обработанный
                                              кусок с началом строки }

          end;
        openBracketK1 := Pos('(',s);
        end; { while }
        s:=strReplace(']',')',s);
        setExpressionParts:=s;
      end;


  • Например, для функции F(x) рассмотренной выше будут выполнены следующие шаги:

    Номер обращения
    (глубина рекурсии)
    Строка s Найденное
    число
    частей
    Массив expS
    1 (x**3-5)*(x+5*(x-1))/(3*x+4)*x 0 ()
    1 @@1#*(x+5*(x-1))/(3*x+4)*x 1 ('x**3-5')
    2 (x-1))/(3*x+4)*x 1 ('x**3-5')
    2 @@2#)/(3*x+4)*x 2 ('x**3-5','x-1')
    2 @@2#]/@@3#*x 3 ('x**3-5','x-1','3*x+4')
    1 @@1#*(x+5*@@2#)/@@3#*x 3 ('x**3-5','x-1','3*x+4')
    1 @@1#*@@4#/@@3#*x 4 ('x**3-5','x-1','3*x+4','x+5*@@2#')



    Для работы со строками написаны вспомогательные процедуры:
  • explode - Разбить строку s на кусочки заданной подстрокой sep и записать результат в массив
  • strReplace - контекстный поиск и замена в строке
  • strReplaceFirst - контекстный поиск и замена первого вхождения подстроки в строке
  • strToInt и strToReal - преобразовать строку в число
    Также написана процедура xPowerN вычисления степенной функции x N
    Смотри также комментарии в самой программе.


  • {$M 64384,0,655360}
    const maxN = 20;
          arrStrN = 40;
    type
       component = object
       { a component of mnogochlen: an*x**n}
         a:real;
         n:integer;
         function value(x:real):real;
         procedure setComponent(aVal:real;nVal:integer);
         end;
       mnogochlen = object
         n:byte;
         c:array[1..maxN] of component;
         procedure init(s1:string);
         function value(x:real):real;
         end;
       expression = object
         n: byte;     { число частей }
         expS,        { массив частей (строки) }
         expSwork: array[1..maxN] of string; { рабочий массив частей для расчетов }
         expV: array[1..maxN] of real;   { числовой массив частей }
         procedure init(s:string);
         function setExpressionParts(s:string;var partNo:byte):string;
         function value(x:real):real;
         end;
       str_arr = array [1..arrStrN] of string;
    {*****************************************}
    function xPowerN(x:real;n:integer):real;
    { Возвращает х в степени n }
    const eps = 1e-8; { полагаем число нулем, если меньше eps }
    var z:shortint;
        r:real;
      begin
      if n=0 then xPowerN := 1 { любое число в нулевой степени = 1 }
      else if abs(x)<eps
        then xPowerN := 0 { 0 в любой положительной степени = 0 }
        else
          begin
          r := exp(n*ln(abs(x)));
          z := 1; if (x<0) then z := -1;
          if n mod 2 = 0
            then xPowerN := r      { четная степень всегда положительна }
            else xPowerN := z * r  { нечетная сохраняет знак }
          end;
      end;
    {*****************************************}
    function component.value(x:real):real;
    { Вычисление значения одной компоненты при заданном х }
      begin
      value:=a*xPowerN(x,n);
      end;
    procedure component.setComponent(aVal:real;nVal:integer);
      begin
      a:=aVal;
      n:=nVal;
      end;
    function mnogochlen.value(x:real):real;
    { Вычисление значения многочлена при заданном х }
    var s:real;
        i:byte;
      begin
      s:=0;
      for i:=1 to n do
        s:=s+c[i].value(x);
      value:=s;
      end;

    {*****************************************}
    { String functions }
    function explode(sep,s:string;var a:str_arr):integer;
    { Разбить строку s на кусочки подстрокой sep и записать результат
      в строковый массив str_arr. Сама функция вернет число кусочков
      Например:
        explode('mp','This is an simple example',a)
          вернет число 3, и строки в массиве a:
          a[1]='This is an si'; a[2]='le exa'; a[3]='le';
        explode(' ','This is an simple example',a) - вернет 5 и отдельные
          слова в массиве a 'This','is','an','simple','example' }

    var L:byte absolute s;
        i,n,k,d:byte;
      begin
      n:=0;
      for i:=1 to arrStrN do
        a[i]:='';
      k:=Pos(sep,s);
      d:=length(sep)-1;
      while (L*k>0) do
        begin
        inc(n);
        a[n]:=copy(s,1,k-1);
        delete(s,1,k+d);
        k:=Pos(sep,s);
        end;
      inc(n);
      a[n]:=s;
      explode:=n;
      end;
    function strReplace(findWhat,replaceTo,inString:string):string;
    { В строке inString заменить все вхождения подстроки findWhat
      на подстроку replaceTo }

    var sa:str_arr;
        n,i:byte;
        res:string;
      begin
      n:=explode(findWhat,inString,sa);
      res:=sa[1];
      for i:=2 to n do
        res:=res+replaceTo+sa[i];
      strReplace:=res;
      end;
    function strReplaceFirst(findWhat,replaceTo,inString:string):string;
    var k:byte;
        s:string;
      begin
      k:=Pos(findWhat,inString);
      if (k>0) then
        begin
        Delete(inString,k,length(findWhat));
        Insert(replaceTo,inString,k);
        end;
      strReplaceFirst:=inString;
      end;
    function strReplaceN(findWhat,replaceTo,inString:string;
                            nFirstTimes:byte):string;
    { В строке inString заменить nFirstTimes первых вхождений
      подстроки findWhat на подстроку replaceTo }

    var sa:str_arr;
        n,i:byte;
        res:string;
      begin
      n:=explode(findWhat,inString,sa);
      res:=sa[1];
      for i:=2 to n do
        if (i-1<=nFirstTimes)
          then res:=res+replaceTo+sa[i]
          else res:=res+findWhat+sa[i];
      strReplaceN:=res;
      end;
    function strToInt(s:string):integer;
    { Преобразовать строку к целому числу. }
    var i, code: Integer;
      begin
      val(s, i, code);
      if code <> 0 then
        begin
        WriteLn('strToInt. Error at position: ', code,
          ' in line ', s, '. Program halted');
        halt
        end
      else
        strToInt := i;
      end;
    function strToReal(s:string):real;
    { Преобразовать строку к вещественному числу. }
    var code: Integer;
        r: real;
      begin
      s:=strReplaceFirst('EM','E-',s);
      s:=strReplaceFirst('EP','E+',s);
      val(s, r, code);
      if code <> 0 then
        begin
        WriteLn('strToReal. Error at position: ', code,
          ' in line ', s, '. Program halted');
        halt
        end
      else
        strToReal := r;
      end;
    function realToString(r:real):string;
    var s: string[20];
      begin
      Str(r:14:8, s);
      realToString:=s;
      end;
    {*****************************************}


    procedure mnogochlen.init(s1:string);
    { Данная процедура принимает в качестве параметра строковое
      представление функции переменной х, представляющей из себя
      многочлен с некоторыми расширениями. После выполнения
      параметры объекта mnogochlen оказыаются заполненными и
      можно вызвать метод value(x) для вычисления значения
      функции при конкретном х }


    var i,j,j2,nParts,t1,t2,t3:byte;
        b,sc,dl:str_arr;
        xPower,s2:string;
        xPowerInt,minus:integer;
      begin
      for i:=1 to maxN do
        c[i].setComponent(0,0);

      s1:=strReplace(' ','',s1); { Убираем все пробелы }

      s1:=strReplace('E+','EP',s1); { Временно заменили символы плюс и
                                      минус выражающие степень
                                      цифрового занчения вещественных
                                      переменных 1.00E+01 }

      s1:=strReplace('E-','EM',s1);

      s1:=strReplace('*-','*M',s1); { данная временная замена нужна для
             предотвращения последующей замены этого минуса на +- и
             добавления еще одного слагаемого. Пример 5+x*-3 }

      s1:=strReplace('/-','/M',s1); { или 5*8/-x }

      { Далее мы будем разбивать строку на части содержащие слагаемые.
        Заменим знаки '-' на '+-', чтобы заменить вычитание на прибавление
        отрицательного слагаемого. Первый минус, если он есть, заменять не
        надо, иначе появится "пустое" слагаемое }

      if (s1[1]='-')
        then s1:='-'+strReplace('-','+-',copy(s1,2,255))
        else s1:=strReplace('-','+-',s1);
      { возвращаем обратно знаки *- и /- }
      s1:=strReplace('*M','*-',s1);
      s1:=strReplace('/M','/-',s1);

      nParts:=explode('+',s1,b);
      { Разбили на слагаемые }
      n:=nParts;
      for i:=1 to nParts do
        begin
        { Здесь возможна ситуация, когда множитель в слагаемом
          появляется с отрицательным знаком: 2*-x*4*-x. Выносим
          все минусы множителей вперед }

        s2:=b[i];
        j:=pos('-',s2);
        minus:=1;
        while j>0 do
          begin
          minus:=-minus;
          Delete(s2,j,1);
          j:=pos('-',s2);
          end;
        if (minus<0) then s2:='-1*'+s2;

        s2:=strReplace('**','^^',s2);

        { Разбиваем на множители }
        t2:=explode('*',s2,sc);

        { Проверим есть ли в каком-либо из множителей знак деления }
        j:=1;
        while (j<=t2) do
        if (pos('/',sc[j])>0) then
          begin
          t3:=explode('/',sc[j],dl);
          { В данном множителе sc[j] найдено t3-1 делителей и одно делимое.
            Каждый делитель запишем как множитель в минус первой степени.
            Это значит у нас в исходном массиве прибавилось
            t3-1 множителей.
            Сдвинем оставшиеся в массиве sc справа множители на
            t3-1 позиции вправо }

          for j2 := t2 downto j+1 do
            sc[j2+t3-1]:=sc[j2];
          { Теперь на освободившееся после сдвига места вписываем
            найденные делители }

          sc[j]:=dl[1]; { делимое }
          for j2:=2 to t3 do
            sc[j+j2-1]:=dl[j2]+'^^-1'; { делители }
          j:=j+t3; t2:=t2+t3-1; { увеличили количество множителей t2 }
          end
        else
          j:=j+1;

        c[i].setComponent(1,0);
        for j:=1 to t2 do
          begin
          { проверяем имеет ли множитель показатель степени }
          t1:=explode('^^',sc[j],dl);
          if (dl[2]='')
            then xPowerInt:=0
            else xPowerInt:=strToInt(dl[2]);
          if t1<2
          then { показателя степени нет. Значит данный множитель либо 'x'
                 либо константа }

            if (sc[j]='x')
              then inc(c[i].n)
              else c[i].a := c[i].a * strToReal(sc[j])
          else { явно задана степень. Данный множитель либо степень х
                 либо степень константы }

            if (dl[1]='x')
              then inc(c[i].n,xPowerInt)
              else c[i].a := c[i].a * xPowerN(strToReal(dl[1]),xPowerInt)
          end;
        end;
      end;

    {*****************************************}


    function expression.setExpressionParts(s:string;var partNo:byte):string;
    { Функция разбирает выражение заданное строкой s на части. }
    var openBracketK1,openBracketK2,closeBracketK,t:byte;
        st,internalPart,partNoSt:string;
      begin
      openBracketK1 := Pos('(',s);
      while (openBracketK1 <>0) do
        begin
        { временно убираем все закрывающие скобки, которые стоят до открывающей }
        t:=Pos(')',s);
        while (t<openBracketK1) do
          begin
          s:=strReplaceFirst(')',']',s);
          t:=Pos(')',s);
          end;
        st := copy(s,openBracketK1+1,255); { часть исходной строки от первой
                                             открывающей скобки до конца }

        openBracketK2 := Pos('(',st);  { следующая открывающая скобка }
        if (openBracketK2>0) then
          openBracketK2:=openBracketK2 + openBracketK1; { позиция второй откры
            вающей скобки в исходной строке s }

        closeBracketK := Pos(')',s);
        if ((openBracketK2>closeBracketK)
          or (openBracketK2=0))
        then { вторая открывающая скобка стоит за первой закрывающей
               или нет больше открывающих скобок }

          begin  { найдена отдельная часть в скобках. Выделяем ее и
                   записываем в массив частей. На ее место в исходной строке
                   записываем указатель на данную часть в виде строки @@N#,
                   где N целое число. Пример @@4#. }

          inc(partNo);
          Str(partNo,partNoSt);
          internalPart:=copy(s,openBracketK1+1,closeBracketK-openBracketK1-1);
          s:=strReplaceFirst('('+internalPart+')','@@'+partNoSt+'#',s);
          expS[partNo]:=internalPart;
          end
        else { первая скобка не закрылась, а вторая уже открылась. Значит внутри
               есть еще одна часть, подлежащая обработке }

          begin
          st:=copy(s,openBracketK2,255); { кусок от второй скобки до конца }
          st:=setExpressionParts(st,partNo); { обрабатываем этот кусок }
          s:=copy(s,1,openBracketK2-1)+st;  { соединяем обработанный
            кусок с началом строки }

          end;
        openBracketK1 := Pos('(',s);
        end; { while }
        s:=strReplace(']',')',s);
        setExpressionParts:=s;
      end;
    procedure expression.init(s:string);
    var i:byte;
      begin
      n := 0;
      for i:=1 to maxN do
        begin expS[i]:=''; expV[i]:=0; end;
      s := setExpressionParts(s,n);
      inc(n);
      expS[n]:=s;
      end;

    function expression.value(x:real):real;
    var mng:mnogochlen;
        i,k,t:byte;
        r:real;
        compNo:string;
      begin
      for i:=1 to n do
        begin
        expSwork[i]:=expS[i];
        k := pos('@@',expSwork[i]);
        while (k>0) do
          begin
          t:=pos('#',expSwork[i]);
          compNo:=copy(expSwork[i],k+2,t-k-2);
          r:=expV[strToInt(compNo)];
          expSwork[i]:=strReplaceFirst('@@'+compNo+'#',realToString(r),expSwork[i]);
          k := pos('@@',expSwork[i]);
          end;
        mng.init(expSwork[i]);
        expV[i]:=mng.value(x);
        end;
      value := expV[n];
      end;

    var s1,xPower:string;
        nParts:integer;
        mn:mnogochlen;
        ex:expression;
        i,part:byte;
        x,y:real;

    function F(x:real):real;
    { Для проверки }
      begin
      F:=3+5*4*3/2/4*12/x/x*x*2/2/3+8*2.1*3*x+4.54*x-x*3*x*x*x+6.5*x*x-2.6*x*x*x*x;
      end;

    function F2(x:real):real;
    { Для проверки }
      begin
      F2:=(x*x*x-5)*(x+5*(x-1))/(3*x+4)*x;
      end;


    begin { MAIN }
    { F(x) }
    for i:=1 to 43 do writeln;
    s1:='3+5*4*3/2/4*12/x/x*x*2/2/3+8*2.1*3*x+4.54*x-x*3*x**3+6.5*x**2-2.6*x**4';
    writeln('F(x)=',s1);
    mn.init(s1);
    randomize;
    x:=5-random(1000)/100;
    writeln(' x=',x:8:3,' F(x)=',mn.value(x):8:3, ' Check: ',F(x):8:3);

    s1:='(x**3-5)*(x+5*(x-1))/(3*x+4)*x';
    writeln('F2(x)=',s1);
    ex.init(s1);
    for i:=1 to 15 do
      begin
      x:=5-random(1000)/100;
      y:=ex.value(x);
      writeln(' x=',x:8:3,' F2(x)=',y:8:3, ' Check: ',F2(x):8:3);
      end;
    end.




     

     

     

     

     

     

     


    HOME