Dua program Pascal membaca dua skor yang sama, 7 dan 8, lalu menghitung rata-ratanya. Satu mencetak mean: 7. Yang lain mencetak mean: 7.50. Keduanya dikompilasi tanpa satu keluhan pun, keduanya berjalan tanpa crash, dan tidak ada yang berbohong tentang apa yang kamu tulis. Tiap program membawa salinan sendiri dari fungsi rata-rata itu, dan salinannya tidak sama. Memperbaiki satu salinan membiarkan yang lain rusak, dan tidak ada yang memberitahu kamu bahwa keduanya sudah bercabang. Unit memperbaiki ini dengan memberi fungsi itu tepat satu badan.
Poin Penting
divmembuang sisanya, jadi(7 + 8) div 2bernilai 7 sementara(7 + 8) / 2bernilai 7.5.- Unit punya dua bagian:
interfaceadalah yang boleh dipanggil program lain,implementationadalah badan yang hanya dilihat unit itu sendiri. - Fungsi yang hanya hidup di
implementationlolos dikompilasi di dalam unit dan gagal di pemanggilnya denganError: Identifier not found. - Free Pascal mengompilasi unit yang ditemukannya di sebelah program dan meninggalkan berkas
.ppu; dari subfolder flag-nya-Fulib, menempel, karena-Fu libdengan spasi tidak menemukannya. - Nama unit dicari sebagai nama berkas, jadi
unit stats;harus tinggal distats.pas.
Sisa pembagian yang menghilang
Mulai dari aritmetikanya, karena bug-nya tinggal di sana. Pascal punya dua operator pembagian, dan keduanya menjawab pertanyaan yang berbeda.
program divide;
var
a, b: integer;
begin
a := 7;
b := 8;
writeln('(a + b) div 2 = ', (a + b) div 2);
writeln('(a + b) / 2 = ', (a + b) / 2:0:1);
writeln('unformatted = ', (a + b) / 2);
end.
$ fpc divide.pas && ./divide
(a + b) div 2 = 7
(a + b) / 2 = 7.5
unformatted = 7.5000000000000000E+000
div adalah pembagian bulat, jadi ia membuang sisanya dan (7 + 8) div 2 bernilai 7. / selalu menghasilkan bilangan real, jadi jumlah yang sama memberi 7.5. Baris terakhir menunjukkan bagaimana Pascal mencetak sebuah real mentah: notasi ilmiah, dengan satu spasi di depan. Ingat-ingat itu, karena kamu akan memakainya nanti.
Berikut program yang mencetak jawaban pendek tadi. Mean menerima integer dan memakai div, jadi 7.5 tidak pernah ada.
program average;
var
a, b: integer;
function Mean(x, y: integer): integer;
begin
Mean := (x + y) div 2;
end;
begin
a := 7;
b := 8;
writeln('scores: ', a, ' and ', b);
writeln('mean: ', Mean(a, b));
end.
$ fpc average.pas && ./average
scores: 7 and 8
mean: 7
Ini salinan pertama. Program mana pun yang menempel fungsi ini mewarisi pemotongan itu, dan program mana pun yang menempel versi / mendapat jawaban yang lain.
Unit punya dua bagian
Unit adalah berkas yang dibelah dua kata kunci. Semua yang ada di bawah interface adalah kontrak: nama, parameter, dan tipe kembalian yang boleh dipanggil program lain. Semua yang ada di bawah implementation adalah badan, kode yang melakukan pekerjaan, hanya terlihat di dalam unit. Berikut isi stats.pas, yang mengambil alih fungsinya.
unit stats;
interface
function Mean(x, y: real): real;
function Format2(n: real): string;
implementation
function Mean(x, y: real): real;
begin
Mean := (x + y) / 2;
end;
function Format2(n: real): string;
begin
Str(n:0:2, Format2);
end;
end.
Mean sekarang menerima dan mengembalikan real serta memakai /. Tiap signature muncul dua kali, satu per bagian: baris interface menjanjikan, baris implementation memenuhinya. Format2 membungkus Str(n:0:2, ...), yang menulis sebuah real dengan dua desimal ke dalam string, jadi pemanggilnya mendapat 7.50 dan bukan notasi ilmiah. Sebuah program memakainya dengan menambahkan satu baris, uses stats;, tepat setelah baris program.
Kompilator membangunnya untukmu
program average;
uses stats;
var
a, b: integer;
begin
a := 7;
b := 8;
writeln('scores: ', a, ' and ', b);
writeln('mean: ', Format2(Mean(a, b)));
end.
$ fpc average.pas && ./average
scores: 7 and 8
mean: 7.50
Tidak ada perintah terpisah untuk unit itu. Saat kompilator mencapai uses stats;, ia mencari stats.pas di sebelah program dan mengompilasinya sekalian. Sesudahnya, stats.o dan stats.ppu tersisa di samping program. Berkas .ppu adalah bentuk terkompilasi unit yang dihasilkan Free Pascal.
Tanpa flag, kompilator hanya menemukan unit itu kalau berkasnya berada di sebelah program. Pindahkan stats.pas ke folder bernama lib, dan proses build berhenti:
$ fpc main.pas
main.pas(3,6) Fatal: Can't find unit stats used by main
Fatal: Compilation aborted
Menambahkan flag dengan spasi tidak mengubah apa pun: fpc -Fu lib main.pas mencetak dua baris yang sama. Flag-nya harus menempel pada path-nya, jadi fpc -Fulib main.pas berhasil dikompilasi, dan program mencetak scores: 7 and 8 serta mean: 7.50.
Namanya juga penting. Kompilator mengubah uses stats; menjadi sebuah nama berkas. Simpan unit stats; sebagai stat2.pas, dan program yang memakainya tidak bisa menemukannya:
$ fpc prog2.pas
prog2.pas(3,6) Fatal: Can't find unit stats used by prog2
Fatal: Compilation aborted
Jadi unit stats; harus tinggal di stats.pas. Perbaikannya ada di nama berkas, bukan di kodenya.
Apa yang dijaga interface
Dua bagian itu bukan hiasan. Misalkan sebuah unit hidden.pas tidak mendeklarasikan apa pun di interface dan mengimplementasikan Mean di bawah implementation. Unitnya berhasil dikompilasi. Program uses_hidden.pas yang menulis uses hidden; dan memanggil Mean tidak:
$ fpc uses_hidden.pas
uses_hidden.pas(6,21) Error: Identifier not found "Mean"
uses_hidden.pas(8) Fatal: There were 1 errors compiling module, stopping
Fatal: Compilation aborted
Mean ada di dalam berkas itu, dan pemanggilnya tetap tidak bisa melihatnya, karena implementation bersifat privat bagi unit. Untuk membagikan sebuah fungsi, taruh header-nya di interface. Untuk menyimpan sebuah helper tetap privat, biarkan di luar.
Dua berkas, satu perintah
Memeriksa keluaran dengan mata lama-lama melelahkan, jadi serahkan pekerjaan itu ke program kedua. check_average.pas membaca baris dari standard input dan membandingkan dua baris pertama dengan teks yang kamu harapkan.
program check_average;
var
expected: array[1..2] of string;
line: string;
i, passed, failed: integer;
begin
expected[1] := 'scores: 7 and 8';
expected[2] := 'mean: 7.50';
passed := 0;
failed := 0;
i := 0;
while not eof do
begin
readln(line);
i := i + 1;
if i <= 2 then
begin
if line = expected[i] then
begin
writeln('PASS line ', i, ': ', expected[i]);
passed := passed + 1;
end
else
begin
writeln('FAIL line ', i, ': expected "', expected[i], '", got "', line, '"');
failed := failed + 1;
end;
end;
end;
if i < 2 then
begin
writeln('FAIL the program printed ', i, ' lines, expected 2');
failed := failed + 1;
end;
writeln;
writeln(passed, ' of 2 checks passed');
if failed > 0 then
ExitCode := 1;
end.
Build sekali dengan fpc check_average.pas. Setelah itu seluruh pemeriksaan jadi satu perintah, ./average | ./check_average, di mana pipa mengirim apa yang dicetak average ke pemeriksa. Build average.pas versi awal dari bagian pertama dan jalankan:
$ ./average | ./check_average
PASS line 1: scores: 7 and 8
FAIL line 2: expected "mean: 7.50", got "mean: 7"
1 of 2 checks passed
Pemeriksa menyebut nomor barisnya dan menunjukkan kedua string. Sekarang ganti average.pas ke versi yang memakai stats, build ulang, dan jalankan perintah yang sama:
$ ./average | ./check_average
PASS line 1: scores: 7 and 8
PASS line 2: mean: 7.50
2 of 2 checks passed
Pemeriksa yang sama, perintah yang sama, jawaban baru.
Latihan. Di versi unit dari average.pas, ubah writeln terakhir menjadi writeln('mean: ', Mean(a, b)); dan buang Format2. Perkirakan dulu apa yang akan kamu lihat, lalu jalankan pemeriksanya. Baris itu menjadi mean: 7.5000000000000000E+000, dengan dua spasi setelah titik dua, dan gagal di baris 2. Itulah alasan Format2 ada.
Ketika dua program butuh perhitungan yang sama, tulis sekali di sebuah unit dan biarkan keduanya uses unit itu, karena salinan yang ditempel adalah bug yang menunggu suntingan pertama.