481

Re: Програма розв'язування задач з фізики! Для учнів 7-го класу.

https://github.com/jrathlev/Delphi-LongInteger ще гляньте

Подякували: ProgramBandera1

482

Re: Програма розв'язування задач з фізики! Для учнів 7-го класу.

koala написав:

https://github.com/jrathlev/Delphi-LongInteger ще гляньте

Дякую пане koala. Можливо ви підкажете про функцію яка рахує вирази на кшталт цього (12+30)-7 +(5+2)=. Тобто рахує вираз з дужками. Я десь бачив таку, але вона мені здається була написана чи на JS чи на PHP. коротше в Googl CHROME ця функція є стандартною. Мжливо ви десь натикалися на таку для Delphi?

483

Re: Програма розв'язування задач з фізики! Для учнів 7-го класу.

Ключова ознака, по якій можна швидко відрізнити компільовану мову від інтерпретованої - функція eval (або exec), яка саме це і робить.
Вам треба про дерева розбору синтаксису почитати... або трохи покрутити мізками і написати рекурсивну функцію для цього, вона не надто складна. Ключова проблема - є трохи різні синтаксиси, тому стандартна функція можлива лише в інтерпретованих мовах.
Звісно, є різні сторонні бібліотеки, достатньо лише погуглити.

Подякували: ProgramBandera1

484

Re: Програма розв'язування задач з фізики! Для учнів 7-го класу.

В бібліотеці JCL є клас TEvaluator спеціально для обчислення виразів. Причому він дозволяє легко розширювати функціонал додаючи іменовані константи та власні функції: https://wiki.delphi-jedi.org/wiki/JCL_Help:TEvaluator

В сучасних версіях Делфі те саме може робити двигун LiveBindings, але виходить не так зручно: https://theroadtodelphi.com/2012/07/06/ … evaluator/

Подякували: ProgramBandera, koala, leofun013

485

Re: Програма розв'язування задач з фізики! Для учнів 7-го класу.

ProgramBandera написав:

То що з масивами? Вірно все з довжиною?

Щоб не ламати голову над подібними питаннями, ввімкніть в налаштуваннях проекту Overflow Checking та Range Checking.

P.S. Є проблеми з індексами.

Подякували: ProgramBandera, koala, leofun013

486 Востаннє редагувалося ProgramBandera (05.02.2023 16:16:50)

Re: Програма розв'язування задач з фізики! Для учнів 7-го класу.

Бажаю здоров'я панове! Допоможіть скласти алгоритм для вирішення ось такого завдання.

Потрібно прибрати зайві нулі з (числа-рядка). Наприклад, в нас є Змінна типу: String в якій записане будь-яке дійсне число: 123,2500.
Завдання:
Якщо в нас в кінці рядка є нулі (їх може і не бути, а можуть бути і всі нулі після коми), тобто поки High(Number) = '0' то з цього рядка видалити останній символ '0'.
Та потрібно врахувати ще умову, що після коми можуть бути всі нулі, наприклад 123,0000. Тоді потрібно видалити ще і кому з рядка. Лишити тільки 123.

В мене змінна fNumberReal: String; в якій зберігається саме (число-рядок).
Та є, наприклад для (числа - рядка) 123,2500, змінні типу Cardinal, в яких зберігаються:
LengthNumber - Загальна довжина (числа - рядка) 123,2500 = 8;
LengthIntegerPartNumber - Довжина цілої частини (числа - рядка) = 3;
ComaPosition - Позиція коми в (числу - рядку) = 4;
LengthDecimalPartNumber - Довжина десяткової  частини (числа - рядка) = 4;

Я спробував написати процедуру, але в мене чомусь видалило всі символи до коми, лишилось 123,!

Procedure TNumbers.DeleteZeroInDecimalPartNumber (Number: String);
Begin
  While Number[High(Number)] = '0' Do
    Repeat
      Delete(Number, High(Number), 1);
      fNumberReal := Number;
      SetLengthNumber  // Процедура яка задає довжину рядка
    Until (LengthNumber = ComaPosition)
End;

487

Re: Програма розв'язування задач з фізики! Для учнів 7-го класу.

ProgramBandera написав:

чомусь видалило всі символи до коми, лишилось 123,!

    Repeat
      Delete(Number, High(Number), 1);
      fNumberReal := Number;
      SetLengthNumber  // Процедура яка задає довжину рядка
    Until (LengthNumber = ComaPosition)

Repeat _ Until(...) це "повторити _ поки не ...".

488

Re: Програма розв'язування задач з фізики! Для учнів 7-го класу.

leofun01 написав:

Repeat _ Until(...) це "повторити _ поки не ...".

Так я вже це зрозумів, тому і питаю алгоритм.

489 Востаннє редагувалося leofun01 (05.02.2023 18:12:44)

Re: Програма розв'язування задач з фізики! Для учнів 7-го класу.

Наявність або відсутність коми в стрічковому представленні числа залежить від локалі середовища. Користуй FormatFloat або FloatToStrF.

s := FormatFloat('0.####', fvalue);
Подякували: ProgramBandera1

490 Востаннє редагувалося ProgramBandera (05.02.2023 18:23:33)

Re: Програма розв'язування задач з фізики! Для учнів 7-го класу.

leofun01 написав:

Наявність або відсутність коми в стрічковому представленні числа залежить від локалі середовища. Користуй FormatFloat або FloatToStrF.

s := FormatFloat('0.####', fvalue);

Вибачте пане leofun01, але тут не те! Функція FormatFloat не підходить. Я вже майже доробив процедуру, залишилось задати параметри (Нулі) комі Та десятковій частині числа.

Procedure TNumbers.DeleteZeroInDecimalPartNumber (Number: String);
Begin
  While LengthNumber > ComaPosition - 1 Do
    If Number[High(Number)] <> '0' Then
      Break
    Else
    Begin
      Delete(Number, High(Number), 1);
      fNumberReal := Number;
      SetLengthNumber;
      SetComaPosition;
      SetLengthDecimalPartNumber;
      If Number[High(Number)] = ',' Then
      Begin
        Delete(Number, High(Number), 1);
        fNumberReal := Number;
        SetLengthNumber;
        // Задати позицію коми (в нуль)
        // Задати довжину десяткової частини (в нуль)
      End;
    End
End;

491 Востаннє редагувалося ProgramBandera (13.02.2023 17:43:07)

Re: Програма розв'язування задач з фізики! Для учнів 7-го класу.

Вітаю панове! Допоможіть написати процедуру (або функцію) яка на вхід буде отримувати масив типу Char, а повертати масив типу Integer.

Спробував ось так:

Procedure TransformArrayCharToDigits(SourceArray: Array Of Char;
                                             Var ReturnArray: Array Of Integer);
Var
  I: Integer;
Begin
  SetLength(ReturnArray, Length(SourceArray));  // Помилка несусні типи

  For I := 0 To Length(SourceArray) Do
    Begin
      If (SourceArray[i] In ['0'..'9']) Then
        Begin
          ReturnArray[i] := StrToInt(SourceArray[i])
        End
    End

End;

виходить помилка несумісні типи.

Спробував іще раз ось так:

Procedure TransformArrayCharToDigits(SourceArray: Array Of Char;
                                           Var ReturnArray: Array Of Integer);
Var
  I: Integer;
  TempArray: Array Of Integer;
Begin
  SetLength(TempArray, Length(SourceArray));

  For I := 0 To Length(SourceArray) Do
    Begin
      If (SourceArray[i] In ['0'..'9']) Then
        Begin
          TempArray[i] := StrToInt(SourceArray[i])
        End
    End;
  ReturnArray := TempArray // Несумісні типи: Масив цілих чисел і Динамічний масив
End;

теж помилка Несумісні типи: Масив цілих чисел і Динамічний масив

492

Re: Програма розв'язування задач з фізики! Для учнів 7-го класу.

Проголосіть Array Of Integer окремим типом:

type IntArray = Array Of Integer;
Procedure TransformArrayCharToDigits(SourceArray: Array Of Char; Var ReturnArray: IntArray);
...
    SetLength(ReturnArray, Length(SourceArray));
Подякували: ProgramBandera, leofun012

493

Re: Програма розв'язування задач з фізики! Для учнів 7-го класу.

Підтримую Коалу. Масив, що проголошений в параметрах функції, хоч і виглядає як динамічний, але має трохи інше значення, і називається відкритим. Якщо ж ви проголошуєте окремий тип, то там усе однозначно.

Подякували: ProgramBandera, leofun012

494 Востаннє редагувалося ProgramBandera (14.02.2023 00:16:24)

Re: Програма розв'язування задач з фізики! Для учнів 7-го класу.

Іще одне питання: про відміну успадкованих методів в класах нащадках.

Якщо в мене є батьківський клас TDefinition в якому є метод SearchInAllDefinition(NameDefi: String);. Він важливий для цього класу так як створений об'єкт на базі цього класу може шукати за допомогою цього методу будь-яке визначення.

  TDefinition = Class

    Strict Private
      ....
      ....
    Private
      ....
      ....
    Protected
      ....
      ....
    Public

      Property Definition: String Read GetDefinition Write SetDefinition;
      Procedure PlayDefinition (Play: Boolean);

      //Метод SearchInAllDefinition в класах нащадках потрібно відмінити
      Procedure SearchInAllDefinition(NameDefi: String);

      Constructor Create; Overload;
      Constructor Create(NameLibSound, NameLibText: PWideChar); Overload;
  End;

Implementation

{ TDefinition }

...
...

End.

Та в класі нащадку він абсолютно не потрібен.

Як мені відмінити цей метод SearchInAllDefinition(NameDefi: String); в класі TNumbers? Щоб при створенні об'єкту на базі класу TNumbers мені не з'являвся після крапки цей метод SearchInAllDefinition. Немає якоїсь директиви для цього? Бо я зробив ось так: прописав такий метод в модифікаторі доступу Private і описав ого в Implementation як пустий метод. І він після цього перестав мені з'являвся після крапки.

Є клас нащадок:

  TNumbers = Class(TDefinition)

    Strict Private
      ....
      ....
    Private
      ....
      Procedure SearchInAllDefinition(NameDefi: String);
    Protected
      ....
      ....
    Public

      
  End;

Implementation

{ TNumbers }
Procedure SearchInAllDefinition(NameDefi: String);
Begin
End;
...
...

End.

495 Востаннє редагувалося koala (14.02.2023 07:29:08)

Re: Програма розв'язування задач з фізики! Для учнів 7-го класу.

Ні. Ви маєте переглянути всю структуру класів. Якщо щось виконується для предка, то воно має виконуватися і для нащадка, це один з базових принципів ООП. Максимум ви можете зробити цей метод приватним і реалізувати його зі створенням виключної ситуації, але це погане рішення.
Узагалі, наслідування - це співвідношення "це такий". У вас TNumbers - це такий TDefinition, у якому... (див. визначення). Якщо вам потрібні не всі методи з TDefinition, значить, у вас завеликий TDefinition і з нього варто виділити, наприклад,
TDefinitionBase (без SearchInAllDefinition), від якого вже наслідувати TDefinition (де проголошено SearchInAllDefinition) та TNumbers. Або взагалі тут наслідування не потрібне

Подякували: ProgramBandera, Torbins, leofun013

496

Re: Програма розв'язування задач з фізики! Для учнів 7-го класу.

Вітаю панове. Скажіть будь ласка: Коли застосовувати процедуру Break то після виходу з циклу в змінних нічого не лишається?

Приклад коли виводить функція те що є в змінній, тут Result := IntToStr(LOperatorPosition); пред Break

Function ExpressionAnswer(PExpression: String): String;
Var
  LOperatorPosition: Integer;
  LOperator: Char;                                                   // Оператор
  LDeleteStartPosition, LDeleteEndPosition: Integer; // Позиції видалення виразу
  LFirstOperand, LSecondOperand: Extended;          // Перший та другий операнди

Begin
  I := 1;
  Result := '';

  For I := 1 To Length(PExpression) Do
    Begin

      LDeleteStartPosition := 0;
      LDeleteEndPosition := 0;

                   {Виконуємо з ліва на право множення або ділення}

      If (PExpression[i] = '*') Or (PExpression[i] = '/') Then
        Begin

          If PExpression[i] = '*' Then
            Begin
              LOperator := '*';
              LOperatorPosition := I;



              Result := IntToStr(LOperatorPosition);
              Break
            End
          Else
            Begin
              LOperator := '/';
              LOperatorPosition := I;



              Result := IntToStr(LOperatorPosition);
              Break
            End;

        End;
    End;

End;

А ось тут функція чомусь нічого не виводить коли Result := IntToStr(LOperatorPosition); стоїть після циклу. Чому!!!

Function ExpressionAnswer(PExpression: String): String;
Var
  LOperatorPosition: Integer;
  LOperator: Char;                                                   // Оператор
  LDeleteStartPosition, LDeleteEndPosition: Integer; // Позиції видалення виразу
  LFirstOperand, LSecondOperand: Extended;          // Перший та другий операнди

Begin
  I := 1;
  Result := '';

  For I := 1 To Length(PExpression) Do
    Begin

      LDeleteStartPosition := 0;
      LDeleteEndPosition := 0;

                   {Виконуємо з ліва на право множення або ділення}

      If (PExpression[i] = '*') Or (PExpression[i] = '/') Then
        Begin

          If PExpression[i] = '*' Then
            Begin
              LOperator := '*';
              LOperatorPosition := I;
              Break
            End
          Else
            Begin
              LOperator := '/';
              LOperatorPosition := I;
              Break
            End;

        End;
    End;
  Result := IntToStr(LOperatorPosition);
End;  

497 Востаннє редагувалося koala (16.02.2023 23:19:34)

Re: Програма розв'язування задач з фізики! Для учнів 7-го класу.

Команда break лише виходить з циклу, змінні лишаються як були. І це не процедура.
Ви перестаралися з викиданням "зайвого" і викинули причину вашої проблеми. Цей код працює як слід, але очевидно, що ви вирізали якісь частини, зокрема, проголошення змінної i (а от купу зайвих змінних лишили).
Ось цей фрагмент

      If (PExpression[i] = '*') Or (PExpression[i] = '/') Then
        Begin
          If PExpression[i] = '*' Then
            Begin
              LOperator := '*';
              LOperatorPosition := I;
              Break
            End
          Else
            Begin
              LOperator := '/';
              LOperatorPosition := I;
              Break
            End;

        End;
    End;

можна скоротити. Оскільки PExpression[i] або '*', або '/', то можна зробити

LOperator := PExpression[i];

і тоді повністю прибрати внутрішній if:

If (PExpression[i] = '*') Or (PExpression[i] = '/') Then
        Begin
            LOperator := PExpression[i];
            LOperatorPosition := I;
            Break
        End;

Ну і не зрозуміло, нащо перетворювати число на стрічку перед поверненням.

Подякували: ProgramBandera1

498 Востаннє редагувалося ProgramBandera (17.02.2023 00:21:00)

Re: Програма розв'язування задач з фізики! Для учнів 7-го класу.

koala написав:

Ось цей фрагмент

      If (PExpression[i] = '*') Or (PExpression[i] = '/') Then
        Begin
          If PExpression[i] = '*' Then
            Begin
              LOperator := '*';
              LOperatorPosition := I;
              Break
            End
          Else
            Begin
              LOperator := '/';
              LOperatorPosition := I;
              Break
            End;

        End;
    End;

можна скоротити. Оскільки PExpression[i] або '*', або '/', то можна зробити

LOperator := PExpression[i];

і тоді повністю прибрати внутрішній if:

If (PExpression[i] = '*') Or (PExpression[i] = '/') Then
        Begin
            LOperator := PExpression[i];
            LOperatorPosition := I;
            Break
        End;

Ну і не зрозуміло, нащо перетворювати число на стрічку перед поверненням.

Цей фрагмент навряд чи можна скоротити тому що в виразі який отримує функція в якості рядка може бути вираз: 2563,25*563,06+25,23*37,54/2 і тоді позиція оператора множення береться буде не 8 а 21, а якщо там буде два тири оператора множення. І тоді не буде послідовного виконання операторів.

Мені потрібно послідовно іти з ліва на право виконувати дії.
Першочерговість множення або ділення потім додавання або віднімання.
Тобто функція знайшла оператора перевірила чи це множення чи це ділення (якщо так) виконала витяг числа з права від оператора та з ліва від оператора(мітка закінчення витягу першого операнда або початок рядка або інший оператор, другого операнда або оператор або кінець рядка). Передала числа та оператора в іншу функцію на обрахування. Інша функція повернула результат і цим результатом замінила числа та оператора в рядку які витягувались. І далі знову з ліва на право ітерація чи рекурсія (поки не визначився, тільки почав писати). Якщо множення і ділення немає до кінця рядка то тоді те саме з ліва на право з додаванням і відніманням.
Тобто мені від початку і до кінця рядка потрібно перевірити всі множення і ділення, коли їх не залишиться потрібно перевірити всі додавання і віднімання.

Ось чернетка функції без всяких видалень (тільки начав писати)

Function ExpressionAnswer(PExpression: String): String;
Var
  LBack, LAhead, I: Integer;                                  // Лічильник циклу
//  Operand1, Operand2: String;
  LOperatorPosition: Integer;
  LOperator: Char;                                                   // Оператор
  LDeleteStartPosition, LDeleteEndPosition: Integer; // Позиції видалення виразу
  LFirstOperand, LSecondOperand: Extended;          // Перший та другий операнди

Begin
  I := 1;
  Result := '';
  // Від початку і до кінця виразу
  For I := 1 To Length(PExpression) Do
    Begin

      //Result := '';
      LDeleteStartPosition := 0;
      LDeleteEndPosition := 0;
//      Operand1 := '';
//      Operand2 := '';

                   {Виконуємо з ліва на право множення або ділення}

      If (PExpression[i] = '*') Or (PExpression[i] = '/') Then
        Begin

          If PExpression[i] = '*' Then
            Begin
              LOperator := '*';
              LOperatorPosition := I;



              Result := IntToStr(LOperatorPosition);
              Break
            End
          Else
            Begin
              LOperator := '/';
              LOperatorPosition := I;



              Result := IntToStr(LOperatorPosition);
              Break
            End;

        End;

//                    {Витягуэмо число перед оператором}
//
//          For LBack := (LOperatorPosition - 1) DownTo 1 Do
//            If (PExpression[LBack]= '/') Or (PExpression[LBack]= '+') Or
//                                                  (PExpression[LBack]= '-') Then
//              Break
//            Else
//               LDeleteStartPosition := LBack;
//
//            If PExpression[LBack] <> ' ' Then
//              LFirstOperand := StrToFloat(Copy(PExpression,LBack,(I - 1) - LBack));
//
//                      {Витягуэмо число після оператороа}
//
//          For LAhead := I To Length(PExpression) Do
//            If (PExpression[LBack]= '/') Or (PExpression[LBack]= '+') Or (PExpression[LBack]= '-') Then
//              Break;

    End;
  Result := FloatToStr(LOperatorPosition);
End;

Т справа поки не в оптимізації коду, мене цікавить чому Result := FloatToStr(LOperatorPosition);в коді вище нічого не виводить мені???
А в слідуючому коді Result := FloatToStr(LOperatorPosition); виводить позицію оператора. Де я помилився? Після Break в LOperatorPosition чи в LOperator має ж щось зберігатися? Пробую в Мемо,  що позицію вивести, що LOperator: Char; порожньо!!! Чому???

       //Відповідь обрахунку виразу
Function ExpressionAnswer(PExpression: String): String;
Var
  LBack, LAhead, I: Integer;                                  // Лічильник циклу
//  Operand1, Operand2: String;
  LOperatorPosition: Integer;
  LOperator: Char;                                                   // Оператор
  LDeleteStartPosition, LDeleteEndPosition: Integer; // Позиції видалення виразу
  LFirstOperand, LSecondOperand: Extended;          // Перший та другий операнди

Begin
  I := 1;
  Result := '';
  // Від початку і до кінця виразу
  For I := 1 To Length(PExpression) Do
    Begin

      //Result := '';
      LDeleteStartPosition := 0;
      LDeleteEndPosition := 0;
//      Operand1 := '';
//      Operand2 := '';

                   {Виконуємо з ліва на право множення або ділення}

      If (PExpression[i] = '*') Or (PExpression[i] = '/') Then
        Begin

          If PExpression[i] = '*' Then
            Begin
              LOperator := '*';
              LOperatorPosition := I;
              Result := FloatToStr(LOperatorPosition);


              Result := IntToStr(LOperatorPosition);
              Break
            End
          Else
            Begin
              LOperator := '/';
              LOperatorPosition := I;
              Result := FloatToStr(LOperatorPosition);


              Result := IntToStr(LOperatorPosition);
              Break
            End;

        End;

//                    {Витягуэмо число перед оператором}
//
//          For LBack := (LOperatorPosition - 1) DownTo 1 Do
//            If (PExpression[LBack]= '/') Or (PExpression[LBack]= '+') Or
//                                                  (PExpression[LBack]= '-') Then
//              Break
//            Else
//               LDeleteStartPosition := LBack;
//
//            If PExpression[LBack] <> ' ' Then
//              LFirstOperand := StrToFloat(Copy(PExpression,LBack,(I - 1) - LBack));
//
//                      {Витягуэмо число після оператороа}
//
//          For LAhead := I To Length(PExpression) Do
//            If (PExpression[LBack]= '/') Or (PExpression[LBack]= '+') Or (PExpression[LBack]= '-') Then
//              Break;

    End;

End;

499

Re: Програма розв'язування задач з фізики! Для учнів 7-го класу.

Все знайшов помилку, Result := IntToStr(LOperatorPosition) цей рядок був прописаний в дужках циклу після Break

       //Відповідь обрахунку виразу
Function ExpressionAnswer(PExpression: String): String;
Var
  LBack, LAhead, I: Integer;                                  // Лічильник циклу
//  Operand1, Operand2: String;
  LOperatorPosition: Integer;
  LOperator: Char;                                                   // Оператор
  LDeleteStartPosition, LDeleteEndPosition: Integer; // Позиції видалення виразу
  LFirstOperand, LSecondOperand: Extended;          // Перший та другий операнди

Begin
  Result := '';
  LDeleteStartPosition := 0;
  LDeleteEndPosition := 0;
//      Operand1 := '';
//      Operand2 := '';
  // Від початку і до кінця виразу
  For I := 1 To Length(PExpression) Do
    Begin
                   {Шукаємо з ліва на право множення або ділення}

      If (PExpression[i] = '*') Or (PExpression[i] = '/') Then
        Begin
          if PExpression[i] = '*' then
            LOperator := '*';
          if PExpression[i] = '/' then
            LOperator := '/';
          LOperatorPosition := I;
          Break
        End;

    End;

  Result := IntToStr(LOperatorPosition)+ LOperator;

End;

500

Re: Програма розв'язування задач з фізики! Для учнів 7-го класу.

ProgramBandera написав:

цей рядок був прописаний в дужках циклу після Break

Ну тепер ви більше не будете так робити? Я в сенсі - копіювати сюди не проблемний код?

Ось вам, нашвидкуруч написав:

uses StrUtils, SysUtils;

function FindOperatorIdx(expr: String): Integer;
var i: Integer;
begin
  {Result} FindOperatorIdx := 0; 
  for i:=Length(expr) downto 1 do begin
    if ({Result} FindOperatorIdx = 0) and (pos(expr[i], '*/')>0) then
      {Result} FindOperatorIdx := i;
    if pos(expr[i], '+-')>0 then begin
      {Result} FindOperatorIdx := i;
      break;
    end;
  end;
end;

function Calculate(expr: String): Double;
var left, right: Double;
    op_idx, code: Integer;
begin
  expr := Trim(expr);
  op_idx := FindOperatorIdx(expr);
  if op_idx>0 then begin
    left := Calculate(LeftStr(expr, op_idx-1));
    right := Calculate(RightStr(expr, Length(expr)-op_idx));
    case expr[op_idx] of
        '+': {Result} Calculate := left + right;
        '-': {Result} Calculate := left - right;        
        '*': {Result} Calculate := left * right;
        '/': {Result} Calculate := left / right;
        else begin
          writeln('Unknown operator: ', expr[op_idx]);
          {Result} Calculate := 0.0 / 0.0;
        end;
    end;
  end
  else begin
    val(expr, Calculate, code);
    if code>0 then begin
      writeln('Can''t convert ', expr, ' to double');
      {Result} Calculate := 0.0 / 0.0;
    end;
  end;
end;

begin
    writeln(Calculate('2+2'));
    writeln(Calculate('2563.25*563.06+25.23*37.54/2'));
end.

У FPC замість Result пишеться назва функції, там Result у коментарі, можете розкоментувати.
У разі помилки повертається NaN (0.0/0.0), можете замінити на виключну ситуацію.

Подякували: ProgramBandera, leofun012