7. Statistik, bagian 2: laporan (stats_report_line, stats_report_sampst, stats_report)

Di akhir simulasi (menit 4800), main memanggil report, yang memanggil stats_report. Tugasnya: membaca angka yang sudah dikumpulkan hook, lalu mencetaknya sebagai satu tabel.

Tiga fungsi bekerja berlapis:

 stats_report            (mencetak seluruh tabel (a)-(f))
    ├─ stats_report_sampst    (siapkan satu baris dari laci sampst)
    │      └─ stats_report_line     (cetak satu baris)
    └─ stats_report_line      (cetak satu baris, untuk (a) dan (c))

Kita bahas dari lapisan paling bawah.

7.1 stats_report_line: mencetak satu baris tabel

src/bus-sim.c baris 562-573

void stats_report_line(FILE *out, const char *label, double avg, double max,
                       double min, int max_is_count)
{
    char avg_text[32], max_text[32], min_text[32];
 
    if (avg < 0.0) sprintf(avg_text, "-"); else sprintf(avg_text, "%.3f", avg);
    if (max < 0.0) sprintf(max_text, "-");
    else sprintf(max_text, max_is_count ? "%.0f" : "%.3f", max);
    if (min < 0.0) sprintf(min_text, "-"); else sprintf(min_text, "%.3f", min);
 
    fprintf(out, "%-42s | %9s | %9s | %9s\n", label, avg_text, max_text, min_text);
}

Baris demi baris

Baris kodeSintaksLogika
void stats_report_line(FILE *out, const char *label, double avg, double max, double min, int max_is_count)enam parametermenerima tujuan cetak, nama baris, tiga angka (rata-rata, maks, min), dan penanda
char avg_text[32], max_text[32], min_text[32];tiga larik karakter masing-masing 32tempat menyusun teks tiap sel tabel
if (avg < 0.0) sprintf(avg_text, "-"); else sprintf(avg_text, "%.3f", avg);if ... else, tanpa kurung kurawalbila avg negatif, sel diisi -; selain itu diisi angka tiga desimal
if (max < 0.0) sprintf(max_text, "-");bila max negatif, sel diisi -
else sprintf(max_text, max_is_count ? "%.0f" : "%.3f", max);operator ? : memilih teks formatselain itu: bila max_is_count benar pakai %.0f (bilangan bulat), bila tidak %.3f
if (min < 0.0) sprintf(min_text, "-"); else sprintf(min_text, "%.3f", min);seperti avg
fprintf(out, "%-42s | %9s | %9s | %9s\n", label, avg_text, max_text, min_text);cetak satu barislabel rata kiri 42 lebar, lalu tiga sel rata kanan 9 lebar, dipisah |

Kenapa nilai negatif berarti ”-”

Semua angka di tabel ini (waktu, jumlah orang) tidak pernah negatif. Jadi nilai -1.0 aman dipakai sebagai tanda “tidak ada nilai”. Pemanggil cukup mengirim -1.0, dan fungsi ini yang mengubahnya menjadi -. Dengan begitu satu fungsi melayani semua baris, baik yang punya min (d, e, f) maupun yang tidak (a, b, c).

Kenapa max_is_count

Maksimum antrian dan jumlah di bus adalah jumlah orang, jadi lebih baik tampil 27 daripada 27.000. Parameter max_is_count = 1 memilih format %.0f untuk itu; 0 memakai %.3f untuk waktu.

Kenapa lewat sprintf dulu, baru fprintf

sprintf menyusun teks setiap sel dulu ke larik (avg_text dan kawan-kawan) sehingga bisa memilih antara - dan angka. Baru kemudian fprintf mencetak keempat teks itu sekaligus dengan lebar kolom yang rata. Kalau langsung mencetak angka, tidak bisa menyisipkan -.

Contoh jalan (menelusuri kode dengan angka contoh)

PemanggilanHasil baris
stats_report_line(out, "[b] Delay ...", 31.414, 91.934, -1.0, 0)[b] Delay ... | 31.414 | 91.934 | -
stats_report_line(out, "[a] Number ...", 7.368, 27.0, -1.0, 1)[a] Number ... | 7.368 | 27 | -
stats_report_line(out, label, -1.0, -1.0, -1.0, 0)ketiga sel - (dipakai bila belum ada data)

7.2 stats_report_sampst: menyiapkan satu baris dari laci sampst

src/bus-sim.c baris 575-590

/* A row from a sampst variable; show_min = 0 prints "-" for the minimum. */
void stats_report_sampst(FILE *out, const char *label, int variable, int show_min)
{
    double count, avg, max, min;
 
    sampst(0.0, -variable);
    avg   = transfer[1];
    count = transfer[2];
    max   = transfer[3];
    min   = transfer[4];
 
    if (count == 0.0)
        stats_report_line(out, label, -1.0, -1.0, -1.0, 0);
    else
        stats_report_line(out, label, avg, max, show_min ? min : -1.0, 0);
}

Baris demi baris

Baris kodeSintaksLogika
void stats_report_sampst(FILE *out, const char *label, int variable, int show_min)parameter variable = nomor laci; show_min = 0 atau 1menerima nomor laci mana yang akan dibaca dan apakah minimum ditampilkan
double count, avg, max, min;empat variabel double dalam satu deklarasitempat menyalin hasil laporan; harus di awal fungsi (aturan C89)
sampst(0.0, -variable);tanda minus di depan variableminta laporan laci itu; angka pertama (0.0) diabaikan
avg = transfer[1];transfer larik milik SIMLIBambil rata-rata
count = transfer[2];ambil jumlah observasi
max = transfer[3];ambil maksimum
min = transfer[4];ambil minimum
if (count == 0.0)== membandingkanbila belum ada observasi
stats_report_line(out, label, -1.0, -1.0, -1.0, 0);tiga -1.0cetak baris berisi tanda - saja
else … stats_report_line(out, label, avg, max, show_min ? min : -1.0, 0);show_min ? min : -1.0selain itu cetak angkanya; bila show_min benar kolom Min diisi min, bila salah diisi -1.0 (jadi -)

Hal penting

  • transfer[] harus disalin segera. Setiap panggilan SIMLIB berikutnya menimpanya. Karena itu empat angka langsung dipindah ke variabel lokal sebelum memanggil stats_report_line.
  • Urutan isi hasil sampst: [1] rata-rata, [2] jumlah, [3] maksimum, [4] minimum. Indeks tertukar menghasilkan angka salah tanpa pesan error apa pun.
  • Kenapa ada pengecekan count == 0.0: kalau laci kosong, SIMLIB mengisi maks dan min dengan angka sentinel 1e30 (sangat besar) yang tidak boleh dicetak apa adanya. Jadi baris dicetak dengan -.
  • Kenapa show_min: soal hanya meminta rata-rata dan maksimum untuk delay (b), jadi kolom Min diberi - (show_min = 0). Untuk (d), (e), (f) soal meminta ketiganya, jadi show_min = 1.

7.3 stats_report: mencetak seluruh tabel

src/bus-sim.c baris 592-634

/* Prints (a)-(f) as one table. */
void stats_report(FILE *out)
{
    int    loc;
    char   label[64];
    double avg, max;
 
    fprintf(out, "Locations: 1 = %s, 2 = %s, 3 = %s\n",
            locations[1].name, locations[2].name, locations[3].name);
    fprintf(out, "All times are in minutes\n\n");
    fprintf(out, "%-42s | %9s | %9s | %9s\n", "", "Average", "Max", "Min");
 
    for (loc = 1; loc <= NUM_LOCATIONS; ++loc) {
        sprintf(label, "%sNumber in queue at location %d", loc == 1 ? "[a] " : "    ", loc);
        filest(LIST_QUEUE(loc));
        avg = transfer[1];
        max = transfer[2];
        stats_report_line(out, label, avg, max, -1.0, 1);
    }
 
    for (loc = 1; loc <= NUM_LOCATIONS; ++loc) {
        sprintf(label, "%sDelay in queue at location %d", loc == 1 ? "[b] " : "    ", loc);
        stats_report_sampst(out, label, SAMPST_QUEUE_DELAY(loc), 0);
    }
 
    timest(0.0, -TIMEST_NUM_ON_BUS);
    avg = transfer[1];
    max = transfer[2];
    stats_report_line(out, "[c] Number on the bus", avg, max, -1.0, 1);
 
    for (loc = 1; loc <= NUM_LOCATIONS; ++loc) {
        sprintf(label, "%sBus stop time at location %d", loc == 1 ? "[d] " : "    ", loc);
        stats_report_sampst(out, label, SAMPST_STOP_TIME(loc), 1);
    }
 
    stats_report_sampst(out, "[e] Bus loop time", SAMPST_LOOP_TIME, 1);
 
    for (loc = 1; loc <= NUM_LOCATIONS; ++loc) {
        sprintf(label, "%sTime in system, arrived at location %d",
                loc == 1 ? "[f] " : "    ", loc);
        stats_report_sampst(out, label, SAMPST_TIME_IN_SYSTEM(loc), 1);
    }
}

Kepala tabel

Baris kodeLogika
int loc; char label[64]; double avg, max;penghitung lokasi, tempat menyusun nama baris, dan tempat menyalin hasil
fprintf(out, "Locations: 1 = %s, 2 = %s, 3 = %s\n", locations[1].name, ...)cetak daftar lokasi; tiap %s diisi satu nama dari tabel locations
fprintf(out, "All times are in minutes\n\n");keterangan satuan; \n\n menyisakan satu baris kosong
fprintf(out, "%-42s | %9s | %9s | %9s\n", "", "Average", "Max", "Min");judul kolom; sel pertama teks kosong selebar 42 agar sejajar dengan label baris

Pola yang berulang: menyusun nama baris

sprintf(label, "%sNumber in queue at location %d", loc == 1 ? "[a] " : "    ", loc);
BagianArti
%s pertamadiisi hasil loc == 1 ? "[a] " : " ": tanda [a] bila loc 1, empat spasi bila bukan
Number in queue at locationteks tetap
%ddiisi loc (1, 2, atau 3)

Hasilnya [a] Number in queue at location 1, lalu Number in queue at location 2 (diawali empat spasi), dan seterusnya. Tanda [a] hanya di baris pertama kelompoknya.

Statistik (a): jumlah di antrian

KodeLogika
for (loc = 1; loc <= NUM_LOCATIONS; ++loc) { ... }ulangi untuk lokasi 1, 2, 3
filest(LIST_QUEUE(loc));minta SIMLIB melaporkan panjang list antrian lokasi itu dari waktu ke waktu
avg = transfer[1]; max = transfer[2];rata-rata terhadap waktu dan maksimum (urutan isi filest: [1] rata-rata, [2] maks, [3] min)
stats_report_line(out, label, avg, max, -1.0, 1);cetak; -1.0 = kolom Min tidak diminta, 1 = maksimum dicetak sebagai bilangan bulat

Statistik (b): delay antrian

stats_report_sampst(out, label, SAMPST_QUEUE_DELAY(loc), 0);

Satu baris per lokasi dari laci sampst nomor loc. Angka 0 di ujung berarti kolom Min tidak dicetak.

Statistik (c): jumlah di bus

KodeLogika
timest(0.0, -TIMEST_NUM_ON_BUS);minta laporan laci timest (tanda minus)
avg = transfer[1]; max = transfer[2];urutan isi timest: [1] rata-rata terhadap waktu, [2] maks, [3] min
stats_report_line(out, "[c] Number on the bus", avg, max, -1.0, 1);satu baris; maks sebagai bilangan bulat

Hanya satu baris karena hanya ada satu bus.

Statistik (d), (e), (f)

stats_report_sampst(out, label, SAMPST_STOP_TIME(loc), 1);
stats_report_sampst(out, "[e] Bus loop time", SAMPST_LOOP_TIME, 1);
stats_report_sampst(out, label, SAMPST_TIME_IN_SYSTEM(loc), 1);

Polanya sama dengan (b); bedanya angka 1 (kolom Min ikut dicetak) dan nomor laci. (d) satu baris per lokasi, (e) satu baris untuk putaran, (f) satu baris per lokasi asal.

Dua urutan isi hasil yang berbeda

Fungsi pembacatransfer[1]transfer[2]transfer[3]transfer[4]
sampst(0.0, -var)rata-ratajumlahmaksmin
timest(0.0, -var) dan filest(list)rata-rata terhadap waktumaksmin(tidak ada)

Itu sebabnya stats_report_sampst membaca [2] sebagai jumlah dan [3] sebagai maks, sedangkan bagian (a) dan (c) di stats_report membaca [2] sebagai maks.

Kenapa stats_report aman dipanggil dua kali

main memanggil report dua kali (layar dan berkas). Meminta laporan tidak mengubah angka, jadi kedua hasil identik.