Perl

Bambang Nurcahyo Prastowo

Secara umum, urusan perkomputeran terdiri dari dua bagian: data dan program. Data adalah bagian yang diolah, sedangkan program adalah bagian instruksi pengolahannya.

Perl adalah salah satu program yang khusus dirancang untuk manipulasi teks seperti menyusun laporan rekapitulasi dari data-data transaksi dan sebagainya.

Perl menyediakan perintah kontrol yang umumnya tersedia di C.

Bab ini mengenalkan beberapa aspek pemrograman Perl secara singkat tetapi cukup lengkap untuk membantu pembaca menulis program yang relatif canggih.

1 Data Dasar

Meskipun ada fasilitas untuk manipulasi data biner, pada dasarnya, Perl dirancang untuk mengolah data teks. Banyak fasilitas pemrograman yang mengasumsikan bahwa input data merupakan deretan simbol yang terpisah-pisah oleh simbol baris baru (new line). Pemrograman Perl relatif akan mudah jika data-data bentuk lain seperti angka bulat/pecah dinyatakan dengan teks simbol angka-angka itu.

1.1 Struktur Data

Ada 3 bentuk struktur data di Perl: skalar, larik, dan asosiasi.

1.1.1 Skalar

Skalar adalah data teks tunggal. Contoh: "padi", "kaca", "kacang atom", "1995", "18 Juni 1995", dsb. Jika teks skalar itu dapat dibaca sebagai angka, misalnya "15", "4.5", maka dia bisa diperlakukan sebagai angka. Mosalnya perkalian "15" * "3" menghasilkan skalar "45". Perhatikan, "15" * "3" tidak sama dengan "15 * 3". Skalar "15 * 3" adalah teks biasa yang berisi angka, spasi dan simbol * yang secara keseluruhan tidak bisa diperlakukan sebagai angka.

1.1.2 Larik

Larik adalah deretan skalar-skalar yang dipisahkan koma dan ditulis dalam tanda kurung. Contoh: ("padi", "jagung", "ubi"), ("senin", "selasa"), ("buku", "5", "pensil", "7"), dan sebagainya.

1.4. Asosiasi

Asosiasi adalah pasangan-pasangan dua skalar yang dipisahkan dengan koma dan ditulis dalam tanda kurung kurawal. Dua skalar dari tiap pasangan dipisahkan dengan tanda =>. Contoh:

"Bambang" =>"Kingston, Kanada", 
 "Sumedi" => "USA", 
 "Dwi" => "Osaka, Jepang")

1.2 Program

Untuk mengetahui hasil pemrograman, diperlukan cara menampilkan data. Perl menyediakan perintah print untuk menampilkan data. Cara paling mudah menjalankan program perl adalah dengan menuliskannya di berkas, misalnya coba.pl, terus jalan kan dengan perintah (dos atau unix)

perl coba.pl

Contoh:

print "halo apakabar?"

akan menapilkan skalar "Halo apakabar?". Kalau lebih dari satu baris, perintah-perintah perl harus dipisahkan dengan semikolon ;.

1.2.1 Merujuk Data

Pertama, kita perlu bisa merujuk data. Data dirujuk dengan nama (atau tenger dalam bahasa jawa). Data skalar dirujuk dengan nama berawalan simbol $, data larik dirujuk dengan nama berawalan , dan data asosiasi dirujuk dengan nama berawalan %. Untuk menempelkan data dan namanya, gunakan perintah dengan simbol =. Contoh:

$name = "Bambang Prastowo";
$alamat = "Kingston, Canada";

@warna = ("Hijau", "Kuning", "Biru", "merah");

%menu = ("Senin" => "Soto Ayam", "Selasa" => "Sayur Lodeh",
         "Rabu" => "Rendang", "Kamis" => "Sambal Balado");

Karena dipakai dalam contoh berikutnya, sebaiknya contoh-contoh ini dieditkan dalam satu berkas, mislnya contoh.pl. Untuk bisa dijalankan dengan command

perl contoh.pl

Dalam contoh-contoh berikut, akan banyak dilakukan perintah print. Untuk memudahkan melihat hasil yang dimaui, perintah print (dan lain-lain) yang tidak diperlukan bisa dilewati saja kalau depannya diberi tanda pagar #. Jadi

# print "bla bla bla";

tidak akan dianggap sebagai bagian dari program.

1.2.2 Merujuk Anggota Larik dan Asosiasi

Anggota larik bisa dirujuk dengan indeks posisinya dalam larik. Anggota pertama dirujuk dengan index posisi 0, yang kedua berindex 1, yang ketiga berindex 2, dan seterusnya. Jadi, kalau kita ketikkan perintah

print $warna%{2%};

Akan menampilkan skalar "Biru". Perhatikan, peindeksan itu memakai awalan $, bukan . Ini menunjukkan bahwa dia merujuk ke skalar anggota @warna, bukan larik @warna itu sendiri.

Untuk merujuk ke anggota asosiasi, kita perlakukan bagian pertama dari tiap anggota asosiasi itu sebagai index. Contohnya

print $menu{"Selasa"}

menampilkan skalar "Sayur Lodeh".

1.3 Saluran Data

Di bagian ini kita keluar sebentar dari urusan perl. Pemrosesan data dengan program macam grep, sed, awk, perl itu mengikuti filosofi saluran dan saringan data. Data disalurkan melalu beberapa saringan yang disambung-sambung dengan pipa. Contoh, kita menuliskan program:

join lokasi.dat telpon.dat > sementara.dat

awk '{print "Nama: " $1; 
      print "Lokasi: " $2; 
      print "Telpon: " $3;
      print ""}' sementara.dat > daftar.dat

Join membaca data dari lokasi.dat dan telpon.dat untuk dijoin dan menuliskan hasilnya ke berkas sementara.dat. Awk membaca sementara.dat mengolahnya dan menuliskan hasilnya ke daftar.dat.

Dua perintah ini bisa disambung langsung dengan pipa tanpa perlu menuliskan berkas sementara. Di unix dan dos, pipa ditulis dengan tanda garis tegak |. Contoh, join dan awk di atas bisa digabung menjadi

join lokasi.dat telpon.dat | 
awk '{print "Nama: " $1; 
      print "Lokasi: " $2; 
      print "Telpon: " $3;
      print ""}' sementara.dat > daftar.dat

1.3.1 Membaca berkas dari dalam Perl

Didalam program perl, data bisa dianggap mengalir dari dan ke berkas. Untuk bisa membuka aliran data, kita gunakan perintah open. Contoh, dalam perl kita bisa buka saluran data dari berkas lokasi.dat dengan

open(LOK, "lokasi.dat");

Setelah open itu, baris baris di berkas lokasi.dat bisa dibaca baris per baris dengan perintah penempelan data ke nama dari saluran <LOK>, misalnya

$lok = <LOK>;

print $lok;

akan menampilkan baris pertama dari berkas "lokasi.dat". Kalau kita ulang perintah

$lok = <LOK>;

print $lok;

maka $lok akan merujuk ke baris berikutnya dari berkas "lokasi.dat" dan seterusnya.

Jika yang disebelah kiri tanda sama dengan itu nama data larik (pakai awalan @, bukan $), maka semua data akan tesedot habis ke situ (berkasnya sendiri sih masih utuh, tidak terganggu gugat). Contohnya:

open(LOK, "lokasi.dat");
@lokasi = <LOK>;

print @lokasi;

menampilkan semua isi berkas lokasi.dat.

1.4 Fungsi

Perl menyediakan banyak sekali fungsi ``built-in''. Kita mulai dengan yang akan banyak dipakai dalam contoh berikut yaitu split. Split memotong-motong data skalar pada posisi-posisi tertentu yang bisa ditentukan sendiri dengan ekspresi regular diantara dua garis miring. Sementara, kita pakai ekspresi paling mudah yaitu spasi, / /. Contoh berikut memotong skalar "Pon Wage Kliwon Legi Pahing" pada tiap spasi menjadi larik ("Pon", "Wage", "Kliwon", "Legi", "Pahing").

@pasaran = split(/ /, "Pon Wage Kliwon Legi Pahing");
print $pasaran%{3%};

menampilkan skalar "Legi". Ingat bahwa, indeks larik perl mulai dari 0. Jadi print $pasaran%{0%} menampilkan "Pon".

2 Unsur-Unsur Perl

2.1 Kontrol Pengulangan

Bagian dari program perl bisa diulang-ulang dengan berbagai kontrol pengulangan.

2.1.1 Pengulangan dengan foreach

Yang sering dilakukan adalah mengulang pekerjaan untuk tiap anggota lrik. Untuk itu kita bisa gunakan foreach dengan sintaks

foreach $perujuk_data (@larik) {perintah-perintah}

Contoh, kita punya berkas lokasi.dat yang berisi:

Bambang Canada
Sumedi Amerika
Juwono Indonesia
Dwi Jepang

Kita bisa buat program

open(LOK, "lokasi.dat");
@lokasi = <LOK>;

foreach $lok (@lokasi){
    @nama_lokasi = split(/ /, $lok);
    print $nama_lokasi%{0%};
}

Yang akan menampilkan "BambangSumediJuwonoDwi". Perlu diingat, kalau tidak menjadi bagian dari skalarnya, perl tidak akan memberikan baris baru pada tiap perintah print. Di perl, baris baru diberi kode "
n"
. Untuk memisahkan nama-nama itu, bisa dicoba

print "$nama_lokasi%{0%}\n";
atau
print "$nama_lokasi%{0%}  ";

2.1.2 Pengulangan Berbingkai

Pengulangan bisa dilakukan didalam badan pengulangan yang lain. Contohnya pernah kita lihat pada proses join. Menggabung data nama-lokasi dan nama-telpon, menjadi data nama-lokasi-telpon. Kita punya berkas "telpon.dat" yang berisi

Bambang 123-456-7890
Sumedi 111-222-3333
Juwono 222-333-4444
Dwi 999-888-7777

Bisa dibuat program berikut, misalnya diberi nama namaloka.pl

open(LOK, "lokasi.dat"); # buka saluran LOK dari berkas lokasi.dat
@lokasi = <LOK>;         # baca semua data, masukkan ke larik @lokasi
open(TEL, "telpon.dat"); #
@telpon = <TEL>;         #

foreach $lok (@lokasi){  # ulang untuk tiap anggota larik @lokasi
                         # dirujuk dengan nama $lok
    chop $lok;           # buang tanda baris baru di ujung akhir $lok
                         # bandingkan hasilnya tanpa chop ini
    @nama_lokasi = split(/ /, $lok); # pisahkan nama dan lokasi
    foreach $tel (@telpon){ #pengulangan di dalam pengulangan
        @nama_telpon = split(/ /, $tel);
        if($nama_lokasi%{0%} eq $nama_telpon%{0%}){
            print "$nama_telpon%{0%} $nama_lokasi%{1%} $nama_telpon%{1%}";
        }
    }
}

Kalau dijalankan dengan perintah

perl namaloka.pl

Akan dihasilkan

Bambang Canada 123-456-7890
Sumedi Amerika 111-222-3333
Juwono Indonesia 222-333-4444
Dwi Jepang 999-888-7777

Bisa dirapikan (seperti contoh awk terdahulu) dengan mengganti perintah print itu dengan

print "Nama:$nama_telpon%{0%}\n",
      "Lokasi:$nama_lokasi%{1%}\n",
      "Telpon:$nama_telpon%{1%}";

Ingat, "\n" itu adalah kode untuk baris baru.

2.2 Perintah bersyarat

Contoh namaloka.pl di atas memakai perintah bersyarat if. Perintah bersyarat adalah perintah yang dijalan kalau syarat tertentu dipenuhi. Perintah bersyarat diawali dengan kata if, diikuti syarat dalam tanda kurung dan perintah-perintah dalam kurung kurawal. Perintah-perintah itu dijalankan kalau syarat dalam tanda kurung itu terpenuhi. Dalam contoh namaloka.pl di atas, print dijalankan kalau bagian nama dari nama_lokasi sama (eq, equal) dengan bagian nama dari nama_telpon.

2.3 Latihan

Dalam contoh di atas, nama, lokasi, dan kota terdiri dari satu kata saja yang dipisahkan dengan satu spasi. Dalam kenyataan, nama lengkap kebanyakan terdiri dari dua kata atau lebih. Demikian pula lokasi bisa diisi dengan alamat lengkap.

Buat contoh data nama alamat dengan nama-nama lengkap dan lokasi berupa alamat jalan, nomor, kota dsb. Ubah program namaloka.pl sehingga bisa menangani data itu. Petunjuk: gunakan simbol pemisah titik dua dan split dengan pemisah /:/.

2.4 @ARGV

Dalam contoh namaloka.pl di atas, nama berkas yang diolah (lokasi.dat dan telpon.dat) dituliskan sebagai bagian dari program itu sendiri. Demikian pula dengan simbol pembatas spasi. Sering kita ingin membuat program fleksibel bisa diterapkan ke nama berkas yang lain. Untuk menangkap informasi dari luar program, kita gunakan larik @ARGV. Larik ini akan terisi kata-kata yang diketikkan pada saat menjalankan program. Misalnya, kalau kita ketikkan

perl namaloka.pl lokasi.dat telpon.dat

maka kata-kata setelah nama program manaloka.pl bisa ditangkap dari dalam program dengan membaca larik @ARGV. Dari contoh itu, $ARGV%{0%} berisi skalar "lokasi.dat" dan $ARGV%{1%} berisi skalar "telpon.dat". Dengan cara itu, pembukaan saluran data bisa merujuk ke isi larik @ARGV. Misalnya dengan open(LOK, $ARGV%{0%}); dan open(TEL, $ARGV%{1%});

Untuk membuat program fleksibel, kita bisa juga member spesifikasi simbol pemisah dari luar program. Misalnya dengan mengetikkan

perl namaloka.pl lokasi.dat telpon.dat :
                     |          |      |
                 $ARGV%{0%}   $ARGV%{1%}  $ARGV%{2%}

Dan pemisahan data nama lokasi dilakukan dengan

    split(/$ARGV%{2%}/, $lok);

Kalau baris-baris datanya menggunakan simbol pemisah :.

Informasi yang dimasukkan dari luar program semacam ini disebut parameter (kadang disebut juga dengan argumen). Pada dasarnya, makin banyak parameter, makin fleksibel kegunaan program itu. Akan tetapi, biasanya program yang terlalu banyak parameternya akan membingungkan pemakaiannya.

2.5 Ekspresi Regular

Ekspresi regular atau sering disebut regex adalah skalar yang mewakili sekelompok skalar-skalar yang lain dengan cara menggunakan kode-kode tertentu. Biasanya, regex ditulis diantara dua garis miring. Berikut ini adalah contoh beberapa regex versi perl yang sering ter(saya)pakai:

/./ mewakili skalar satu simbol apa saja /../ mewakili skalar dua simbol apa saja

huruf-huruf dan angka-angka mewakili dirinya sendiri, jadi
/Bambang/ mewakili skalar "Bambang".

Simbol yang sama berjajar bisa dinyatakan dengan tambahan kode * atau +. * berarti nol atau lebih, + berarti satu atau lebih.
/Bambang*/ mewakili "Bamban", "Bambang", "Bambangg", "Bambanggg", dst.
/Bambang+/ mewakili "Bambang", "Bambangg", "Bambanggg", dst.
/ +/ mewakili satu spasi atau lebih (sering dipakai sebagai pemisah dalam perintah split.

Untuk merujuk ke simbol yang dipakai sebagai kode regex, gunakan garis miring terbalik Jadi /\./ mewakili skalar "." /\*\*\+\./ mewakili skalar "**+.".

Tab, yang sering tertampilkan sebagai beberapa spasi yang memenuhi sampai kolom tertentu, ditulis dengan . Angka diwakili dengan \d (singkatan digit). Jadi skalar angka bilangan bulat bisa diwakili dengan /\d+/ yang bararti satu digit atau lebih "0", "1", "2", ..., "10", ..., "100" dan seterusnya. Angka desimal pecahan bisa diwakili dengan /\d+\.\d*/ yang berarti satu atau lebih digit, diikuti dengan titik, diikuti dengan nol atau lebih digit.

Tanda kurung bisa dipakai untuk mengelompokkan bagian dari regex yang terpengaruh kode. Misalnya /(dor)+/ mewakili "dor", "dordor", "dordordor", dst. /cas(cis)*cus/ mewakili "cascus", "casciscus", "cascisciscus", dst.

2.6 Operator Pembanding

Oprator pembanding =~ punya dua fungsi:

  1. pertama membandingkan skalar disebelah kirinya dengan regex disebelah kanan sebagai kondisi dijalankannya bagian program, dan
  2. menganti bagian dari skalar disebelah kiri dengan cara yang ditentukan opeh operasi di sebelah kanan

Untuk contoh berikut, gunakan berkas dir.dat yang isinya:

 Volume in drive D is /hda6/dos
 Directory of D:\PERL\CONTOH

.            <DIR>     07-24-95  11:53a
..           <DIR>     07-12-95  10:02a
DIR      DAT        37 07-24-95  11:53a
LOKASI   DAT        58 07-23-95   8:01p
TELPON   DAT        78 07-23-95   8:02p
DAFTAR   DAT       202 06-21-95  12:45a
JOIN     PL        266 07-12-95  10:30a
SEMENTAR DAT       108 07-12-95  10:21a
OPENFILE PL         62 07-21-95  10:57a
FORMAT   AWK       145 07-14-95   3:48a
ASO      PL         83 07-21-95  10:24a
ULANG    PL         98 07-21-95  11:20a
SPLIT    DAT        77 07-21-95  11:51a
NAMA     PL        142 07-21-95  11:57a
SYSTEM   PL         15 07-24-95  11:51a
NAMALOKA PL        743 07-23-95   8:04p
       16 file(s)       2114 bytes
                    76365824 bytes free

Data semacam ini bisa diperoleh dengan mengetikkan dir > dir.dat Di DOS. Bagi yang menggunakan unix, gunakan saja data di atas.

2.6.1 Pembanding Sebagai Syarat

Misalnya kita hendak memilih baris-baris dari dir.dat dari nama program perl. Dalam contoh, nama program perl berakhiran PL. Maka kita print baris itu kalau mengandung "PL".

open(DIR, "dir.dat");
@dir = <DIR>;

foreach $baris (@dir){
    if($baris =~ /PL/){
         print $baris;
    }
}

Program di atas kurang sempurna karena baris

"SPLIT DAT 77 07-21-95 11:51a"

ikut tersaring. Disini kita dituntut untuk berkreasi. Misalnya, syarat print diatas bisa kita ubah menjadi ($baris =~ / PL /) mengharuskan PL yang dicari diapit spasi.

3 Bawaan

Perl mengistimewakan nama data yang terdiri dari satu simbol garis bawah $_. Beberapa fungsi bisa dijalankan tanpa menyebut nama data inputnya. Jika tidak disebut nama, maka yang dimaksud adalah data yang bernama $_.

3.1 Bawaan dalam Kontrol Pengulangan

Secara umum, nama $_ bisa dipakai untuk merujuk skalar sebagaimana nama-nama yang lain. Misalnya

open(DIR, "dir.dat");
@dir = <DIR>;

foreach $_ (@dir){
    $_ =~ s/95/1995/;
    print $_;
}

Secara khusus, kontrol foreach itu bisa diganti for, tanpa menyebutkan nama rujukan $_ (sudah dianggap ada dari sononya).

for (@dir){
   $_ =~  s/95/1995/;
    print $_;
}

Jika dikenakan pada $_, operator =~ tidak perlu menyebutkan $_.

for (@dir){
   s/95/1995/;
    print $_;
}

Perintah print $_; juga tidak perlu menyebutkan $_. Contoh program melengkapi angka tahun diatas bisa ditulis demikian:

open(DIR, "dir.dat");
@dir = <DIR>;

for (@dir){
    s/95/1995/;
    print;
}

Dengan bawaan, contoh program namaloka.pl di 5.2 bisa disederhanakan menjadi

open(LOK, "lokasi.dat"); # buka saluran LOK dari berkas lokasi.dat
@lokasi = <LOK>;         # baca semua data, masukkan ke larik @lokasi
open(TEL, "telpon.dat"); #
@telpon = <TEL>;         #

for (@lokasi){  # ulang untuk tiap anggota larik @lokasi
                # dirujuk dengan nama $_
    chop;       # chop $_ bisa ditulis chop saja
    @nama_lokasi = split; # tanpa keterangan tambahan, split akan
                          # berlaku seperti split(/%{ \t%}+/, $_)
    for (@telpon){
        @nama_telpon = split;
        if($nama_lokasi%{0%} eq $nama_telpon%{0%}){
            print "$nama_telpon%{0%} $nama_lokasi%{1%} $nama_telpon%{1%}\n";
        }
    }
}

3.2 Jenis Kontrol

Kontrol dalam pengulangan dan perintah bersyarat punya bentuk yang sama. Keduanya berbeda fungsi saja. Dalam pengulangan, kontrol dipakai untuk menentukan perlu tidaknya kelompok perintah diulang lagi. Dalam perintah bersyarat, kontrol dipakai untuk menentuka dikerjakan atau tidaknya sekelompok perintah. Ada tiga macam kontrol: bentuk perbandingan, nilai dan kesuksesan. Sebagai kontrol, semuanya akan diinterpretasikan sebagai "ya" atau "tidak" terhadap dijalankannya perintah-perintah yang dikontrol.

3.2.1 Kontrol perbandingan

Ini yang paling mudah difahami. Kita membandingakan dua skalar. Jika yang kita bandingkan adalah urutan abjadnya, perbandingan menggunakan operator lt, gt, le, ge, eq, dan ne sebagai perbandingan lebih kecil, lebih besar, lebih kecil atau sama, lebih besar atau sama, sama, dan tidak sama. Jika kita membandingkan skalar sebagai angka, maka operator pembandingnya memakai simbol <, >, <=, >=, ==, dan !=. Nilai "ya" atau "tidak" nya kontrol perbandingan saya kita sudah jelas.

3.2.2 Kontrol nilai

Kontrol nilai punya interpretasi sebagai berikut:

  1. teks kosong, "", bernilai ``tidak''
  2. angka nol, 0, bernilai ``tidak''
  3. teks angka nol, "0", bernilai ``tidak''
data-data selain itu diinterpretasikan sebagi ``ya''.

Silakan bereksperimen dengan baris baris berikut, pelajari baik-baik hasilnya:

if(0)        {print "testing kontrol (0) \n";}
if(1)        {print "testing kontrol (1) \n";}
if("")       {print "testing kontrol (\"\") \n";}
if("0")      {print "testing kontrol (\"0\") \n";}
if("00")     {print "testing kontrol (\"00\") \n";}
if("2-2")    {print "testing kontrol (\"2-2\") \n";}
if("2" - "2"){print "testing kontrol (\"2\" - \"2\") \n";}

3.2.3 Kontrol kesuksesan

Beberapa perintah yang berhubungan dengan hal-hal diluar program misalnya membuka saluran data dari dan ke berkas, dalam kontrol bisa dinterpertasikan sebagai sebagai ``ya'' atau ``tidak'' tergantung kesuksesan perintah itu. Misalnya:

if (open(DATA, "lokasi.dat")) { print <DATA>;}
else {print "lokasi.dat tidak bisa dibuka\n";}

else {...} adalah perintah yang bisa dituliskan setelah perintah bersyarat. Ini bisa dipakai untuk menjalankan perintah-perintah dalam tanda kurung kurawal itu sebagai alternatif, sekiranya kontrol dalam perintah bersyarat sebelumnya itu berniai ``tidak''.

3.3 Menangani berkas berukuran besar

Dalam contoh sebelum ini, untuk diolah, data dari berkas dimasukkan semua sekaligus ke dalam larik seperti

open(DIR, "dir.dat");
@dir = <DIR>;

yang menyalin isi dir.dat ke larik @dir semuanya sekaligus. Jika dir.dat berukuran raksasa, @dir = <DIR> bisa mengakibatkan sistem kehabisan memori (out of memory). Untuk mengatasi masalah memori ini, data bisa diambil dan diolah satu per satu. Misalnya:

open(DIR, "dir.dat");

while ( $baris = <DIR>) {
    $baris =~ s/95/1995/;
    print $baris;
}
atau
open(DIR, "dir.dat");

while(<DIR>){
    s/95/1995/;
    print;
}

Perintah while adalah cara lain membuat pengulangan. Bentuknya

while(kontrol){...}
Perintah-perintah dalam kurung kurawal dijalan kalau kontrolnya bernilai ``ya''. Setelah selesai, kontrol kembali diinterpretasikan, kalau nilainya masih ``ya'', perintah-perintah dalam kurung kurawal di jalankan lagi.

Ingat bahwa $baris = <DIR> mengambil data dari saluran DIR baris-perbaris. Setelah data habis, maka perintah itu akan gagal sehingga sebagai kontrol, interpretasinya dalah ``tidak''.

Contoh while kedua itu menggunakan bawaan, baris dioleh dengan nama $_ yang menurut perjanjian tidak perlu dituliskan.