Kurs języka Object/Delphi Pascal na bazie implementacji Free Pascal. autor Łukasz Stafiniak Email: lukstafi@gmail.com, lukstafi@ii.uni.wroc.pl Web: www.ii.uni.wroc.pl/~lukstafi Jeśli zauważysz błędy na slajdach, proszę daj znać! Wykład 2: Dane i instrukcje Pierwotne typy danych i manipulacja nimi; składanie danych razem: rekordy, tablice. 1
Diagramy składniowe Do opisu składni Pascala tradycyjnie używa się graficznej reprezentacji notacji EBNF. [patrz Language reference guide] 2
Pliki źródłowe Pascala Jak w większości języków, w leksyce mamy słowa zastrzeżone (kluczowe), identyfikatory, operatory, znaki odstępu ( białe znaki ), stałe, komentarze. Pascal jest niewrażliwy na wielkość liter. Komentarze: (* This is an old style comment *) { This is a Turbo Pascal comment } // This is a Delphi comment. All is ignored till the end of the line. Komentarze można zagnieżdżać. Rozszerzenia dla plików źródłowych:.pas. Standardowe..pp. Używane m.in. przez źródła Free Pascal Compiler (FPC) dla plików niekompatybilnych z Delphi, nie polecane..lpr. Lazarus PRoject. Można używać dla programów, żeby odróżnić je od modułów. 3
Tryby Free Pascala i dyrektywy kompilatora {$mode fpc}. Tryb domyślny. Nie polecany! W zasadzie jest to Turbo Pascal z drobnymi rozszerzeniami. {$mode objfpc}. Tryb Object Pascal. Polecany, tego będziemy zawsze używać. (Z wiersza poleceń: -Mobjfpc.) {$mode delphi}. Tryb zgodności z Delphi. Pomimo że Delphi było pionierem wprowadzania cech językowych z popularnych języków obiektowych (C++, Java) do Object Pascala, część konstrukcji Free Pascala pojawiła się w Delphi później i z inną składnią, lub wcale. {$modeswitch objectivec2}. Pod-tryb Objective-Pascal naśladujący język Objective-C, do pracy z Mac OS. {$H+}. Interpretuje String jako AnsiString, zamiast ShortString. Polecane. Oryginalnie stringi w Pascalu miały długość maksymalnie 255 (obecny ShortString). Ciągle można używać stringów o zadanej długości String[N] z N 255. (Także: {$LONGSTRINGS ON}. Z wiersza poleceń: -Sh.) 4
{$COPERATORS ON}. Pozwól na operatory przypisania jak w C: +=, -=, *=, /=. (Z wiersza poleceń: -Sc.) {$define Symbol}, {$ifdef Symbol}, {$else}, {$endif}. Kompilacja warunkowa. Inne dyrektywy: {$ifndef Symbol}, {$if COND}, {$elseif COND}. Żeby zdefiniować Symbol z linii poleceń, przekaż do fpc parametr -dsymbol. -Ciort. Parametry fpc generuj testy: -Ci wejścia/wyjścia, -Co przepełnienia, -Cr zakresów tablic i typów podzakresowych, -Ct stosu. -gl. Parametr fpc włączający lineinfo dodaje pozycje w pliku do stacktrace. -viwn. Parametr fpc wyświetl info, warnings and notes. (Zazwyczaj są już w pliku fpc.cfg.) Domyślne argumenty dla fpc można umieścić w pliku ~/fpc.cfg, pod Linuxem patrz też w pliku /etc/fpc.cfg. Różnice pomiędzy Free Pascalem (w trybie objfpc) i Delphi omówimy jak poznamy już większość języka. Wykład zakłada implementację Free Pascal 2.6.0. 5
Typy bazowe Type Range Size in bytes Byte 0.. 255 1 Shortint -128.. 127 1 Smallint -32768.. 32767 2 Word 0.. 65535 2 Integer either smallint or longint size 2 or 4 Cardinal longword 4 Longint -2147483648.. 2147483647 4 Longword 0.. 4294967295 4 Int64-9223372036854775808.. 8 9223372036854775807 QWord 0.. 18446744073709551615 8 Typy całkowitoliczbowe Czytamy o typach w Free Pascal: Reference guide. 6
Przegląd operatorów, instrukcji i procedur Typy wbudowane System System math WriteLn Sin Cos min degtorad Integer Real ReadLn Ln pi Exp max norm Longint String ParamStr Sqrt Power sum randg Char Boolean ParamCount Random Round tanh power Sterujące Typ boolowski Typ napisów Przypisanie := if...then...else... + Ala Zbiory: >< for...:=...to...do... true false Length + - * include while...do... and or SetLength in <= exclude repeat...until... not xor Str Dzielenie całk. div for...in...do... Val Modulo mod A : Array[1..10] of String; ^ spod wskaź. s[i] :=! ; B : Array of Integer; @ pobierz adres SetLength (B, 1000); ** tylko dostępny do przeciążania 7
Czytamy Reference guide: typy,wyrażenia i instrukcje Czytamy Run Time Library: moduły System i math 8
Przykłady programów program RulerProg; var ruler : String = 1 ; ruler := ruler + 2 + ruler; ruler := ruler + 3 + ruler; ruler := ruler + 4 + ruler; WriteLn (ruler) end. 9
program IntOps; var a, b : Integer; sum, prod, quot, rem : Integer; ParseCodeA, ParseCodeB : Word; Val (ParamStr (1), a, ParseCodeA); Val (ParamStr (2), b, ParseCodeB); if ParseCodeA + ParseCodeB = 0 then sum := a + b; prod := a * b; quot := a div b; rem := a mod b; WriteLn (a, +, b, =, sum); WriteLn (a, *, b, =, prod); WriteLn (a, /, b, =, quot); WriteLn (a, %, b, =, rem) end else WriteLn ( Error in a? Character, ParamStr(1)[ParseCodeA]); WriteLn ( Error in b? Character, ParamStr(2)[ParseCodeB]) end end. 10
program Quadratic; var b, c, d, root1, root2 : Real; ParseCode : Word; // parse coefficients from command line Val (ParamStr (1), b, ParseCode); if ParseCode <> 0 then exit; Val (ParamStr (2), c, ParseCode); if ParseCode <> 0 then exit; // calculate roots d := b*b - 4*c; if d >= 0 then d := sqrt (d); root1 := (-b + d) / 2; root2 := (-b - d) / 2; WriteLn ( root1 =, root1 : 10:2); WriteLn ( root2 =, root2 : 10:2) end else WriteLn ( determinant =, d : 2:2) end. 11
program LeapYear; var Year : Integer; IsLeapYear : Boolean; ParseCode : Word; Val (ParamStr (1), Year, ParseCode); if ParseCode <> 0 then exit; IsLeapYear := (Year mod 4 = 0) and (Year mod 100 <> 0); IsLeapYear := IsLeapYear or (Year mod 400 = 0); WriteLn (IsLeapYear) end. 12
program SquareRoot; const epsilon : Real = 1e-15; var c, t : Real; ParseCode : Word; Val (ParamStr (1), c, ParseCode); if ParseCode <> 0 then exit; t := c; while abs (t - c/t) > t*epsilon do t := (c/t + t) / 2; WriteLn (t : 0:2) end. Stała otypowana mogą być dowolnego typu (ale muszą być statycznie stałe ). 13
program Gambler; var stake, goal, T, wins, experiment, cash : Integer; ParseCode : Word; Val (ParamStr (1), stake, ParseCode); if ParseCode <> 0 then exit; Val (ParamStr (2), goal, ParseCode); if ParseCode <> 0 then exit; Val (ParamStr (3), T, ParseCode); if ParseCode <> 0 then exit; wins := 0; for experiment := 1 to T do cash := stake; Musieliśmy zadeklarować cash od razu. while (cash > 0) and (cash < goal) do Random if random < 0.5 then Inc (cash) bez argumentu daje Real 0 i <1, else Dec (cash); random(7) dałoby Integer w 0..6. if cash = goal then Inc (wins) Inc i Dec zamiast C++/--. end; WriteLn (wins, wins of, T) end. 14
program ShuffleDeck; Napisany pod górkę, żeby pokazać type różne konstrukcje typów wyliczeniowych, podzakresów, rekordów. Suit = (Clubs := 0, Diamonds, Hearts, Spades); Wyliczenie i tak zacząłby się od zera. LowRank = 2..10; Court = (Jack := High (LowRank)+1, Queen, King, Ace); Przesuwamy początek Card = record żeby zakresy Ord były rozłączne. suit : Suit; case IsFace : Boolean of Pole rekordu po którym poznamy True: (face : Court); którego zestawu pól używać. False: (rank : LowRank) Rekord używający case będę nazywać end; rekordem z wariantami. const SuitNames : array[clubs..spades] of String Stała tablica indeksowana = ( Trefl, Karo, Kier, Pik ); typem wyliczeniowym. FaceNames : array[jack..ace] of String = ( Walet, Dama, Król, As ); N = Ord zwraca liczbę porządkową wartości wyliczeniowej. (Ord (High (Court)) - Ord (Low (LowRank)) + 1) * 4-1; var Deck : array[0..n] of Card; Gdy nie ma powodu zaczynać od 1 to zaczynamy od 0 r, i, pos : Integer; (bo tablice dynamiczne zawsze zaczynają się od 0). s : Suit; Wszystkie rekordy tablicy Deck i rekord zmiennej c c : Card; są od razu przydzielone na stosie. 15
for s in Suit do Pętla po typie wyliczeniowym. for r in [Low (LowRank)..Ord (High (Court))] do Pętla po zbiorze wartości. with Deck[(r - Low (LowRank))*4 + Ord (s)] do Zapisujemy pola rekordu. suit := s; IsFace := (r >= Ord (Low (Court))); rank := r end Dzięki zapewnionej zgodności reprezentacji end; zapisujemy za jednym zamachem rank i face. for i := 0 to N do pos := i + random (N-i+1); c := Deck[pos]; Deck[pos] := Deck[i]; Deck[i] := c end; for i := 0 to N do if ParamStr(1) = pl then if Deck[i].IsFace then WriteLn (FaceNames[Deck[i].face],, SuitNames[Deck[i].suit]) else WriteLn (Deck[i].rank,, SuitNames[Deck[i].suit]); end else if Deck[i].IsFace then Przypadki rekordu z wariantami. WriteLn (Deck[i].face, of, Deck[i].suit) Zazwyczaj użylibyśmy else WriteLn (Deck[i].rank, of, Deck[i].suit); instrukcji case...of... end ale tu warianty są po typie boolowskim. end. 16
http://imgs.xkcd.com/comics/donald_knuth.png 17
program MatrixMult; {$COPERATORS ON} type TMatrix = array of array of Real; var M, N : Integer; ParseCode : Word; A,B,C : TMatrix; i,j,k : Integer; Val (ParamStr(1), N, ParseCode); if ParseCode <> 0 then exit; Val (ParamStr(2), M, ParseCode); if ParseCode <> 0 then exit; SetLength (A, M, N); SetLength (B, N, M); SetLength (C, M, M); {code to fill A and B here} for i := 0 to M-1 do for j := 0 to M-1 do C[i,j] := 0; for k := 0 to N-1 do C[i,j] += a[i,k] * b[k,j]; end end. 18
program SelfAvoidingWalk; var N, T : Integer; DeadEnds : Integer; walk, x, y : Integer; A : array of array of Boolean; ParseCode : Word; DeadEnd : Boolean; r : Real; Val (ParamStr (1), N, ParseCode); if ParseCode <> 0 then exit; Val (ParamStr (2), T, ParseCode); if ParseCode <> 0 then exit; SetLength (A, N, N); DeadEnds := 0; for walk := 1 to T do for x := 0 to N-1 do for y := 0 to N-1 do A[x,y] := false; x := N div 2; y := N div 2; DeadEnd := false; while not DeadEnd and (x > 0) and (x < N-1) and (y > 0) and (y < N-1) do A[x,y] := true; {Check for dead end and make a random move.} if A[x-1,y] and A[x+1,y] and A[x,y-1] and A[x,y+1] then Inc (DeadEnds); DeadEnd := true end Czyścimy planszę pod następny spacer. Zamiast break używamy flagi kontrolującej opuszczenie pętli. Porównania muszą być w nawiasach. Kontynuacja na następnej stronie. 19
else r := random; if r < 0.25 then if not A[x+1,y] then Inc(x); end else if r < 0.50 then if not A[x-1,y] then Dec(x); end else if r < 0.75 then if not A[x,y+1] then Inc(y); end else if r < 1.00 then if not A[x,y-1] then Dec(y); end end end end; WriteLn ((100*DeadEnds) div T, % dead ends ) end. Pamiętaj że przy wypróbowywaniu kierunku tutaj nie zawsze robimy krok. 20