Zwiększanie wartości zmiennych w pętli

0

Przekazuję fragment mojego kodu.

Program ma m.i za zadanie zwiększanie wartości zmiennych licznik1,licznik2,licznik3 i licznik4 o wartości napisane w pętli.

Z jakiegoś nieznanego mi powodu program nie zwiększa wartości żadnej ze zmiennych.

Oto fragment programu:

{silnik graficzny}
licznik1 :=-16;
licznik2 :=-12;
licznik3 :=1;
licznik4 :=1;
for i2:=1 to 768 do{768 to ilosc pol na ekranie (32x24)}
                   {wykrywa zawartosc pol}
begin
ekran := ekran+1;
  if (mapax[graczx-licznik1] = 1) and (mapay[graczy-licznik2] = 1) then
  begin

  bar(1,1,20,20{licznik3,licznik4,licznik3+20,licznik4+20});
  end;

  {przetwarzanie polozenia pol na ekranie}
licznik1 := licznik1+1;
  if licznik1 = 17 then
  begin
  licznik2 := licznik2+1;
  licznik1 := -16;
  end;
  if licznik2 = 13 then
  begin
  licznik1 := -16;
  licznik2 := -12;
  end;
licznik3 := licznik3+20;{warunki do funkcji rysujacej pola ekranie}
  if licznik3 = 641 then
  begin
  licznik4 := licznik4+20;
  licznik3 := 1;
  end;
  if licznik4 = 481 then
  begin
  licznik4:=1;
  end;
  {koniec przetwarzania polozenia pol na ektanie}
end;

readkey;
end

Jedynie gdy zamiast pisać:

licznik1:=licznik1+1;

piszę:

licznik1:=i2;

wartość zmiennej licznik1 ulega zmianie ale tylko po to by po kilku powtórzeniach zamknąć program.

Co wy na to?

Mogę pokazać kod całego programu ale wydaje mi się że to nie będzie potrzebne.

3

Taka porada - bo pewnie jeszcze nie wiesz, jak działa i jak korzysta się z debuggera - więc może na razie stwórz sobie jakiś plik tekstowy i w każdej iteracji pętli wrzucaj do niego wartość poszczególnych zmiennych. Zobaczysz, co się z nimi dzieje/w jaki sposób ich wartości się zmieniają. To może być duża pomoc w ustaleniu o co tutaj chodzi.

0

@cerrato: Tak, nigdy nie korzystałem z debugger'a. Nawet nie wiem jak się z niego korzysta ani co dokładnie robi. Nie miałem nigdy okazji się przekonać. Mam nadzieję że ten błąd nie wynika z powodu starego kompilatora ale wszystkie funkcje w programie zdają się być poprawne :(

1

Tak podejrzewałem - dlatego proponuję, żebyś zapisał kolejne przebiegi pętli (oraz wartość zmiennych, jaka tam będzie) w pliku tekstowym, potem popatrz na otrzymane wartości i wyciągnij wnioski ;) Serio - zrób to i pewnie wszystko się stanie jasne.

0

@cerrato: Spoko tylko nie wiem jak z tego skorszstać

4

Breakpoint służy do oznaczenia linijki, na której ma się zatrzymać wykonywanie programu — oznaczasz jakąś linijkę kodu jako breakpoint (ustaw kursor tekstowy w takiej linijce i wciśnij F5). Takich breakpointów możesz nastawiać ile chcesz w ilu modułach chcesz. Jak już wszystkie ustawisz, to normalnie uruchom program — jeśli wykonanie dojdzie do linijki z breakpointem to się zatrzyma i będziesz mógł wtedy na spokojnie sprawdzać jak sytuacja wygląda.

Kiedy juz program się zatrzyma, możesz kursorem myszki najeżdżać na różne zmienne i będą się wyświetlać hinty z ich obecnymi wartościami. Aby wykonać jedną linijkę kodu, wciśnij F8. Jesli w danej linijce znajduje się wywołanie jakiejś funkcji to możesz wykonać taką linijkę kodu z wejściem do tej funkcji, wciskając klawisz F7. Aby wykonać cały kod aktualnej funkcji i wyskoczyć z niej, wciśnij Shift+F8. Aby wznowić wykonanie programu, wciśnij F9. Podczas gdy wykonanie programu jest zatrzymane, masz różne możliwości podglądania zmiennych (np. okno Watches), możesz podglądać stos wywołań i inne rzeczy, wykonywać program linijka po linice i sprawdzać co tam się w środku dzieje — możliwości jest masa.


Nie ma się czego bać — postaw trochę breakpointów w różnych linijkach, pobaw się wykonywaniem programu linijka po linijce, podglądaj zmienne itd., nieczego przecież nie zepsujesz. Zobaczysz, że to bardzo proste, a po drugie, że cholernie przydatne. Dzięki znajomości debuggera, będziesz mógł w przyszłości oszczędzić mnóstwo czasu i nie tylko podglądać czy program wykonuje się tak jak chciałeś, ale także ułatwi i przyspieszy wyszukiwanie błędów.

0

@matej47:

NIGDY w pascalu nie użyłem pętli for z ręcznym papraniem jej indeksem (i może dwa razy w reszcie jezyków, przez całą ścieżkę zaowodową). Moze przekaz był, że to akurat w Pascalu zakazane???
(Już prędzej bym się odważył w for C, gdzie jej manualna konstrukcja bardziej do tego zacheca. )

W while by mnie mniej drazniło - pod KONIECZNYM warunkiem, że operacja na idenksie jest wykonywana RAZ w odnodze ifów (tzn nie jest w sposób nieczytelny kumulowana)

Włożyłeś bardzo dużo wysiłku, aby totalnie popsuć czytelność tego kodu, sam ten pomysł, nazwy zmiennych dobrane, aby nic nie objaśniały, magiczne stałe. Oliwa sprawieldiwa, są skutki.

1

Przekopiowałem kod tej pętli do IDE, sformatowałem tak aby wyglądał po Free Pascalowemu:

licznik1 := -16;
licznik2 := -12;
licznik3 :=   1;
licznik4 :=   1;

for i2 := 1 to 768 do
begin
  ekran := ekran+1;
  
  if (mapax[graczx - licznik1] = 1) and (mapay[graczy - licznik2] = 1) then
    bar(1, 1, 20, 20);

  licznik1 += 1;
  
  if licznik1 = 17 then
  begin
    licznik2 += 1;
    licznik1 := -16;
  end;
  
  if licznik2 = 13 then
  begin
    licznik1 := -16;
    licznik2 := -12;
  end;
  
  licznik3 += 20;
  
  if licznik3 = 641 then
  begin
    licznik4 += 20;
    licznik3 := 1;
  end;
  
  if licznik4 = 481 then
    licznik4 := 1;
end;

Jeśli użyłeś w tej pętli iteratora i np. przypisałeś go do zmiennej, a później — jak twierdzisz — program się wyłącza, to znaczy, że gdzieś wyrzuca runtime error i program kończy działanie, bo nie dochodzi do instrukcji ReadKey (ona wstrzymałaby dalsze wykonanie programu do momentu wciśnięcia czegokolwiek).

Skompiluj ten program w formie przyjaznej debuggerowi i odpal go z poziomu IDE, a dowiesz się w czym rzecz.

0

@furious programming: Podmieniłem swój fragment kodu na twój i IDE nie rozpoznało operatora "+='" po poprawieniu wszystkiego na "zm.:=zm.+1" i dodaniu brakujących end'ów program całkowkcie się zawiesza. Przeniosłem cały kod mojego programu do Free Pascal'a i nie był on wstanie znaleźć modułu "crt" nawet gdy wrzuciłem do folderu "Units" wszystkie moduły z poprzedniego IDE i ręcznym wskazaniu ścieżki do nich. Sprawdzałem już przedwczoraj jakie wyniki wyrzuca mi program w konsoli (terminalu) i z tąd wiem jakie wyniki otrzymuję.

Co do debugger'a.. wciąż to rozgryzam.

0

Problem rozwiązany.
Okazało się bowiem iż wystarczyło użyć pętli "Repeat, Until" zamiast "For".

Tak teraz wygląda mój kod:

nameloop:=0;
licznik1 :=-16;
licznik2 :=-12;
licznik3 :=1;
licznik4 :=1;
repeat

writeln;
writeln(licznik1); {pokazuje stan licznikow}
writeln(licznik2);
writeln(licznik3);
writeln(licznik4);


repeat until KeyPressed; {obsluga ESC}
Ch := ReadKey;
if Ch = #27 then
begin
Writeln('All done.');
readkey;
end; {koniec obslugi ESC}

if (mapax[graczx-licznik1] = 1) and (mapay[graczy-licznik2] = 1) then
begin
bar(licznik3,licznik4,licznik3+20,licznik4+20);
end;

{przetwarzanie polozenia pol na ekranie}
licznik1 := licznik1+1;
if licznik1 = 17 then
begin
licznik2 := licznik2+1;
licznik1 := -16;
end;
if licznik2 = 13 then
begin
licznik1 := -16;
licznik2 := -12;
end;
{koniec przetwarzania polozenia pol na ektanie}
readkey;
until nameloop=0;
end.

Jedyne co tu pozostaje mi zrobić to sprawić by pętla wykonywała się do momentu użycia klawisza "ESC" a nie raz ale poza tym program prawidłowo wykonuje operacje matematyczne na zmiennych licznik(x). Obyło się bez debugger'@ 😏

0

@cerrato:
@furious programming:

Jest jakas regulacja formalna w standardzie pascala dot. zakazu modyfikacji zniennej indeksowej for ?
Dosłowne czytanie doniesień kolegi by na to wskazywało ???
Ja już od dawna odpadłem, i nie będę angażował energii w odbudowę skillów pasalowych

2

@ZrobieDobrze: Wydawało mi się, że tak, ale pewny nie byłem. Zresztą - to jest pytanie z kategorii czy wolno wsadzić toster do wanny gdy się kąpiesz - może i masz różnicówkę i nic się nie stanie, ale po co sprawdzać? Zysk żaden, totalnie bez sensu, za to mogą być problemy. Jeśli musisz stosować modyfikację zmiennej sterującej, znaczy że masz totalnie źle napisany kod/nie przemyślałeś algorytmu, albo struktura FOR nie jest odpowiednim wyborem, zamiast tego powinieneś zastosować jakiś while czy coś w ten deseń.

W każdym razie - tak na szybko sprawdziłem na Lazarusie i sytuacja wygląda tak, jak poniżej (czyli znaczy to, że dobrze pamiętałem). Może jakieś dyrektywy/zmiany trybu pracy kompilatora (typu zgodność z Delphi/tryb FPC) mogą to jakoś zmodyfikować.

screenshot-20230125113230.png

https://stackoverflow.com/questions/2071872/why-does-pascal-forbid-modification-of-the-counter-inside-the-for-block

EDIT
Jednak dobrze podejrzewałem, da się to wyłączyć/umożliwić grzebanie w wartości licznika pętli:
https://stackoverflow.com/questions/41598922/how-to-increment-counter-in-for-loop-pascal

Delphi does not allow modification of the loop variable, TP does. In its own modes FPC follows Delphi because that is the sane thing to do because of the reasons that MartynA listed frustrate optimization.
However FPC does allow it in Turbo Pascal mode for old codebases, add {$mode tp} or compile with -Mtp

3

@ZrobieDobrze: w obecnych standardach Pascala (czyli nie TP), już o dawna nie można dłubać w liczniku pętli for, bo iterator jest chroniony. Ale jest opcja awaryjna, bo można modyfikować jego wartość z poziomu wskaźnika przechowującego jego adres (z absolute to nie przejdzie), czyli taki backdoor:

{$mode objfpc}{$H+}

var
  Iterator: Integer;
  Backdoor: PInteger;
begin
  Backdoor := @Iterator;

  for Iterator := 0 to 10 do
  begin
    if Iterator = 3 then
      Backdoor^ := 8; // pointer daje bezpośredni dostęp do pamięci iteratora

    WriteLn(Iterator);
  end;
end.

Wyjście będzie zgodne z założeniami:

0
1
2
8
9
10

Sposób ten jest dozwolony i będzie działał poprawnie dla każdej pętli for, nawet jeśli za pomocą wskaźnika ustawimy wartość iteratora spoza zakresu. Czyli gdybyśmy w powyższym przykładzie zmienili jego wartość na 20, co jest wartością większą niż istniejące w nagłówku 10, to pętla sama w sobie nadal będzie działać prawidłowo. Natomiast to czy iterator o wartości 20 w tej pętli będzie poprawny czy nie, to już kwestia tego do czego zostanie użyty.

Ale czegoś takiego, czyli dłubania w iteratorze pętli for się nie robi, bo nie do tego ta pętla służy (tzn. nie tego oczekiwali twórcy FPC). Jeśli w ciele zapętlonego bloku, wartość iteratora ma się zmieniać dowolnie, to należy użyć pętli while lub repeat. I nie, nie ma nic złego w dowolnym modyfikowaniu wartości iteratora pętli while czy repeat — to nie jest żadna zła praktyka czy hacki. Są algorytmy, które tego wymagają, więc język musi zapewniać taką możliwość i te dwa rodzaje pętli ją zapewniają.


PS: bardziej mi się podoba zachowanie w standardzie Turbo Pascala, czyli możliwość bezpośredniej modyfikacji iteratora. W końcu to programista ma rządzić kodem, a nie kod nim. Ale to moja prywatna uwaga na ten temat — nie lubię silnego typowania i wszędobylskiego trzymania kodera za rączkę, żeby sobie przypadkiem ziaziu nie zrobił.

4

W końcu to programista ma rządzić kodem, a nie kod nim. Ale to moja prywatna uwaga na ten temat — nie lubię silnego typowania i wszędobylskiego trzymania kodera za rączkę, żeby sobie przypadkiem ziaziu nie zrobił.

A ja uważam zupełnie inaczej.

Dla mnie te mechanizmy ochronne są jak ABS/ESP, ostrzeganie o wyjechaniu poza pas ruchu, pilnowanie odległości od pojazdu poprzedzającego itp. w nowoczesnych samochodach. Może jakimś zawodowym/wyczynowym kierowcom będzie to przeszkadzać, ale takich mamy znikomy odsetek. Za to zdecydowanej większości kierowców wiele razy te mechanizmy ratowały tyłek.

Tak samo pilnowanie typów/ścisłe typowanie, blokada zmiany wartości zmiennej iterującej, pilnowanie zakresów tablic i tak dalej - raczej w niczym to nie przeszkadza, za to może mocno pomóc w wypadku jakiegoś błędu po stronie programisty. Po to mamy ESP w aucie, żeby korzystać, a nie jechać jak polonezem 40 lat temu :P

2

Ale czegoś takiego, czyli dłubania w iteratorze pętli for się nie robi, bo nie do tego ta pętla służy (tzn. nie tego oczekiwali twórcy FPC). Jeśli w ciele zapętlonego bloku, wartość iteratora ma się zmieniać dowolnie, to należy użyć pętli while lub repeat. I nie, nie ma nic złego w dowolnym modyfikowaniu wartości iteratora pętli while czy repeat — to nie jest żadna zła praktyka czy hacki. Są algorytmy, które tego wymagają, więc język musi zapewniać taką możliwość i te dwa rodzaje pętli ją zapewniają.


Mocne słowa. To by oznaczało, że w czystych językach funkcyjnych ( w których nie ma pętli i nawet zmiennych) nie da się zrobić pewnych algorytmów czyli nie są turing complete.
Z drugiej strony - to prawda (!!!). Część algorytmów została sformułowana zanim ktoś pomyślał, że będziemy pisać więć ich definicja troche nie ma sensu - np wymóg . sortowania "in place", gdzie samo sformułowanie "in - place" po prostu nie ma sensu w fp- relikt myślenia w kategoriach bitów - bajtów. Więc albo przyjmuje się mniej restrykcyjne definicje algorytmu, albo zapewnia (często ze względów wydajnościowych), że faktyczna implementacja jest in-place (albo na poziomie kompilatora, albo (częściej) na poziomie bibliotek).

0

@jarekr000000: tu nie chodzi o moją opinię, a o ograniczenia języka i jego wbudowane zabezpieczenia. Twórcy Free Pascala postanowili, że iterator pętli for ma być nietykalny z wnętrza ciała pętli. I to tyle — nie ma tu żadnej magii. W trybie kompatybilności z Turbo Pascalem, bezpośrednia modyfikacja iteratora jest dozwolona i to pozwala nie tylko zwiększyć czytelność pętli, ale też ograniczyć ilość kodu do naklepania.

Jest to absolutnie nieistotne czy użycję pętli for czy while/repeat, skoro implementuję dokładnie ten sam algorytm. Różnica polega jednak na tym, że w przypadku while/repeat muszę sam dbać o inicjalizację iteratora i jego końcową inkrementację, a sam zakres wartości, w których porusza się iterator, nie jest zbyt jawny. Natomiast w przypadku użycia pętli for, spodziewany zakres wartości iteratora mam wypisany w nagówku pętli (jest klarowny) i nigdzie sam nie muszę go inkrementować.

Niżej dwa przykłady ilustrujące w czym rzecz. Najpierw pętla while:

var
  I: Integer;

I := 0; // ręczna inicjalizacja iteratora

while I < 32 do // brak automatycznej inkrementacji
begin
  if {condition} then
    I += 3;

  if {condition} then
    I -= 1;

  I += 1; // ręczna końcowa inkrementacja iteratora
end;

i to samo, tylko że z pętlą for:

var
  I: Integer;

// inicjalizacja iteratora, widoczny zakres jego wartości, automatyczna inkrementacja
for I := 0 to 31 do
begin
  if {condition} then
    I += 3;

  if {condition} then
    I -= 1;
end;

W przypadku pętli for mam trzy razy mniej na głowie i dodatkowo wyższą czytelność.

1

To nie jest w kategoriach wolnosci czy jej braku, zupełnie gdzie indziej jest przestrzeń przeciwieństw. Raczej czystość i skuteczna gwarancja semantyki, niż spór bezpieczeństwo-wolnosć

a) pętla robi to "coś" w stosunku do materiału pętli "magią", a naprawdę nie magią, a semantyką języka -> wynika zakaz paprania. Tu podpada pascalowy for (ale for C), C# foreach itd
Jakbym robił kompilatory, to te rzeczy by były u mnie w body pętli "effective const" (indeks w pscalu, ukryty iterator w C#)

b) pętla robi coś manualne, np trzecią formułą pętli for C - rób co chcesz, a nasza propozycja (pętli z 10 strony podręcznika) wygląda właśnie i++

Pasalowy programista ma wolnosc użycia while / repeat , C# -owy tak samo ma inne rodzaje pętli.

0

Ten.. jeden problem rozwiązałem, pojawił się kolejny (https://4programmers.net/Forum/Delphi_Pascal/366219-nieprzerwana_petla_ktora_przerywa?p=1890921#id1890921) a wy dalej ciągniecie temat. Nie żeby coś ale to czy iterator w pętli for powinien być podatny na ingeręcje użytkownika to pojęcie względne tzn. po coś taka furtka powstała a przysłowiowy dom się od tego nie spali. Jak dla mnie spoko o ile nie utrudnia mi to pracy. Takie buty ⚙️

2
ZrobieDobrze napisał(a):

Pasalowy programista ma wolnosc użycia while / repeat , C# -owy tak samo ma inne rodzaje pętli.

Przecież dokładnie to napisałem w swoim poście, który w dodatku sam zaplusowałeś. Ło tu:

furious programming napisał(a):

Ale czegoś takiego, czyli dłubania w iteratorze pętli for się nie robi, bo nie do tego ta pętla służy (tzn. nie tego oczekiwali twórcy FPC). Jeśli w ciele zapętlonego bloku, wartość iteratora ma się zmieniać dowolnie, to należy użyć pętli while lub repeat. I nie, nie ma nic złego w dowolnym modyfikowaniu wartości iteratora pętli while czy repeat — to nie jest żadna zła praktyka czy hacki.

Więc po co tłumaczysz mi coś, co wiem i co sam opisałem? Czytajcie posty ze zrozumieniem. Pętla for we Free Pascalu (w nowszych trybach niż TP) nie pozwala na modyfikację iteratora, tak jest zaprogramowany kompilator i kropka. Jeśli iterator ma być modyfikowany (lub ma nie być go w ogóle) to się korzysta z while/repeat, tak jak opisałem.

Natomiast to co napisałem jako postscriptum, czyli to:

furious programming napisał(a):

PS: bardziej mi się podoba zachowanie w standardzie Turbo Pascala, czyli możliwość bezpośredniej modyfikacji iteratora. W końcu to programista ma rządzić kodem, a nie kod nim. Ale to moja prywatna uwaga na ten temat — nie lubię silnego typowania i wszędobylskiego trzymania kodera za rączkę, żeby sobie przypadkiem ziaziu nie zrobił.

to moje prywatne przemyślenia na temat tego, co chciałbym mieć w języku, aby mi się pracowało wygodnie, abym nie czuł się skrępowany i żebym nie musiał walczyć z językiem. To co mogłem to sobie w obecnym projekcie powyłączałem odpowiednimi dyrektywami (np. silne typowanie pointerów, wyjątki, automatyczną dereferencję itp.), aby kompilator przestał się wymądrzać i przeszkadzać mi w pisaniu kodu. I nie, nie użeram się ani z błędami, ani z wyciekami, a przy okazji nie użeram się z samym językiem (w większości przypadków).

Podsumowując, jeśli potrzebujecie aby wam kompilator palcem pokazywał co macie robić to spoko, nie widzę problemu — jeśli uważacie to za pomocne, praktyczne. Ja tego nie potrzebuję, mam inne preferencje, nie potrzebuję indoktrynacji w tym temacie.

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