LINUX.ORG.RU

Дифференцирование функций на паскале


0

0

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

На скрине вы видете редактор GNU/Emacs версии 22.0.50.1 (к сожалению, кроме убогого pascal-mode никаких инструментов для паскаля в нем нет. К счастью, gdb и gud теперь поддерживают отладку паскалевского кода в полном обьеме). В нем можете лицезреть код, который дифференцирует произведение и частное. Если у кого-то есть предложения о том, как бы код упростить -- буду рад выслушать.

Все это запущено в kde-3.4 под xorg-x11-6.8.2 в gentoo linux.

ЗЫ. Емакс собран без gtk для улучшения быстродействия.

>>> Просмотр (1024x768, 66 Kb)



Проверено: Shaman007 ()

нееее вот что что а паскаль с динамическими структурами работает очень плоско.Никакой абстракции так сказать,,,даже скушна как то=(.Ставь себе крышки да следи чтоб во время nil были и усе.

Другое дело с++ такие извращения над динамическими данными мона тварить=). а проще ваще забабахать все в отдельный класс переопределить все операции для класса(что в паскале в принципе не возможно) и работать с твоими дифурами как с динамичиским классом данных :)

U-ZvER
()

> Если у кого-то есть предложения о том, как бы код упростить -- буду рад выслушать.

взять готовую математическую библиотеку и переписать все на с++

anonymous
()

мда.. паскаль это круто..функции, которые на экран не влезают, абстракции, которых нет))

вот код из sicp:

(define (deriv exp var)
  (cond ((number? exp) 0)
        ((variable? exp)
         (if (same-variable? exp var) 1 0))
        ((sum? exp)
         (make-sum (deriv (addend exp) var)
                   (deriv (augend exp) var)))
        ((product? exp)
         (make-sum
           (make-product (multiplier exp)
                         (deriv (multiplicand exp) var))
           (make-product (deriv (multiplier exp) var)
                         (multiplicand exp))))
        (else
         (error "unknown expression type -- DERIV" exp))))

ezhi
()
Ответ на: комментарий от anonymous

> взять готовую математическую библиотеку и переписать все на с++

не хватало только на c++ фунции дифференцировать :)

ezhi
()
Ответ на: комментарий от anonymous

> взять готовую математическую библиотеку и переписать все на с++

Я бы рад, но курсач по паскалю.

Ну и конечно, лисп для таких задач подходит лучше всего.

И где тут наши паскалезащитники?

nsav
() автор топика

Что за colorscheme? \\ В смысле, что в .Xdresouces ? \\ \\

PS \\ Однако, нашествие имаксеров какое-то! \\ Шучк -- отличный редактор, жаль только, что я vim-ер :)

anonymous
()
Ответ на: комментарий от anonymous

> PS \\ Однако, нашествие имаксеров какое-то! \\ Шучк -- отличный редактор, жаль только, что я vim-ер :)

emacs не редактор, это платформа! :)

ezhi
()
Ответ на: комментарий от ezhi

> кстати, только сейчас заметил darcs-mode

Не очень, если честно. Умеет только самое примитивное. С xtla ни в какое сравнение не идет. Собственно из-за xtla я в следующий раз буду юзать arch, а не другую VCS.

nsav
() автор топика
Ответ на: комментарий от ezhi

За скрин - респект :Emacs+pascal выглядят супер. Теперь такой вот вопросец - наверное уже все заметили по скрину, что Begin в каждой логической скобке перебегает под предыдущую строку - для меня это очень неудобно - я привык структуировть программу выделяя Begin..end отдельно - один под другим - вопрос: Как этого можно добиться в паскаль моде на Emacs? Даже пускай вообще ничего не делает - только бы оставил бы в покое Begin

Да, еще - вот с отладкой тут немного неудобно - что вы используете - gdb?

Да, и еще один минус у FreePascal - у него нет типа Запись :( а мне в курсовом почти 80 процентов всего надо было делать через Записи.

anonymous
()
Ответ на: комментарий от anonymous

> Да, и еще один минус у FreePascal - у него нет типа Запись :( а мне в курсовом почти 80 процентов всего надо было делать через Записи.

Странно, а у меня есть...

svyatogor ★★★★★
()
Ответ на: комментарий от anonymous

>За скрин - респект :Emacs+pascal выглядят супер. Теперь такой вот 
вопросец - наверное уже все заметили по скрину, что Begin в каждой 
логической скобке перебегает под предыдущую строку - для меня это очень 
неудобно - я привык структуировть программу выделяя Begin..end отдельно
 - один под другим - вопрос: Как этого можно добиться в паскаль моде на 
Emacs? Даже пускай вообще ничего не делает - только бы оставил бы в 
покое Begin

То есть так?

for i:=0 to 5 do begin
something1;
something2; 
                 end;

ИМХО, мой стиль почитабельнее будет.

> Да, еще - вот с отладкой тут немного неудобно - что вы используете - gdb?

Да, gdb+gud отличная связка. В gentoo с фрипаскалем устанавливаются скрипты для gdb, так что с отладкой гораздо лучше чем в BP7.

> Да, и еще один минус у FreePascal - у него нет типа Запись :( а мне в курсовом почти 80 процентов всего надо было делать через Записи. 

Что-что? Все в нем есть.

nsav
() автор топика

Советую немного забыть про лисп и перечитать книгу по паскалю в тем местах где написано про case ... of. Также советую сменить стиль трешного кодинга в стиле "напишу ка я все в одну строчку" на более или менее структурированное написание: вроде процедуры и функции в паскле никто не отменял.

ukez
()
Ответ на: комментарий от ukez

Лучше предоставь своё код на паскале, решающий эту задачу. Код на mit-scheme выше по треду. И мы сравним что удобнее.

ugoday ★★★★★
()

В примерах к PAIP http://www.norvig.com/paip.html есть код, умеющий дифференцировать/интегрировать/упрощать(до некоторой степени) выражения, причём понимает он не только + - * /, но и sin/cos/exp/etc. Работает с инфиксной записью, всё вместе - 20 кб кода на лиспе.

При всём при этом упрощение производится при помощи правил, описываемых кодом типа

(setf *simplification-rules* 
 (append *simplification-rules* (mapcar #'simp-rule '(
  (log 1         = 0)
  (log 0         = undefined)
  (log e         = 1)
  (sin 0         = 0)
  (sin pi        = 0)
  (cos 0         = 1)
  (cos pi        = -1)
  (sin(pi / 2)   = 1)
  (cos(pi / 2)   = 0)
  (log (e ^ x)   = x)
  (e ^ (log x)   = x)
  ((x ^ y) * (x ^ z) = x ^ (y + z))
  ((x ^ y) / (x ^ z) = x ^ (y - z))
  (log x + log y = log(x * y))
  (log x - log y = log(x / y))
  ((sin x) ^ 2 + (cos x) ^ 2 = 1)
  ))))

BTW - PAIP - потрясающая книга, хоть и описанный там "искусственный интеллект" несколько устарел. Очень советую (хотя купить её можно разьве что через Amazon...)

anonymous
()

[trying again - formatting fixed]

В примерах к PAIP http://www.norvig.com/paip.html есть код, умеющий
дифференцировать/интегрировать/упрощать(до некоторой степени)
выражения, причём понимает он не только + - * /, но и sin/cos/exp/etc.
Работает с инфиксной записью, всё вместе - 20 кб кода на лиспе.

При всём при этом упрощение производится при помощи правил,
описываемых кодом типа

(setf *simplification-rules* 
 (append *simplification-rules* (mapcar #'simp-rule '(
  (log 1         = 0)
  (log 0         = undefined)
  (log e         = 1)
  (sin 0         = 0)
  (sin pi        = 0)
  (cos 0         = 1)
  (cos pi        = -1)
  (sin(pi / 2)   = 1)
  (cos(pi / 2)   = 0)
  (log (e ^ x)   = x)
  (e ^ (log x)   = x)
  ((x ^ y) * (x ^ z) = x ^ (y + z))
  ((x ^ y) / (x ^ z) = x ^ (y - z))
  (log x + log y = log(x * y))
  (log x - log y = log(x / y))
  ((sin x) ^ 2 + (cos x) ^ 2 = 1)
  ))))

BTW - PAIP - потрясающая книга, хоть и описанный там "искусственный
интеллект" несколько устарел. Очень советую (хотя купить её можно
разьве что через Amazon...)

anonymous
()
Ответ на: комментарий от ugoday

Давай лучше сравним твой код на схеме и строчку из MathCad-а ? что удобнее ? Глупо сравнивать средство ориентированное на решение задач AI и обработку символьных данных с чисто учебным языком программирования. В плане простоты реализации паскаль абсолютно сольет на этом примере: необходимо писать лексический, синтаксический анализатор выражений, процедуру обработки синтаксического дерева и т.д. В итоге кода будет раз в 40 больше.

Но в плане обучения студентов:

Cкажи чему научится человек написавший такую программу на схеме ? Ответ - примерно ничему. Такая же программа как и все остальные на схеме. Максимум - станет немного лучше владеть схемой. А чему научится человек написавший подобную программу на паскале - многому из того что надо знать относительно принципов построения синтаксических анализаторов. поди еще будет вынужден заглянуть в "дракона" по мере написания.

курсовой пишут не ради программы как таковой, а ради усвоения некоторых профессиональных навыков.

ukez
()
Ответ на: комментарий от ukez

>То есть так? for i:=0 to 5 do begin something1; something2; end;

Возможно не получится точно показать здесь(так как тут довольно интересное форматирование текста получается), но что-то в таком районе

While .... do

Begin

....

end;

Короче отдельным блоком

anonymous
()
Ответ на: комментарий от anonymous

Как видно - выше не получилось - отступ от While в 2-3 позиции, а все что внутри - еще отступ уже от Begin\End

anonymous
()
Ответ на: комментарий от ukez

> Cкажи чему научится человек написавший такую программу на схеме ? Ответ - примерно ничему. Такая же программа как и все остальные на схеме.

сильно сказано - все программы на схеме одинаковы. не понятно, критика это или нет? :)

предлагаю писать тогда курсовые такого рода на ассемблере, походу ещё можно будет много чему научиться..например написать сначала компилятор си, libc, потом интерпретатор R5RS схемы, ну а потом уж и передрать из sicp'а ту самую программку приведённую выше.

для того, чтобы увидеть чему можно научиться на схеме, см. sicp - вводный курс по программированию, читающийся в MIT

ezhi
()
Ответ на: комментарий от ukez

Насчёт анализаторов - в том же PAIP можно найти парсер http://www.norvig.com/paip/syntax1.lisp

Имхо дифференцирование - это одна задача, а разбор - другая, и незачем их столь варварским образом перемешивать - иначе у студентов каша в голове будет.

anonymous
()
Ответ на: комментарий от ezhi

Я читал эту книгу. Често - ни одного принципиально важного "откровения" не нашел. В приницпе все интересные задачки от туда можно реализовать на том же паскале.

ukez
()
Ответ на: комментарий от ukez

> Советую немного забыть про лисп и перечитать книгу по паскалю в тем местах где написано про case ... of.

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

Можно конечно от названий функций перейти к их номерам, но мне облом запоминать таблицу 30 функций.

> Также советую сменить стиль трешного кодинга в стиле "напишу ка я все в одну строчку" на более или менее структурированное написание: вроде процедуры и функции в паскле никто не отменял.

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

nsav
() автор топика
Ответ на: комментарий от nsav

Спасибо, значит тогда так
 While....do
   Begin
     ...
   end;
Надеюсь так получится

anonymous
()
Ответ на: комментарий от ukez

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

не спорю, а ещё можно на vb :)

ezhi
()
Ответ на: комментарий от ezhi

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

ukez
()
Ответ на: комментарий от anonymous

> Имхо дифференцирование - это одна задача, а разбор - другая

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

ukez
()
Ответ на: комментарий от ukez

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

Слушай умник, напиши свою реализацию. Слабо? Или только пространно о неполноценности других рассуждать можешь?

ЗЫ. Считай что парсер и принтер у тебя есть.

nsav
() автор топика
Ответ на: комментарий от ukez

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

это именно глюк языка. я уверен, что 90% программ, написанных на паскале вылгядят именно так. на схеме же что-то написать без введения дополнительных абстракций из предметной области просто не получится.

схемного кода аналогичного по стилю приведённому на скрине не существует. :)

ezhi
()
Ответ на: комментарий от nsav

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

program DelphiSexpr; 
{$APPTYPE CONSOLE}
uses
  SysUtils;

type

{лексема выражения ::= число | идентификатор | открывающая скобка | закрывающая скобка | Конец в воду :) }

   TokenType = ( Num , Id, OpenParen, CloseParen, EOF ); { код лексемы }

   Token = {лексема}
   record
     case KindOf : TokenType of
        Num : ( Number : integer;   );
        Id  : (Identifier : string[10]; );
   end;

   SExprType = (Atom,Form);  { s-выражение - может быть формой или атомом.
                                Не помню как это согласуется с lisp-идеологией}

   type PSExpr = ^SExpr;

   SExpr =
   record
     case KindOf :SExprType of
        Atom : ( AtomToken : Token; );
        Form : ( SExprNodes : array [1..10] of PSexpr); { тут на самом деле должен быть список. лень рисовать. }
    end;

var
  exprstring : string;
  T : Token;

procedure Pipez(pipezstring: string);
begin
        WriteLn('Error! ',pipezstring);
        Halt(1);
end;

function Lex (var exprstring: string): Token;
var
   CurToken : Token;
   i,j : integer;

 begin

    i:=Length(TrimRight(exprstring));

    if i>0 then
    begin

      j:=i;

      case exprstring[i] of

      '(' : CurToken.KindOf:=OpenParen;
      ')' : CurToken.KindOf:=CloseParen;

      'A'..'Z': begin
                  while exprstring[i-1] in ['A'..'Z'] do dec(i);

                  if (i-1>0) and not (exprstring[i-1] in [' ','(' ,')']) then Pipez('Wrong token!');

                  CurToken.KindOf:=Id;
                  CurToken.Identifier:=Copy(exprstring,i,j-i+1);

                end;

      '0'..'9': begin
                while exprstring[i-1] in ['0'..'9'] do dec(i);

                  if (i-1>0) and not (exprstring[i-1] in [' ','(',')']) then Pipez('Wrong token!');

                  CurToken.KindOf:=Num;
                  CurToken.Number:=StrToInt(Copy(exprstring,i,j-i+1));

                end;

       else Pipez('Wrong token!');
       end;

       SetLength(exprstring,i-1);

    end
    else
      CurToken.KindOf:=EOF;

    Lex:=CurToken;

end;

function Parse(exprstring:string): PSExpr;
var
   T:Token;

   function ParseAtom(T:Token):PSExpr;
   var
      P:PSExpr;

   begin
      New(P);
      P^.KindOf:=Atom;
      P^.AtomToken:=T;

      ParseAtom:=P;
   end;

   function ParseSExpr(var exprstring:string):PSExpr;
   var
       T:Token;
       P:PSExpr;
       i,j:integer;

   begin
      i:=10;

      New(P);
      P^.KindOf:=Form;

      repeat
        T:=Lex(exprstring);

        case T.KindOf of
           CloseParen : begin P^.SExprNodes[i]:=ParseSExpr(exprstring); dec(i); end;
           OpenParen  : ;
           EOF        : Pipez('Unexpected EOF!');
           else         begin P^.SExprNodes[i]:=ParseAtom(T); dec(i); end;
        end;

        if i<0 then Pipez('i<0!');

      until T.KindOf=OpenParen;

      for j:=i downto 1 do P^.SExprNodes[j]:=nil;

      ParseSExpr:=P;

   end;
begin

   T:=Lex(exprstring);

   case T.KindOf of
     CloseParen : Parse:=ParseSExpr(exprstring);
     EOF: Pipez('Empty s-expr!');
     else Parse:=ParseAtom(T);
   end;

  if Lex(exprstring).KindOf<>EOF then Pipez('Syntax error!');

end;

procedure PrintSExpr(P : PSExpr);
 var
   i : integer;
 begin
   case P^.KindOf of
     Form :
            begin
              Write('[ ');
              for i:=1 to 10 do {реально  тут должен быть проход по спсику }
                if P^.SExprNodes[i]<>nil then PrintSExpr(P^.SExprNodes[i]);
              Write('] ');
            end;
       else
            case P^.AtomToken.KindOf of

             Id : Write(P^.AtomToken.Identifier,' ');
             Num : Write(P^.AtomToken.Number,' ');

             end;
       end
   end;


begin

   WriteLn('Please, enter s-expression:');
   ReadLn(exprstring);

   PrintSExpr(Parse(exprstring));

end.


Забавно. Конечно все писалось на коленке, но вышло напорядок читабельнее имхо.

ukez
()
Ответ на: комментарий от ukez

Осталось прикрутить к этой фигне грамотный анализатор s-expr на предмет дифференицируемости. Фактически это трансляция из одного дерева в другое. По идее ничего сложного быть не должно.

ukez
()
Ответ на: комментарий от ukez

> Осталось прикрутить к этой фигне грамотный анализатор s-expr на предмет дифференицируемости. Фактически это трансляция из одного дерева в другое. По идее ничего сложного быть не должно.

Парсер секспов у меня тоже есть. Я говорил, про сам алгоритм дифференцирования, который мы здесь обсуждали.

nsav
() автор топика

Дифференциирование - ерунда. Вот попробуй грамотно упростить полученное выражение...

Проверять следующим:
sin^2(x) + cos^2(x)
x+x-x
x+x

Ну и так далле по известным формулам.

linux_newbe
()
Ответ на: комментарий от linux_newbe

Я тоже 2 года назад чё-то ковырял подобное в delfi. В итоге поставили 4 - не удовлетворило упрощение. Кстати есть такой язычок очень неплохой для символьной обработки данных - ICON.

linux_newbe
()
Ответ на: комментарий от linux_newbe

> Дифференциирование - ерунда.

Да, ерунда. Согласен. Цель этого скрина показать насколько геморойно такая ерунда реализовывается на паскале.

А для упрощения выражений нужен вообще pattern matching. В общем, проще написать интерпретатор лиспа на паскале и не мучаться :)

nsav
() автор топика
Ответ на: комментарий от nsav

и какой там алгоритм просто грамотный обходчик исходного дерева. вид примерно такой:

для аргументов формы :

Если дифф. число, то результат его дифф =0 Если дифф. переменную, то результат =1 Если аргумент s-выражение, проверяешь есть ли среди его вершин-аргументов переменная, если есть - дифференцируешь в соотвествии с правилами для данной функции, в противном случае результат равен нулю.

Например для (+ x 1) код должен быть таким

function Diff (...) : PSExpr

if CheckForVarible(P) then ...

...

if P^.funcName = Plus then Diff:=MakeSum(Diff(Arg(1,P)),Diff(Arg(2,P))); ...

В конце конечно получится кривое выражение, которое можно упростить выполнив все возможные действия. Для этого можно написать простенький интерпертатор SExpr

ukez
()
Ответ на: комментарий от ukez

Черт, вот представляешь у меня такой же алгоритм. В суд подавать за плагиат надеюсь не будешь?

В общем, жду от тебя работающего кода. Забей на упрощение. Просто дифференцирование. Если будет принципиально лучше моего, то я признаю свою неправоту. Договорились?

ЗЫ. Еще не забудь о том как дифференцируется f(x)^g(x).

nsav
() автор топика
Ответ на: комментарий от nsav

А твой код - сплошные закорючки таким макаром пиши хоть на лиспе хоть на cи, хоть на паскале - черт ногу сломит в любом случае. Оформлять надо лучше.

Я уверен что у тебя такая же бодяга и в парсере s-expr.

ukez
()
Ответ на: комментарий от ukez

> Окей. Деньги за клепание тебе курсача переведешь через ВебМани я полагаю ?

Не съезжай. Мне твой код нафиг не нужен. Для меня понятие "честность" не пустой звук.

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

Юзать парсер секспов внутри программы -- это уже костыль. Если уж делать так, то надо делать интерпретатор диалекта лиспа, так будет ИМХО гораздо логичней, чем мешать лисп и паскаль в одну кучу.

nsav
() автор топика
Ответ на: комментарий от nsav

_принципиально_ другой подход к кодированию данного алгоритма.

Приницпиально иной подход к кодированию это внушение компьютеру усилием мысли ?

По существу алгоритма : другой реализации кроме как обработки символьных данных я не вижу.

По существу твоего скрина : ты пожалуйста не передергивай. Сам написал, что скрин показывает "насколько легко и приятно работать в паскале с динамическими структурами данных." Я тебе отвечаю не с позиций "паскаль круче лиспа при решении задачи символьного дифф.", а с позиции что твой код не является образцом того как правильно работать со структурами данных в паскале. Из-за выбора неправильных решений на уровне реализации твой код превратился в месиво. По сути возможности паскаля в области необъектных структур данных приравниваются возможностям Си (если не брать в расчет адресную арифметику). Более строгая типизация эффективной работе с динамическими данными не мешеает. Просто у тебя привычки сишные и лисповские и ты их переломить не можешь.

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

ukez
()
Ответ на: комментарий от aim1159

Есть такой сервис в инете, раздает доменные имена третьего уровня нашару. Сходи на http://no-ip.org и почитай подробней.

nsav
() автор топика
Ответ на: комментарий от ukez

> Сам написал, что скрин показывает "насколько легко и приятно работать в паскале с динамическими структурами данных." Я тебе отвечаю не с позиций "паскаль круче лиспа при решении задачи символьного дифф.", а с позиции что твой код не является образцом того как правильно работать со структурами данных в паскале. Из-за выбора неправильных решений на уровне реализации твой код превратился в месиво

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

Я ж не говорю, что я гуру и мой код идеальный. Я всегда рад чему-нибудь научиться.

nsav
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.