Zmiana wyniku po użyciu pułapki

0

Niestety nie mogę sobie z tym poradzić, choć siedzę nad tym kilka godzin i wydaje mi się to kompletnie irracjonalne.

Napisałem moduł, w którym jest umieszczonych kilka funkcji.

W głównej treści programu odniosłem się do jednej z tych funkcji. Wynikiem tej funkcji jest rekord danych, a parametrami trzy inne rekordy. Wszystko bardzo fajnie działa. Z wyjątkiem jednej z danych wyniku funkcji. Wszystkie wyniki zapisuje do pliku tekstowego, a w nim ta jedna dana ma taką samą wartość dla każdego użycia funkcji. Funkcja jest wykonywana 6 razy, za każdym razem ma inne dane, do tego część jest losowana w funkcji, tak więc jest nikłe prawdopodobieństwo, że wyniki po prostu się powtarzają.

Próbowałem ustawić kilka pułapek, żeby sprawdzić gdzie jest błąd... Okazało się, że błędu nie ma :o wartość tej danej przyjmuje za każdym razem inną wartość i o dziwo w pliku tekstowym również są inne wartości. Szczęśliwy, usunąłem wszystkie pułapki i uruchomiłem program. Znowu ta sama dana przyjmuje 6 razy tą samą wartość.

for I := 1 to IloscOsob do
    begin
      SetLength(Wyniki, I);
      Wyniki[I - 1] := Silnik.Oblicz(Miejsce, DaneOsobowe[I], Pogoda);
    end;

AssignFile(PlikWyniku, 'wyniki/wynik.txt');  
ReWrite(PlikWyniku);
  for I := 0 to IloscOsob - 1 do
    WriteLn( FloatToStrF(Wyniki[I].Srednia, ffNumber, 0, 2),  '	', FloatToStr(Wyniki[I].OcenaLaczna));
  CloseFile(PlikWyniku);

Tak wygląda kod, w którym pojawia się błąd, po umieszczeniu pułapki, przed end zaczyna działać.

Pomóżcie mi, bo ja nie ogarniam na prawdę co się dzieje. Dzięki

0

Wszystkie wyniki zapisuje do pliku tekstowego [...]

Do gołego pliku tekstowego, czy korzystasz z jakiegoś konkretnego formatu do przechowywania tych danych? Pokaż zawartość pliku z tymi konfiguracjami; A tak w ogóle to co dokładnie trzymasz w tych plikach?

for I := 1 to IloscOsob do
begin
  SetLength(Wyniki, I);
  Wyniki[I - 1] := Silnik.Oblicz(Miejsce, DaneOsobowe[I], Pogoda);
end;

Dziwna ta pętla - czym są te Wyniki? Pokaż deklarację tej zmiennej;

Co do zapisu danych do pliku to nie zapisujesz ich do pliku - nie podajesz zmiennej PlikWyniku jako pierwszy argument procedury Writeln :]

Poza tym sam kod obsługujący plik docelowy jest nienajlepszy - brak zabezpieczenia przed wyciekami pamięci; Jeśli otwarcie, zapis oraz zamknięcie pliku realizowane jest w jedym bloku kodu (np. w jednej metodzie), zaleca się korzystanie z bloku Try Finally:

var
  tfOutput: TextFile;
begin
  AssignFile(tfOutput, 'File.txt');
  ReWrite(tfOutput);
  try
    { zapis danych do pliku }
  finally
    CloseFile(tfOutput);
  end;
end;

Jeśli podczas zapisu danych zostanie utworzony jakikolwiek wyjątek (niekoniecznie związany z procedurą Writeln), powyższy kod i tak zamknie plik, dzięki czemu wycieków pamięci nie będzie; Jeśli wszystko pójdzie gładko to też plik zostanie zamknięty; To gwarantuje właśnie sekcja Finally;

Jeśli Twój projekt to zwykła aplikacja konsolowa to jedyne co złego się stanie to wszystko po prostu trafi na ekran konsoli; Jeśli jednak jest to aplikacja okienkowa - dostaniesz konkretny kod błędu po zaistnieniu wyjątku; A zaistnieje, bo w przypadku aplikacji okienkowych, funkcje Write i Writeln mogą być używane jedynie w kontekście zapisu danych do pliku.

0
furious programming napisał(a):

Wszystkie wyniki zapisuje do pliku tekstowego [...]

Do gołego pliku tekstowego, czy korzystasz z jakiegoś konkretnego formatu do przechowywania tych danych? Pokaż zawartość pliku z tymi konfiguracjami; A tak w ogóle to co dokładnie trzymasz w tych plikach?

Do pustego pliku tekstowego, wyniki są oddzielone "tabami", tak żeby sobie to wkleić później do excela i dalej stworzyć wykresy.

furious programming napisał(a):

Dziwna ta pętla - czym są te Wyniki? Pokaż deklarację tej zmiennej;

Wyniki są tego samego typu co typ funkcji Silnik.Oblicz

 
var
  Wyniki: Array of Dane; 

W drugim module:

 
Dane = Record
    Sila: Array[1..7] of Extended;
    Kierunek: Array[1..7] of Integer;
    Odleglosc: Extended;
    NumerID: Integer;
    Srednia: Extended;
    OcenaLaczna: Extended;
  end;

function Oblicz(Miejsce: DaneLokalizacji; Osoba: DaneOsoby; Pogoda: DanePogody): Dane;
furious programming napisał(a):

Co do zapisu danych do pliku, to nie zapisujesz ich do pliku - nie podajesz zmiennej PlikWyniku jako pierwszy argument procedury Writeln :]

A to przez moje kombinacje, normalnie jest wszystko poprawnie. Na forum po prostu usunąłem część WriteLn

Dziwi mnie to że wszystkie wyniki wychodzą poprawnie, a jedynie Wyniki[I].Srednia dla każdego I ma taką samą wartość. Choć tak jak napisałem, po wstawieniu pułapki wszystko działa poprawnie. Tak jakby program potrzebował zatrzymania w danym miejscu, żeby sczytać dane, bo inaczej w tym konkretnym przypadku z autoamatu przypisuje wartość z pierwszego użycia funkcji.

0

[...] wyniki są oddzielone "tabami" [...]

Ten dziwny odstęp to tabulator? Normalnie to się robi wstawiając #9 zamiast literału;

'Wyniki' są tego samego typu co typ funkcji Silnik.Oblicz

Co zwracany typ przez funkcję Silnik.Oblicz; Przy okazji - wszystkie słowa kluczowe (zarezerwowane słowa) piszemy tylko i wyłącznie małymi literami, trzymaj się tej zasady;

Jeśli to zwykła macierz dynamiczna to powinieneś używać funkcji Low i High do indeksacji pętli (przy czym ta pierwsza w przypadku macierzy dynamicznych może być zastąpiona wartością 0); Poza tym nie nadawaj macierzy nowego rozmiaru w każdej iteracji pętli; Najpierw nadaj rozmiar, a następnie w pętli tylko wypełnij struktury w niej zawarte:

var
  I: Integer;
begin
  SetLength(Wyniki, IloscOsob);

  for I := Low(Wyniki) to High(Wyniki) do
    Wyniki[I] := Silnik.Oblicz(Miejsce, DaneOsobowe[I], Pogoda);

Znacznie szybsze rozwiązanie; Zresztą IMO lepszym wyjściem było by przekazanie całego rekordu przez pustą referencję (ze słówkiem Out) w dodatkowym parametrze metody Oblicz; Nie proponuję wskaźników, bo wiem że mało kto je lubi :]

Jednak w dalszym ciągu nie rozumiem w czym problem, w której linijce jest błąd i jaka jest treść tego błędu.

0

Problem w tym, że żaden błąd się nie pojawia, wszystko jest liczone. Postaram się skrócić kod, żeby wstawić w całości, tak żeby było widocznie co i jak.

Wynikiem funkcji jest rekord danych. Wszystkie dane z wyjątkiem jednej są liczone poprawnie. Natomiast ten jeden wychodzi taki sam dla każdego użycia. Dopiero po zatrzymaniu wykonywania programu przez pułapkę, albo nawet messagebox zaczyna rozróżniać wartości tej jednej danej. Ciężko jest to wytłumaczyć :/

0

W takim razie problemem nie jest zapis danych do pliku (choć kod i tak wymaga poprawienia i napisałem Ci jak to zrobić), a obliczanie wartości dla pól struktury; Dlatego też pokaż kod wykonujący obliczenia, bo w nim musi się znajdować bug.

0

Zaraz wstawię cały kod, gdzieś pewnie musiałem popełnić błąd. Ale tak jak mówię, ta wartość jest obliczalna poprawnie, natomiast jest pomijana jeśli nie zatryzmam programu w odpowiednim momenice.

0

Okej, skróciłem wszystko jak tylko mogłem. Mam nadzieję, że będzie jasne.

 
unit aps;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, sils;

type
  TAp = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    Silnik: TSilnik;
    procedure CzGlowna;
  public
  end;

var
  Ap: TAp;

implementation

{$R *.lfm}

procedure TAp.FormCreate(Sender: TObject);
begin
  CzGlowna;
  Application.Terminate;
end;

procedure TAp.CzGlowna;
var
  Wyniki: Array of Extended;
  I: Integer;
begin
  SetLength(Wyniki, 7);

  for I := 1 to 6 do
    Wyniki[I] := Silnik.Oblicz;

  for I := 1 to 6 do
    ShowMessage(FloatToStrF(Wyniki[I], ffNumber, 0, 2));
end;

end.  

I dodatkowy moduł, w którym znajduje się funkcja

unit sils;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls;

type
  TSilnik = class
   private
   public
     function Oblicz: Extended;
   end;

implementation

function TSilnik.Oblicz:Extended;
var
  SumaWiatru: Extended;
begin
  Randomize;
  sumawiatru := random(200);

  Oblicz := SumaWiatru;
end;

end.    
0

Doszedłeś do wniosku, że jedna dana nie pasuje, ale nie podzielisz się tą informacją.

Daj input, output programu, potem prawidłowy output, a rozwiązanie same się znajdzie.

2

@dani17 - jeśli masz zwykłą funkcję lub metodę funkcyjną to używaj ukrytej zmiennej Result do ustalania rezultatu, zamiast odwoływać się do nazwy funkcji, jak robiło się to 20 lat temu w TP7;

Poza tym procedurę Randomize wrzuć do głównego modułu projektu (pliku .lpr), najlepiej na początek głównego bloku kodu; Dzięki temu będziesz miał pewność, że w całym cyklu życia programu, została wywołana jedynie raz; Zapamiętaj sobie - ta procedura ma być wywoływana tylko raz, najlepiej podczas rozruchu programu - wtedy będzie działać prawidłowo, a funkcja Random zwracać będzie faktycznie "losowe" liczby;

Edit: Sprawdź też co dla różnych wartości zwraca FloatToStrF, według tego co podajesz jej w parametrach.

0
furious programming napisał(a):

Poza tym procedurę Randomize wrzuć do głównego modułu projektu (pliku .lpr), najlepiej na początek głównego bloku kodu; Dzięki temu będziesz miał pewność, że w całym cyklu życia programu, została wywołana jedynie raz; Zapamiętaj sobie - ta procedura ma być wywoływana tylko raz, najlepiej podczas rozruchu programu - wtedy będzie działać prawidłowo, a funkcja Random zwracać będzie faktycznie "losowe" liczby;

To jest rozwiązanie, jakoś tego nie przemyślałem. Przecież przy każdym użyciu funkcji próbowałem inicjować Randomize, a to wszystko psuło.

Dzięki wielki. Problem okazał się wręcz idiotyczny. Natomiast dziękuję za porady dodatkowe, które zastosuję.

1 użytkowników online, w tym zalogowanych: 0, gości: 1