Czemu niepowiązana zmienna kopiuje wartość od drugiej?

0

Czemu niepowiązana zmienna kopiuje wartość od drugiej?
Z nieznanego powodu pRekur kopiuje wartość pierwiastekIter. Ma oczywiście samodzielnie liczyć wartość, ze środka funkcji są poprawne wydruki.
Przykładowe dane wejściowe 2,1000,0.0001,10
Można to zauważyć jak zakomentuje się pierwiastekIter.

program zadanie_v4;
var a, p, Eps: real;
var MaxI: integer;
var pierwiastekIter:real;
var pRekur: real;
var iIter, iRekur:integer; // liczniki iteracji dla pierwiastka iteracyjnego i rekurencyjnego

procedure CzytajDanePierwiastka;
begin
     write( 'Podaj liczbe rzeczywista - a: ' ); readln( a );
     writeln;
     write( 'Podaj wartosc pierwszego przyblizenia - p: ' ); readln( p );
     writeln;
     write( 'Podaj dokladnosc Eps: ' ); readln( Eps );
     writeln;
     write( 'Podaj maksymalna liczbe iteracji MaxI: ' ); readln( MaxI );
     writeln;
end;


function LiczPierwiastekIteracyjnie(a, p, Eps:real; var MaxI:integer): real;
var pierwiastek, b:real;
var i: integer;
begin
     pierwiastek := p;
     b := a;
     i := 0;

       while(abs(p-b)>=Eps) and (i<MaxI) do
       begin
           p := (p+b)/2;
           b := a/p;
           writeln('pierwiastek ', p:5:5, ' b:', b:5:5);
           i := i+1;
       end;
       writeln;
       iIter := i;
       LiczPierwiastekIteracyjnie:=b;
end;


function LiczPierwiastekRekurencyjnie(a, p, Eps:real; var MaxI:integer): real;
var pierwiastek, b:real;
var i: integer;
begin
    if(abs(p*p-a)<=Eps) and (i<MaxI) then
    begin
        LiczPierwiastekRekurencyjnie:=p;
    end
    else
        begin
             p:=0.5*(p + a/p);
             i := i+1;
             writeln ('p:', p);
             LiczPierwiastekRekurencyjnie(a,p,Eps,MaxI);
        end;
     iRekur := i;
end;


procedure WypiszPierwiastek(var pierwiastekIter, pierwiastekRekur:real);
begin
     writeln('Obliczona wartosc pierwiastka iteracyjnie x=', pierwiastekIter:7:7);
     writeln('Wykonana liczba iteracji - ', iIter);
     writeln('Kwadrat pierwiastka - x*x=', (pierwiastekIter*pierwiastekIter):10:10);
     writeln('Roznica liczby i kwadratu pierwiastka - a-x*x=', (a-pierwiastekIter*pierwiastekIter):10:10);
     writeln;
     writeln('Obliczona wartosc pierwiastka rekurencyjnie x=', pierwiastekRekur:7:7);
     writeln('Wykonana liczba rekurencji - ', (iRekur-1));
     writeln('Kwadrat pierwiastka - x*x=', (pierwiastekRekur*pierwiastekRekur):10:10);
     writeln('Roznica liczby i kwadratu pierwiastka - a-x*x=', (a-pierwiastekRekur*pierwiastekRekur):10:10);
end;


begin
     pierwiastekIter := 0;
     pRekur := 0;
     CzytajDanePierwiastka;
     pierwiastekIter := LiczPierwiastekIteracyjnie(a,p,Eps,MaxI);
     pRekur := LiczPierwiastekRekurencyjnie(a,p,Eps,MaxI);
     writeln('pierwiastekrekur ', pRekur);
     WypiszPierwiastek(pierwiastekIter, pRekur);
     readln;
end.
1

Jesteś pewien, że ten program działa dla wskazanych przez Ciebie danych?
Odpaliłem go u siebie i wpisałem te dane testowe, które podałeś w poście - jak zresztą widać na poniższym obrazku.

screenshot-20221202131222.png

Wprawdzie liczba iteracji została wpisana na 10, ale (sądząc po ilości danych wrzuconych do konsoli) poszły ich setki, a może tysiące. A, po chwili, program się wysypał z efektem widocznym na drugim załączonym obrazku :/

screenshot-20221202131252.png

1
  1. Nie zainicjowane i dla LiczPierwiastekRekurencyjnie, dlatego wszystkim wychodzi błąd.
  2. LiczPierwiastekRekurencyjnie nie zwraca wartości z rekurencji, więc pRekur przyjmuje coś, w tym przypadku ostatnią obliczoną wartość.
  3. iRekur zawsze będzie wynosiło 1 po wyjściu z funkcji i nie zwróci w ten sposób licznika rekurencji. To jest zmienna lokalna.

Odpal ten kod pod debugerem i prześledź krok po kroku co się dzieje.
Poza powyższym wyniki wychodzą chyba ok:
screenshot-20221202152617.png

0

@cerrato: Mi działa ;)(oczywiście mowa o tym, ze program sie nie wysypuje).Zrzut ekranu 2022-12-02 154837.png
@Clarc

  1. Nie wiem jak to zrobić. Inicjowanie w ciele funkcji powoduje zerowanie za każdym razem, bo cała funkcja jest wywołana rekurencyjnie!
  2. Widzę, że nie zwraca, ale dlaczego? Rekurencja działa, a nie zwraca wyniku.

Po wynikach widzę, że poprawiłeś kod. Co zrobiłeś?

2

Jeśli odpowiadasz do zadanych punktów to numeruj odpowiednio do uwagi a na od tyłu....

  1. Skoro się zeruje (bo to zmienna lokalna) to może warto ją przekazywać w parametrach przez referencje i wtedy będzie prawidłowo zliczane każde wywołanie.
  2. Nie zwraca bo jest zastosowana brzydka reguła zwracania wyniku funkcji przez jej nazwę a nie przez Result. Zmień zwracania wartości przez Result to zobaczysz w kodzie czemu nie zwracało. Sprawdź co i kiedy zwraca LiczPierwiastekRekurencyjnie

Zrobiłem dokładnie to co napisałem w dwóch swoich pierwszych punktach żeby wyświetlało poprawnie.

1
  1. Nie wiem jak to zrobić. Inicjowanie w ciele funkcji powoduje zerowanie za każdym razem, bo cała funkcja jest wywołana rekurencyjnie!

Uzgodnijmy zeznania, bo nie mam pewności tego samego rozumienia.

Zmienna lokalna w funkcji rekurencyjnej jest w każdej ramce niezależna. Zerując w ramce [n] nie zerujemy w ramce [n+1]

Jak/gdyby w funckji rekurencyjne się używa / by się używało globalnej to sepuku

0

Zmieniłem licznik, działa dobrze.
Niestety co do 2. punktu po prostu nie rozumiem dlaczego nie przypisuje wartości do pRekur. Zmieniłem zwrot na Result.

function LiczPierwiastekRekurencyjnie(a, p, Eps:real; var MaxI, i:integer): real;
var b:real;
begin
    if(abs(p*p-a)<=Eps) or (i>=MaxI) then
    begin
         writeln('koniec rekurencji p=', p);
         iRekur := i;
         Result:=p;  //zwrot wartosci przez funkcje

    end
    else
        begin
             p:=0.5*(p + a/p);    //obliczenie przyblizenia - srednia z starego przyblizenia i dzielenia liczby a przez stare przyblizenie
             i := i+1;      //inkrementacja licznika
             writeln ('p:', p, ' i:', i, ' MaxI:', MaxI);
             LiczPierwiastekRekurencyjnie(a,p,Eps,MaxI, i); //wywolanie rekurencyjne

        end;

end;
2

@rzezram:
Ja bym dał (choć zgubiłem się w zawiłościach wątku)

Result := LiczPierwiastekRekurencyjnie(a,p,Eps,MaxI, i); //wywolanie rekurencyjne

Pamiętaj, że w lataniu po rekurencji (analogicznie jak tłumaczyłem wyżej) mowa o Resulcie z obecnej ramki, a niekoniecznie globalnym, ostatecznym,

0

@ZrobieDobrze: Wchodzi tylko raz do if true i to na końcu, jak wskazuje wydruk, więc aktualna ramka powinna być ostatnia.

1

@rzezram: postaw breakpoint i przedebuguj kod linijka po linijce, sprawdzając jak wygląda przepływ sterowania oraz wartości zmiennych. W kilka minut znajdziesz przyczynę problemów.

1

Znalazłem rozwiązanie po zobaczeniu na rekurencję w silnii. Powinno być:

LiczPierwiastekRekurencyjnie := LiczPierwiastekRekurencyjnie(a,p,Eps,MaxI, i);

@ZrobieDobrze: Prawdopodobnie dobrze napisałeś, ale ja w złym miejscu podstawiałem.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.