7 Evaluasi Ekspresi dan Persamaan
8.1 Penggunaan Type Guards
Seperti di manapun bahasa pemrograman, perancangan/design hati-hati dari struktur data application-dependent adalah salah satu perhatian yang utama manakala mengembangkan scripts Q yang pergi di luar numeric sederhana, pemrosesan string and list. Sebagai contoh khas untuk suatu struktur data non-list yang memainkan suatu peran terkemuka di dalam banyak script Q, mari kita mempertimbangkan pohon pencarian biner (binary search trees), yang merupakan suatu alat-alat menyenangkan untuk menerapkan sets, bags, dictionaries dan semacamnya. Kita akan menggunakan struktur data ini sebagai contoh yang berjalan (a running example) sepanjang bab ini.
Suatu pilihan dari constructors/pembangun untuk pohon biner adalah berikut:
public const nil, bin X T1 T2;
Fakta bahwa nil dan bin kepunyaan type pohon biner (binary tree), sebagai ganti deklarasi di atas kita menggunakan suatu deklarasi type seperti berikut, yang mana, di samping mendeklarasikan simbol pembangun/constructor, juga memperkenalkan symbol type BinTree:
public type BinTree = const nil, bin X T1 T2;
Deklarasi ini memberitahukan kepada interpreter bahwa masing-masing ungkapan format nil atau bin X T1 T2 harus diperlakukan sebagai suatu anggota dari type BinTree. Ini juga dikenal sebagai suatu type aljabar; deklarasi type sangat utama menandai simbol pembangun yang digunakan untuk menciptakan anggota dari type itu. Symbol type kemudian bisa digunakan sebagai suatu pengawal pada variabel pada sisi left-hand penyamaan/equation dan definisi variabel untuk memastikan bahwa suatu variabel hanya ditarungkan ke object dari type yang diberikan, lihat Bagian 7.5 [Type Guards], halaman 60. E.G., aturan berikut mempekerjakan Type Guards seperti itu dalam rangka membatasi argument dari fungsi foo ke object BinTree:
foo T:BinTree = ...;
ini membuat itu mungkin untuk menghindari pola argument tegas/eksplisit seperti
foo (bin X T1 T2) = ...;
jika di mana nilai-nilai komponen adalah tidak benar-benar diperlukan. ini dapat menyederhanakan berbagai hal banyak, khususnya jika berbagai argument (multiple arguments) harus ditarungkan ke type yang ditentukan. Juga, adalah 74 Bahasa pemrograman Q yang lebih efisien dibanding mengecek type suatu obyek di dalam bagian qualifier dari suatu aturan dengan menggunakan suatu sebutan/predicate yang ditentukan oleh user (a user-defined predicate), karena interpreter dapat menggunakan informasi type segera di dalam pola proses yang mempertemukan (matching process).
Alasan penting yang lain untuk lebih menyukai type guards pola argument tegas/eksplisit di atas adalahisu “menyembunyikan informasi”. Dengan definisi terdahulu dari fungsi foo di atas kita
tidak membuat acuan tegas/eksplisit manapun untuk pola constructor/pembangun dalam membuat tipe data Bin Tree. Ini membuatnya mungkin untuk perlakukan Bin Tree sebagai suatu tipe data abstrak (ADT) yang menyembunyikan detail implementasi dasar (khususnya constructor/pembangun), selama masih mampu memverifikasi bahwa jenis proper obyek disediakan sebagai suatu argument. Akses manapun ke object menyangkut ADT akan diterapkan dengan mengacu pada operasi yang sesuai yang disediakan oleh type itu. Sesungguhnya, kita dapat membuat simbol private/pribadi constructor/pembangun yang hanya dapat diakses untuk script yang mengimplementasikan tipe Bin Tree:
public type BinTree = private const nil, bin X T1 T2;
Sebagai contoh konkret, mari kita mengasumsikan operasi pohon pencarian (search tree) standard insert T X dan delete T X, yang memasukkan/menyisipkan suatu elemen X ke dalam suatu pohon T, dan memindahkannya/mengeluarkannya dari pohon, berturut-turut. Operasi ini dapat diterapkan sebagai berikut (lihat [Bird/Wadler 1988]):
public insert T X, delete T X; private join T1 T2, init T, last T; insert nil Y = bin Y nil nil;
insert (bin X T1 T2) Y = bin X (insert T1 Y) T2 if X>Y; = bin X T1 (insert T2 Y) if X<Y; = bin Y T1 T2 if X=Y;
delete nil Y = nil;
delete (bin X T1 T2) Y = bin X (delete T1 Y) T2 if X>Y; = bin X T1 (delete T2 Y) if X<Y; = join T1 T2 if X=Y;
join nil T2 = T2;
join T1 T2 = bin (last T1) (init T1) T2 otherwise; init (bin X T1 nil) = T1;
init (bin X T1 T2) = bin X T1 (init T2) otherwise; last (bin X T1 nil) = X;
last (bin X T1 T2) = last T2 otherwise;
(Catat bahwa operasi terakhir dan init mengembalikan elemen terakhir suatu pohon biner (binary tree), dan suatu pohon biner dengan elemen terakhir dipindahkan/dikeluarkan, berturut- turut. Gabungan itu, init dan fungsi terakhir adalah hanya untuk penggunaan internal, dan dapat karenanya diterapkan sebagai fungsi private.)
Lagipula, kita mengasumsikan fungsi bintree berikut yang membangun suatu pohon biner (binary tree) dari suatu list, dan anggota fungsi yang mengembalikan daftar elemen-elemen yang disimpan di dalam suatu pohon/tree (dalam urutan menaik):
public bintree Xs;
bintree Xs:List = foldl insert nil Xs; public members T;
members nil = [];
members (bin X T1 T2) = members T1 ++ [X|members T2];
(Definisi bintree mempekerjakan operasi standard foldl, lihat Bab 11 [Standard library], halaman 113.) Kita dapat menggunakan operasi penghubung (interface operations) dari BinTree ADT dalam rangka menerapkan fungsi union dan diff yang menambahkan(add) dan memindahkan(remove) semua anggota dari suatu pohon ke/dari pohon yang lain :
public union T1 T2; // add elements of T2 to T1 union T1:BinTree T2:BinTree
= foldl insert T1 (members T2); public diff T1 T2; // remove elements of T2 from T1 diff T1:BinTree T2:BinTree
= foldl delete T1 (members T2);
Amati bahwa tidak ada acuan tegas/eksplisit dibuat untuk pembangun/constructors BinTree;
kita hanya mempekerjakan fungsi public “interface” insert, delete dan anggota dari BinTree
ADT. Ini mengijinkan kita untuk merubah perwujudan dari struktur data tanpa keharusan untuk menulis kembali definisi union dan diff. Meski demikian, definisi union dan diff menjadi
“aman”; BinTree type guard memastikan bahwa argument diberikan kepada union dan diff dibutuhkan object BinTree yang mampu menyelesaikan operasi yang diminta itu.
Mulai dari versi 7.2, Q sekarang juga menyediakan suatu sangkutan ke dalam expression/ungkapan built-in pretty-printer sedemikian sehingga kamu dapat menggambarkan kebiasaan yang tidak menguraikan untuk manapun user-defined, built-in atau tipe data eksternal. Ini bermanfaat untuk tipe aljabar dengan pembangun constructor private, karena mengijinkan kamu untuk dengan sepenuhnya menyembunyikan penyajian internal sekalipun suatu object dari type dicetak di dalam interpreter itu. Ini dilaksanakan dengan hanya menambahkan definisi untuk fungsi (predefined/yang sudah dikenal) unparsed/yang tidak diuraikan. Nilai dikembalikan dengan tidak menguraikan(kalimat) harus menjadi suatu
expression/ungkapan “yang dikutip” yang kemudian dicetak sebagai ganti salah satu yang asli
itu. Sebagai contoh:
unparse T:BinTree = ’(bintree Xs) where Xs = members T;
Suatu uraian menyangkut operator tanda kutip ‘’’ dapat ditemukan di Bab 9 [Format Khusus],
halaman 83. Dengan definisi di atas suatu object BinTree kini dicetak sebagai berikut:
==> def T1 = bintree [17,5,26], T2 = bintree [8,17] ==> union T1 T2
bintree [5,8,17,26]
Tolong catat bahwa dalam suatu kasus dimana hasil yang dicetak pada umumnya bukan suatu format normal yang nyata, tetapi ungkapan/expression literal lain yang dikira merekonstruksi object manakala [itu] dievaluasi. (Tidak ada cara bahwa interpreter bisa menguatkan/menyelenggarakan kondisi itu, sehingga hasil yang dicetak bisa benar-benar
menjadi expression/ungkapan Q yang valid. Tetapi [itu] direkomendasikan untuk mengikuti prinsip itu bagaimanapun, jika hanya karena [itu] mengijinkan kamu untuk “menjadikan ceritera bersambung (serialize)” object itu dan menyimpannya di dalam suatu file.)
Kadang-kadang itu berguna untuk sepenuhnya melumpuhkan/disable kebiasaan yang tidak menguraikan sedemikian sehingga kamu dapat lihat format normal “yang mentah”. Ini
terpenuhi dengan mudah dengan yang tidak menguraikan(kalimat) off command/perintah, lihat Bagian B.2 [Bahasa Perintah (Command Language)], halaman 205.