mirror of
https://github.com/adambard/learnxinyminutes-docs.git
synced 2024-11-23 22:27:35 +03:00
693 lines
21 KiB
Common Lisp
693 lines
21 KiB
Common Lisp
|
---
|
||
|
|
||
|
language: "Common Lisp"
|
||
|
filename: commonlisp-ms.lisp
|
||
|
contributors:
|
||
|
- ["Paul Nathan", "https://github.com/pnathan"]
|
||
|
- ["Rommel Martinez", "https://ebzzry.io"]
|
||
|
translators:
|
||
|
- ["Burhanuddin Baharuddin", "https://github.com/burhanloey"]
|
||
|
lang: ms-my
|
||
|
---
|
||
|
|
||
|
Common Lisp ialah programming language yang general-purpose (boleh digunakan untuk semua benda) dan multi-paradigm (konsep yang pelbagai) sesuai untuk pelbagai kegunaan di dalam
|
||
|
industri aplikasi. Common Lisp biasa digelar sebagai programmable programming language (programming language yang boleh di-program-kan).
|
||
|
|
||
|
Sumber bacaan yang klasik ialah [Practical Common Lisp](http://www.gigamonkeys.com/book/). Sumber bacaan yang lain dan
|
||
|
yang terbaru ialah [Land of Lisp](http://landoflisp.com/). Buku baru mengenai best practices (amalan terbaik),
|
||
|
[Common Lisp Recipes](http://weitz.de/cl-recipes/), baru sahaja diterbitkan.
|
||
|
|
||
|
|
||
|
|
||
|
```common-lisp
|
||
|
|
||
|
;;;-----------------------------------------------------------------------------
|
||
|
;;; 0. Syntax
|
||
|
;;;-----------------------------------------------------------------------------
|
||
|
|
||
|
;;; General form (Bentuk umum)
|
||
|
|
||
|
;;; Ada dua asas dalam syntax CL: ATOM dan S-EXPRESSION.
|
||
|
;;; Kebiasaannya, gabungan S-expression dipanggil sebagai `forms`.
|
||
|
|
||
|
10 ; atom; bermaksud seperti yang ditulis iaitu nombor 10
|
||
|
:thing ; juga atom; bermaksud simbol :thing
|
||
|
t ; juga atom, bermaksud true (ya/betul/benar)
|
||
|
(+ 1 2 3 4) ; s-expression
|
||
|
'(4 :foo t) ; juga s-expression
|
||
|
|
||
|
|
||
|
;;; Comment (Komen)
|
||
|
|
||
|
;;; Comment satu baris bermula dengan semicolon; gunakan empat untuk comment
|
||
|
;;; mengenai file, tiga untuk seksyen penghuraian, dua untuk yang dalam definition,
|
||
|
;;; dan satu untuk satu baris. Sebagai contoh,
|
||
|
|
||
|
;;;; life.lisp
|
||
|
|
||
|
;;; Foo bar baz, disebabkan quu quux. Sangat optimum untuk krakaboom dan umph.
|
||
|
;;; Diperlukan oleh function LINULUKO. Ini merepek sahaja kebaboom.
|
||
|
|
||
|
(defun meaning (life)
|
||
|
"Memulangkan hasil pengiraan makna KEHIDUPAN"
|
||
|
(let ((meh "abc"))
|
||
|
;; Jalankan krakaboom
|
||
|
(loop :for x :across meh
|
||
|
:collect x))) ; Simpan hasil ke x, kemudian pulangkan
|
||
|
|
||
|
;;; Komen berbentuk blok, sebaliknya, membenarkan komen untuk bentuk bebas. Komen
|
||
|
;;; tersebut berada di antara #| dan |#
|
||
|
|
||
|
#| Ini adalah komen berbentuk blok di mana
|
||
|
tulisan boleh ditulis dalam beberapa baris dan
|
||
|
#|
|
||
|
juga boleh dalam bentuk nested (berlapis-lapis)!
|
||
|
|#
|
||
|
|#
|
||
|
|
||
|
|
||
|
;;; Environment (benda-benda yang diperlukan untuk program menggunakan Common Lisp)
|
||
|
|
||
|
;;; Common Lisp ada banyak jenis; kebanyakannya mengikut standard. SBCL
|
||
|
;;; ialah titik permulaan yang baik. Quicklisp boleh digunakan untuk install
|
||
|
;;; library third party.
|
||
|
|
||
|
;;; CL kebiasaannya digunakan dengan text editor dan Real Eval Print
|
||
|
;;; Loop (REPL) yang dilancarkan dengan serentak. REPL membolehkan kita menjelajah
|
||
|
;;; program secara interaktif semasa program tersebut sedang berjalan secara "live".
|
||
|
|
||
|
|
||
|
;;;-----------------------------------------------------------------------------
|
||
|
;;; 1. Datatype primitif dan operator
|
||
|
;;;-----------------------------------------------------------------------------
|
||
|
|
||
|
;;; Simbol
|
||
|
|
||
|
'foo ; => FOO Perhatikan simbol menjadi huruf besar secara automatik.
|
||
|
|
||
|
;;; INTERN menjadikan string sebagai simbol secara manual.
|
||
|
|
||
|
(intern "AAAA") ; => AAAA
|
||
|
(intern "aaa") ; => |aaa|
|
||
|
|
||
|
;;; Nombor
|
||
|
|
||
|
9999999999999999999999 ; integer
|
||
|
#b111 ; binary => 7
|
||
|
#o111 ; octal => 73
|
||
|
#x111 ; hexadecimal => 273
|
||
|
3.14159s0 ; single
|
||
|
3.14159d0 ; double
|
||
|
1/2 ; ratio
|
||
|
#C(1 2) ; complex number
|
||
|
|
||
|
;;; Function ditulis sebagai (f x y z ...) di mana f ialah function dan
|
||
|
;;; x, y, z, ... adalah argument.
|
||
|
|
||
|
(+ 1 2) ; => 3
|
||
|
|
||
|
;;; Jika anda ingin membuat data sebagai data bukannya function, gunakan QUOTE
|
||
|
;;; untuk mengelakkan data tersebut daripada dikira oleh program
|
||
|
|
||
|
(quote (+ 1 2)) ; => (+ 1 2)
|
||
|
(quote a) ; => A
|
||
|
|
||
|
;;; Singkatan untuk QUOTE ialah ' (tanda petikan)
|
||
|
|
||
|
'(+ 1 2) ; => (+ 1 2)
|
||
|
'a ; => A
|
||
|
|
||
|
;;; Operasi arithmetic asas
|
||
|
|
||
|
(+ 1 1) ; => 2
|
||
|
(- 8 1) ; => 7
|
||
|
(* 10 2) ; => 20
|
||
|
(expt 2 3) ; => 8
|
||
|
(mod 5 2) ; => 1
|
||
|
(/ 35 5) ; => 7
|
||
|
(/ 1 3) ; => 1/3
|
||
|
(+ #C(1 2) #C(6 -4)) ; => #C(7 -2)
|
||
|
|
||
|
;;; Boolean
|
||
|
|
||
|
t ; true; semua nilai yang bukan NIL ialah true
|
||
|
nil ; false; termasuklah list yang kosong: ()
|
||
|
(not nil) ; => T
|
||
|
(and 0 t) ; => T
|
||
|
(or 0 nil) ; => 0
|
||
|
|
||
|
;;; Character
|
||
|
|
||
|
#\A ; => #\A
|
||
|
#\λ ; => #\GREEK_SMALL_LETTER_LAMDA
|
||
|
#\u03BB ; => #\GREEK_SMALL_LETTER_LAMDA
|
||
|
|
||
|
;;; String ialah array character yang tidak berubah panjang
|
||
|
|
||
|
"Hello, world!"
|
||
|
"Benjamin \"Bugsy\" Siegel" ; backslash ialah escape character
|
||
|
|
||
|
;;; String boleh digabungkan
|
||
|
|
||
|
(concatenate 'string "Hello, " "world!") ; => "Hello, world!"
|
||
|
|
||
|
;;; String boleh diperlakukan seperti urutan character
|
||
|
|
||
|
(elt "Apple" 0) ; => #\A
|
||
|
|
||
|
;;; FORMAT digunakan untuk output mengikut format, daripada penggubahan string
|
||
|
;;; yang simple sehinggalah loop dan conditional. Argument pertama untuk FORMAT
|
||
|
;;; menentukan ke mana string akan pergi. Jika NIL, FORMAT
|
||
|
;;; akan pulangkan string sebagai data string; jika T, FORMAT akan output
|
||
|
;;; ke standard output, biasanya di screen, kemudian pulangkan NIL.
|
||
|
|
||
|
(format nil "~A, ~A!" "Hello" "world") ; => "Hello, world!"
|
||
|
(format t "~A, ~A!" "Hello" "world") ; => NIL
|
||
|
|
||
|
|
||
|
;;;-----------------------------------------------------------------------------
|
||
|
;;; 2. Variable
|
||
|
;;;-----------------------------------------------------------------------------
|
||
|
|
||
|
;;; Anda boleh membuat variable global (dynamically scoped) menggunakan DEFVAR dan
|
||
|
;;; DEFPARAMETER. Nama variable boleh guna mana-mana character kecuali: ()",'`;#|\
|
||
|
|
||
|
;;; Beza antara DEFVAR dengan DEFPARAMETER ialah DEFVAR tidak akan ubah nilai
|
||
|
;;; variable jika dijalankan semula. Manakala DEFPARAMETER, akan mengubah nilai
|
||
|
;;; jika dijalankan semula.
|
||
|
|
||
|
;;; Kebiasaannya, variable global diletakkan earmuff (asterisk) pada nama.
|
||
|
|
||
|
(defparameter *some-var* 5)
|
||
|
*some-var* ; => 5
|
||
|
|
||
|
;;; Anda juga boleh menggunakan character unicode.
|
||
|
(defparameter *AΛB* nil)
|
||
|
|
||
|
;;; Variable yang tidak wujud boleh diakses tetapi akan menyebabkan undefined
|
||
|
;;; behavior. Jangan buat.
|
||
|
|
||
|
;;; Anda boleh membuat local binding menggunakan LET. Dalam snippet berikut, `me`
|
||
|
;;; terikat dengan "dance with you" hanya dalam (let ...). LET mesti akan pulangkan
|
||
|
;;; nilai `form` yang paling terakhir.
|
||
|
|
||
|
(let ((me "dance with you")) me) ; => "dance with you"
|
||
|
|
||
|
|
||
|
;;;-----------------------------------------------------------------------------;
|
||
|
;;; 3. Struct dan collection
|
||
|
;;;-----------------------------------------------------------------------------;
|
||
|
|
||
|
|
||
|
;;; Struct
|
||
|
|
||
|
(defstruct dog name breed age)
|
||
|
(defparameter *rover*
|
||
|
(make-dog :name "rover"
|
||
|
:breed "collie"
|
||
|
:age 5))
|
||
|
*rover* ; => #S(DOG :NAME "rover" :BREED "collie" :AGE 5)
|
||
|
(dog-p *rover*) ; => T
|
||
|
(dog-name *rover*) ; => "rover"
|
||
|
|
||
|
;;; DOG-P, MAKE-DOG, dan DOG-NAME semuanya dibuat oleh DEFSTRUCT secara automatik
|
||
|
|
||
|
|
||
|
;;; Pair
|
||
|
|
||
|
;;; CONS membuat pair. CAR dan CDR pulangkan head (kepala) dan tail (ekor) CONS-pair.
|
||
|
|
||
|
(cons 'SUBJECT 'VERB) ; => '(SUBJECT . VERB)
|
||
|
(car (cons 'SUBJECT 'VERB)) ; => SUBJECT
|
||
|
(cdr (cons 'SUBJECT 'VERB)) ; => VERB
|
||
|
|
||
|
|
||
|
;;; List
|
||
|
|
||
|
;;; List ialah data structure linked-list, dihasilkan daripada pair CONS dan
|
||
|
;;; berakhir dengan NIL (atau '()) menandakan akhirnya list tersebut
|
||
|
|
||
|
(cons 1 (cons 2 (cons 3 nil))) ; => '(1 2 3)
|
||
|
|
||
|
;;; LIST ialah constructor untuk memudahkan penghasilan list
|
||
|
|
||
|
(list 1 2 3) ; => '(1 2 3)
|
||
|
|
||
|
;;; Apabila argument pertama untuk CONS ialah atom dan argument kedua ialah
|
||
|
;;; list, CONS akan pulangkan CONS-pair baru dengan argument pertama sebagai
|
||
|
;;; item pertama dan argument kedua sebagai CONS-pair yang lain
|
||
|
|
||
|
(cons 4 '(1 2 3)) ; => '(4 1 2 3)
|
||
|
|
||
|
;;; Gunakan APPEND untuk menggabungkan list
|
||
|
|
||
|
(append '(1 2) '(3 4)) ; => '(1 2 3 4)
|
||
|
|
||
|
;;; Atau CONCATENATE
|
||
|
|
||
|
(concatenate 'list '(1 2) '(3 4)) ; => '(1 2 3 4)
|
||
|
|
||
|
;;; List ialah type utama, jadi ada pelbagai function untuk mengendalikan
|
||
|
;;; list, contohnya:
|
||
|
|
||
|
(mapcar #'1+ '(1 2 3)) ; => '(2 3 4)
|
||
|
(mapcar #'+ '(1 2 3) '(10 20 30)) ; => '(11 22 33)
|
||
|
(remove-if-not #'evenp '(1 2 3 4)) ; => '(2 4)
|
||
|
(every #'evenp '(1 2 3 4)) ; => NIL
|
||
|
(some #'oddp '(1 2 3 4)) ; => T
|
||
|
(butlast '(subject verb object)) ; => (SUBJECT VERB)
|
||
|
|
||
|
|
||
|
;;; Vector
|
||
|
|
||
|
;;; Vector ialah array yang tidak berubah panjang
|
||
|
|
||
|
#(1 2 3) ; => #(1 2 3)
|
||
|
|
||
|
;;; Gunakan CONCATENATE untuk menggabungkan vector
|
||
|
|
||
|
(concatenate 'vector #(1 2 3) #(4 5 6)) ; => #(1 2 3 4 5 6)
|
||
|
|
||
|
|
||
|
;;; Array
|
||
|
|
||
|
;;; Vector dan string adalah sejenis array.
|
||
|
|
||
|
;;; 2D array
|
||
|
|
||
|
(make-array (list 2 2)) ; => #2A((0 0) (0 0))
|
||
|
(make-array '(2 2)) ; => #2A((0 0) (0 0))
|
||
|
(make-array (list 2 2 2)) ; => #3A(((0 0) (0 0)) ((0 0) (0 0)))
|
||
|
|
||
|
;;; Perhatian: nilai awal MAKE-ARRAY adalah bergantung kepada jenis Common Lisp.
|
||
|
;;; Untuk meletakkan nilai awal secara manual:
|
||
|
|
||
|
(make-array '(2) :initial-element 'unset) ; => #(UNSET UNSET)
|
||
|
|
||
|
;;; Untuk mengakses element di kedudukan 1, 1, 1:
|
||
|
|
||
|
(aref (make-array (list 2 2 2)) 1 1 1) ; => 0
|
||
|
|
||
|
|
||
|
;;; Adjustable vector (vector yang boleh berubah)
|
||
|
|
||
|
;;; Adjustable vector mempunyai rupa yang sama dengan
|
||
|
;;; vector yang tidak berubah panjang.
|
||
|
|
||
|
(defparameter *adjvec* (make-array '(3) :initial-contents '(1 2 3)
|
||
|
:adjustable t :fill-pointer t))
|
||
|
*adjvec* ; => #(1 2 3)
|
||
|
|
||
|
;;; Tambah element baru
|
||
|
|
||
|
(vector-push-extend 4 *adjvec*) ; => 3
|
||
|
*adjvec* ; => #(1 2 3 4)
|
||
|
|
||
|
|
||
|
;;; Set hanyalah list:
|
||
|
|
||
|
(set-difference '(1 2 3 4) '(4 5 6 7)) ; => (3 2 1)
|
||
|
(intersection '(1 2 3 4) '(4 5 6 7)) ; => 4
|
||
|
(union '(1 2 3 4) '(4 5 6 7)) ; => (3 2 1 4 5 6 7)
|
||
|
(adjoin 4 '(1 2 3 4)) ; => (1 2 3 4)
|
||
|
|
||
|
;;; Tetapi, anda perlukan data structure yang lebih baik untuk digunakan dengan
|
||
|
;;; data set yang sangat banyak
|
||
|
|
||
|
;;; Kamus dibuat menggunakan hash table.
|
||
|
|
||
|
;;; Bina hash table
|
||
|
|
||
|
(defparameter *m* (make-hash-table))
|
||
|
|
||
|
;;; Tetapkan nilai
|
||
|
|
||
|
(setf (gethash 'a *m*) 1)
|
||
|
|
||
|
;;; Baca nilai
|
||
|
|
||
|
(gethash 'a *m*) ; => 1, T
|
||
|
|
||
|
;;; CL boleh memulangkan beberapa nilai (multiple value).
|
||
|
|
||
|
(values 1 2) ; => 1, 2
|
||
|
|
||
|
;;; dan boleh digunakan dengan MULTIPLE-VALUE-BIND untuk bind setiap nilai
|
||
|
|
||
|
(multiple-value-bind (x y)
|
||
|
(values 1 2)
|
||
|
(list y x))
|
||
|
|
||
|
; => '(2 1)
|
||
|
|
||
|
;;; GETHASH antara contoh function yang memulangkan multiple value. Value
|
||
|
;;; pertama ialah nilai untuk key dalam hash table; jika key tidak
|
||
|
;;; jumpa GETHASH akan pulangkan NIL.
|
||
|
|
||
|
;;; Value kedua menentukan sama ada key tersebut betul-betul wujud dalam hash
|
||
|
;;; table. Jika key tidak jumpa dalam table value tersebut ialah NIL. Cara ini
|
||
|
;;; membolehkan kita untuk periksa sama ada value untuk key ialah NIL.
|
||
|
|
||
|
;;; Dapatkan value yang tidak wujud akan pulangkan nil
|
||
|
|
||
|
(gethash 'd *m*) ;=> NIL, NIL
|
||
|
|
||
|
;;; Anda boleh menentukan value default untuk key yang tidak wujud
|
||
|
|
||
|
(gethash 'd *m* :not-found) ; => :NOT-FOUND
|
||
|
|
||
|
;;; Jom lihat penggunaan multiple return value di dalam code.
|
||
|
|
||
|
(multiple-value-bind (a b)
|
||
|
(gethash 'd *m*)
|
||
|
(list a b))
|
||
|
; => (NIL NIL)
|
||
|
|
||
|
(multiple-value-bind (a b)
|
||
|
(gethash 'a *m*)
|
||
|
(list a b))
|
||
|
; => (1 T)
|
||
|
|
||
|
|
||
|
;;;-----------------------------------------------------------------------------
|
||
|
;;; 3. Function
|
||
|
;;;-----------------------------------------------------------------------------
|
||
|
|
||
|
;;; Gunakan LAMBDA untuk membuat anonymous function. Function sentiasa memulangkan
|
||
|
;;; value untuk expression terakhir.
|
||
|
|
||
|
(lambda () "Hello World") ; => #<FUNCTION (LAMBDA ()) {1004E7818B}>
|
||
|
|
||
|
;;; Gunakan FUNCALL untuk memanggil anonymous function
|
||
|
|
||
|
(funcall (lambda () "Hello World")) ; => "Hello World"
|
||
|
(funcall #'+ 1 2 3) ; => 6
|
||
|
|
||
|
;;; Panggilan kepada FUNCALL juga boleh terjadi apabila lambda tersebut ialah CAR
|
||
|
;;; (yang pertama) untuk list (yang tidak mempunyai tanda petikan)
|
||
|
|
||
|
((lambda () "Hello World")) ; => "Hello World"
|
||
|
((lambda (val) val) "Hello World") ; => "Hello World"
|
||
|
|
||
|
;;; FUNCALL digunakan apabila argument sudah diketahui. Jika tidak, gunakan APPLY
|
||
|
|
||
|
(apply #'+ '(1 2 3)) ; => 6
|
||
|
(apply (lambda () "Hello World") nil) ; => "Hello World"
|
||
|
|
||
|
;;; Untuk menamakan sebuah function, guna DEFUN
|
||
|
|
||
|
(defun hello-world () "Hello World")
|
||
|
(hello-world) ; => "Hello World"
|
||
|
|
||
|
;;; Simbol () di atas bermaksud list kepada argument
|
||
|
|
||
|
(defun hello (name) (format nil "Hello, ~A" name))
|
||
|
(hello "Steve") ; => "Hello, Steve"
|
||
|
|
||
|
;;; Function boleh ada argument optional (tidak wajib); argument tersebut bernilai
|
||
|
;;; NIL secara default
|
||
|
|
||
|
(defun hello (name &optional from)
|
||
|
(if from
|
||
|
(format t "Hello, ~A, from ~A" name from)
|
||
|
(format t "Hello, ~A" name)))
|
||
|
|
||
|
(hello "Jim" "Alpacas") ; => Hello, Jim, from Alpacas
|
||
|
|
||
|
;;; Nilai default boleh ditetapkan untuk argument tersebut
|
||
|
|
||
|
(defun hello (name &optional (from "The world"))
|
||
|
(format nil "Hello, ~A, from ~A" name from))
|
||
|
|
||
|
(hello "Steve") ; => Hello, Steve, from The world
|
||
|
(hello "Steve" "the alpacas") ; => Hello, Steve, from the alpacas
|
||
|
|
||
|
;;; Function juga mempunyai keyword argument untuk membolehkan argument diletakkan
|
||
|
;;; tidak mengikut kedudukan
|
||
|
|
||
|
(defun generalized-greeter (name &key (from "the world") (honorific "Mx"))
|
||
|
(format t "Hello, ~A ~A, from ~A" honorific name from))
|
||
|
|
||
|
(generalized-greeter "Jim")
|
||
|
; => Hello, Mx Jim, from the world
|
||
|
|
||
|
(generalized-greeter "Jim" :from "the alpacas you met last summer" :honorific "Mr")
|
||
|
; => Hello, Mr Jim, from the alpacas you met last summer
|
||
|
|
||
|
|
||
|
;;;-----------------------------------------------------------------------------
|
||
|
;;; 4. Kesamaan
|
||
|
;;;-----------------------------------------------------------------------------
|
||
|
|
||
|
;;; CL mempunyai sistem kesaksamaan yang canggih. Antaranya adalah seperti berikut.
|
||
|
|
||
|
;;; Untuk nombor, guna `='
|
||
|
(= 3 3.0) ; => T
|
||
|
(= 2 1) ; => NIL
|
||
|
|
||
|
;;; Untuk identiti object (lebih kurang) guna EQL
|
||
|
(eql 3 3) ; => T
|
||
|
(eql 3 3.0) ; => NIL
|
||
|
(eql (list 3) (list 3)) ; => NIL
|
||
|
|
||
|
;;; untuk list, string, dan bit-vector, guna EQUAL
|
||
|
(equal (list 'a 'b) (list 'a 'b)) ; => T
|
||
|
(equal (list 'a 'b) (list 'b 'a)) ; => NIL
|
||
|
|
||
|
|
||
|
;;;-----------------------------------------------------------------------------
|
||
|
;;; 5. Control Flow
|
||
|
;;;-----------------------------------------------------------------------------
|
||
|
|
||
|
;;; Conditional (syarat)
|
||
|
|
||
|
(if t ; test expression
|
||
|
"this is true" ; then expression
|
||
|
"this is false") ; else expression
|
||
|
; => "this is true"
|
||
|
|
||
|
;;; Dalam conditional, semua value yang bukan NIL ialah true
|
||
|
|
||
|
(member 'Groucho '(Harpo Groucho Zeppo)) ; => '(GROUCHO ZEPPO)
|
||
|
(if (member 'Groucho '(Harpo Groucho Zeppo))
|
||
|
'yep
|
||
|
'nope)
|
||
|
; => 'YEP
|
||
|
|
||
|
;;; Guna COND untuk meletakkan beberapa test
|
||
|
(cond ((> 2 2) (error "wrong!"))
|
||
|
((< 2 2) (error "wrong again!"))
|
||
|
(t 'ok)) ; => 'OK
|
||
|
|
||
|
;;; TYPECASE adalah seperti switch tetapi untuk data type value tersebut
|
||
|
(typecase 1
|
||
|
(string :string)
|
||
|
(integer :int))
|
||
|
; => :int
|
||
|
|
||
|
|
||
|
;;; Loop
|
||
|
|
||
|
;;; Recursion
|
||
|
|
||
|
(defun fact (n)
|
||
|
(if (< n 2)
|
||
|
1
|
||
|
(* n (fact(- n 1)))))
|
||
|
|
||
|
(fact 5) ; => 120
|
||
|
|
||
|
;;; Iteration
|
||
|
|
||
|
(defun fact (n)
|
||
|
(loop :for result = 1 :then (* result i)
|
||
|
:for i :from 2 :to n
|
||
|
:finally (return result)))
|
||
|
|
||
|
(fact 5) ; => 120
|
||
|
|
||
|
(loop :for x :across "abc" :collect x)
|
||
|
; => (#\a #\b #\c #\d)
|
||
|
|
||
|
(dolist (i '(1 2 3 4))
|
||
|
(format t "~A" i))
|
||
|
; => 1234
|
||
|
|
||
|
|
||
|
;;;-----------------------------------------------------------------------------
|
||
|
;;; 6. Mutation
|
||
|
;;;-----------------------------------------------------------------------------
|
||
|
|
||
|
;;; Guna SETF untuk meletakkan nilai baru untuk variable yang sedia ada. Ini sama
|
||
|
;;; seperti contoh hash table di atas.
|
||
|
|
||
|
(let ((variable 10))
|
||
|
(setf variable 2))
|
||
|
; => 2
|
||
|
|
||
|
;;; Sebaik-baiknya kurangkan penggunaan destructive function dan elakkan
|
||
|
;;; mutation jika boleh.
|
||
|
|
||
|
|
||
|
;;;-----------------------------------------------------------------------------
|
||
|
;;; 7. Class dan object
|
||
|
;;;-----------------------------------------------------------------------------
|
||
|
|
||
|
;;; Takde dah class untuk haiwan. Jom buat Human-Powered Mechanical
|
||
|
;;; Conveyances (Kenderaan Mekanikal Berkuasa Manusia).
|
||
|
|
||
|
(defclass human-powered-conveyance ()
|
||
|
((velocity
|
||
|
:accessor velocity
|
||
|
:initarg :velocity)
|
||
|
(average-efficiency
|
||
|
:accessor average-efficiency
|
||
|
:initarg :average-efficiency))
|
||
|
(:documentation "A human powered conveyance"))
|
||
|
|
||
|
;;; Argument untuk DEFCLASS, mengikut susunan ialah:
|
||
|
;;; 1. nama class
|
||
|
;;; 2. list untuk superclass
|
||
|
;;; 3. list untuk slot
|
||
|
;;; 4. specifier optional (tidak wajib)
|
||
|
|
||
|
;;; Apabile list untuk superclass tidak ditetapkan, list yang kosong bermaksud
|
||
|
;;; class standard-object. Ini *boleh* ditukar, kalau anda tahu apa yang anda buat.
|
||
|
;;; Baca Art of the Metaobject Protocol untuk maklumat lebih lanjut.
|
||
|
|
||
|
(defclass bicycle (human-powered-conveyance)
|
||
|
((wheel-size
|
||
|
:accessor wheel-size
|
||
|
:initarg :wheel-size
|
||
|
:documentation "Diameter of the wheel.")
|
||
|
(height
|
||
|
:accessor height
|
||
|
:initarg :height)))
|
||
|
|
||
|
(defclass recumbent (bicycle)
|
||
|
((chain-type
|
||
|
:accessor chain-type
|
||
|
:initarg :chain-type)))
|
||
|
|
||
|
(defclass unicycle (human-powered-conveyance) nil)
|
||
|
|
||
|
(defclass canoe (human-powered-conveyance)
|
||
|
((number-of-rowers
|
||
|
:accessor number-of-rowers
|
||
|
:initarg :number-of-rowers)))
|
||
|
|
||
|
;;; Panggilan DESCRIBE kepada class HUMAN-POWERED-CONVEYANCE di REPL akan memberi:
|
||
|
|
||
|
(describe 'human-powered-conveyance)
|
||
|
|
||
|
; COMMON-LISP-USER::HUMAN-POWERED-CONVEYANCE
|
||
|
; [symbol]
|
||
|
;
|
||
|
; HUMAN-POWERED-CONVEYANCE names the standard-class #<STANDARD-CLASS
|
||
|
; HUMAN-POWERED-CONVEYANCE>:
|
||
|
; Documentation:
|
||
|
; A human powered conveyance
|
||
|
; Direct superclasses: STANDARD-OBJECT
|
||
|
; Direct subclasses: UNICYCLE, BICYCLE, CANOE
|
||
|
; Not yet finalized.
|
||
|
; Direct slots:
|
||
|
; VELOCITY
|
||
|
; Readers: VELOCITY
|
||
|
; Writers: (SETF VELOCITY)
|
||
|
; AVERAGE-EFFICIENCY
|
||
|
; Readers: AVERAGE-EFFICIENCY
|
||
|
; Writers: (SETF AVERAGE-EFFICIENCY)
|
||
|
|
||
|
;;; Perhatikan apa yang berlaku. CL memang direka sebagai sistem interaktif.
|
||
|
|
||
|
;;; Untuk membuat method, jom kira berapa panjang lilitan untuk
|
||
|
;;; roda basikal menggunakan formula: C = d * pi
|
||
|
|
||
|
(defmethod circumference ((object bicycle))
|
||
|
(* pi (wheel-size object)))
|
||
|
|
||
|
;;; Nilai PI memang sudah ada dalam CL
|
||
|
|
||
|
;;; Katakanlah kita ingin ambil tahu efficiency value (nilai keberkesanan)
|
||
|
;;; rower (pendayung) di dalam canoe (perahu) adalah berbentuk logarithmic. Ini
|
||
|
;;; boleh ditetapkan di dalam constructor/initializer.
|
||
|
|
||
|
;;; Untuk initialize instance selepas CL sudah siap construct:
|
||
|
|
||
|
(defmethod initialize-instance :after ((object canoe) &rest args)
|
||
|
(setf (average-efficiency object) (log (1+ (number-of-rowers object)))))
|
||
|
|
||
|
;;; Kemudian untuk construct sesebuah instance dan periksa purata efficiency...
|
||
|
|
||
|
(average-efficiency (make-instance 'canoe :number-of-rowers 15))
|
||
|
; => 2.7725887
|
||
|
|
||
|
|
||
|
;;;-----------------------------------------------------------------------------
|
||
|
;;; 8. Macro
|
||
|
;;;-----------------------------------------------------------------------------
|
||
|
|
||
|
;;; Macro membolehkan anda untuk menambah syntax language. CL tidak ada
|
||
|
;;; WHILE loop, tetapi, kita boleh mencipta syntax ter. Jika kita buat menggunakan
|
||
|
;;; naluri, kita akan dapat:
|
||
|
|
||
|
(defmacro while (condition &body body)
|
||
|
"While `condition` is true, `body` is executed.
|
||
|
`condition` is tested prior to each execution of `body`"
|
||
|
(let ((block-name (gensym)) (done (gensym)))
|
||
|
`(tagbody
|
||
|
,block-name
|
||
|
(unless ,condition
|
||
|
(go ,done))
|
||
|
(progn
|
||
|
,@body)
|
||
|
(go ,block-name)
|
||
|
,done)))
|
||
|
|
||
|
;;; Jom lihat versi yang lebih high-level:
|
||
|
|
||
|
(defmacro while (condition &body body)
|
||
|
"While `condition` is true, `body` is executed.
|
||
|
`condition` is tested prior to each execution of `body`"
|
||
|
`(loop while ,condition
|
||
|
do
|
||
|
(progn
|
||
|
,@body)))
|
||
|
|
||
|
;;; Namun, dengan compiler yang modern, cara ini tidak diperlukan; form LOOP
|
||
|
;;; compile sama sahaja dan juga mudah dibaca.
|
||
|
|
||
|
;;; Perhatikan ``` digunakan, sama juga `,` dan `@`. ``` ialah operator jenis quote
|
||
|
;;; yang dipanggil quasiquote; operator tersebut membolehkan penggunaan `,` .
|
||
|
;;; `,` membolehkan variable "di-unquote-kan". @ mengembangkan list.
|
||
|
|
||
|
;;; GENSYM membuat simbol unik yang pasti tidak wujud di tempat-tempat yang
|
||
|
;;; lain. Ini kerana macro dikembangkan semasa compile dan
|
||
|
;;; nama variable di dalam macro boleh bertembung dengan nama variable yang
|
||
|
;;; digunakan dalam code yang biasa.
|
||
|
|
||
|
;;; Baca Practical Common Lisp dan On Lisp untuk maklumat lebih lanjut mengenai macro.
|
||
|
```
|
||
|
|
||
|
|
||
|
## Bacaan lanjut
|
||
|
|
||
|
- [Practical Common Lisp](http://www.gigamonkeys.com/book/)
|
||
|
- [Common Lisp: A Gentle Introduction to Symbolic Computation](https://www.cs.cmu.edu/~dst/LispBook/book.pdf)
|
||
|
|
||
|
|
||
|
## Maklumat tambahan
|
||
|
|
||
|
- [CLiki](http://www.cliki.net/)
|
||
|
- [common-lisp.net](https://common-lisp.net/)
|
||
|
- [Awesome Common Lisp](https://github.com/CodyReichert/awesome-cl)
|
||
|
- [Lisp Lang](http://lisp-lang.org/)
|
||
|
|
||
|
|
||
|
## Kredit
|
||
|
|
||
|
Terima kasih banyak diucapkan kepada ahli Scheme yang membuat permulaan yang sangat
|
||
|
bagus dan mudah untuk diguna pakai untuk Common Lisp.
|
||
|
|
||
|
- [Paul Khuong](https://github.com/pkhuong) untuk review yang bagus.
|