summaryrefslogtreecommitdiffhomepage
path: root/ms-my
diff options
context:
space:
mode:
Diffstat (limited to 'ms-my')
-rw-r--r--ms-my/clojure-macros-my.html.markdown154
-rw-r--r--ms-my/common-lisp-my.html.markdown692
-rw-r--r--ms-my/elisp-my.html.markdown347
3 files changed, 1193 insertions, 0 deletions
diff --git a/ms-my/clojure-macros-my.html.markdown b/ms-my/clojure-macros-my.html.markdown
new file mode 100644
index 00000000..099f376e
--- /dev/null
+++ b/ms-my/clojure-macros-my.html.markdown
@@ -0,0 +1,154 @@
+---
+language: "clojure macros"
+filename: learnclojuremacros-ms.clj
+contributors:
+ - ["Adam Bard", "http://adambard.com/"]
+translators:
+ - ["Burhanuddin Baharuddin", "https://github.com/burhanloey"]
+lang: ms-my
+---
+
+Sama seperti Lisp yang lain, sifat Clojure yang mempunyai [homoiconicity](https://en.wikipedia.org/wiki/Homoiconic)
+membolehkan anda untuk menggunakan sepenuhnya language ini untuk menulis code yang boleh generate code sendiri yang
+dipanggil "macro". Macro memberi cara yang sangat menarik untuk mengubahsuai language mengikut kehendak anda.
+
+Jaga-jaga. Penggunaan macro boleh dikatakan tidak elok jika digunakan secara berlebihan jika function sahaja sudah mencukupi.
+Gunakan macro hanya apabila anda mahu lebih kawalan terhadap sesuatu form.
+
+Biasakan diri dengan Clojure terlebih dahulu. Pastikan anda memahami semuanya di
+[Clojure in Y Minutes](/docs/ms-my/clojure-my/).
+
+```clojure
+;; Define macro menggunakan defmacro. Macro anda akan output list yang boleh
+;; dijalankan sebagai code clojure.
+;;
+;; Macro ini adalah sama seperti (reverse "Hello World")
+(defmacro my-first-macro []
+ (list reverse "Hello World"))
+
+;; Lihat hasil macro tersebut menggunakan macroexpand atau macroexpand-1.
+;;
+;; Pastikan panggilan kepada macro tersebut mempunyai tanda petikan
+(macroexpand '(my-first-macro))
+;; -> (#<core$reverse clojure.core$reverse@xxxxxxxx> "Hello World")
+
+;; Anda boleh menggunakan eval terus kepada macroexpand untuk mendapatkan hasil:
+(eval (macroexpand '(my-first-macro)))
+; -> (\d \l \o \r \W \space \o \l \l \e \H)
+
+;; Tetapi anda sepatutnya menggunakan cara yang lebih ringkas, sama seperti panggilan kepada function:
+(my-first-macro) ; -> (\d \l \o \r \W \space \o \l \l \e \H)
+
+;; Anda boleh memudahkan cara untuk membuat macro dengan mengguna tanda petikan
+;; untuk membuat list untuk macro:
+(defmacro my-first-quoted-macro []
+ '(reverse "Hello World"))
+
+(macroexpand '(my-first-quoted-macro))
+;; -> (reverse "Hello World")
+;; Perhatikan yang reverse bukan lagi function tetapi adalah simbol.
+
+;; Macro boleh mengambil argument.
+(defmacro inc2 [arg]
+ (list + 2 arg))
+
+(inc2 2) ; -> 4
+
+;; Tetapi jika anda membuat cara yang sama menggunakan tanda petikan, anda akan mendapat error sebab
+;; argument tersebut juga akan mempunyai tanda petikan. Untuk mengatasi masalah ini, Clojure memberi
+;; cara untuk meletak tanda petikan untuk macro: `. Di dalam `, anda boleh menggunakan ~ untuk mendapatkan scope luaran
+(defmacro inc2-quoted [arg]
+ `(+ 2 ~arg))
+
+(inc2-quoted 2)
+
+;; Anda boleh menggunakan destructuring untuk argument seperti biasa. Gunakan ~@ untuk mengembangkan variable
+(defmacro unless [arg & body]
+ `(if (not ~arg)
+ (do ~@body))) ; Jangan lupa do!
+
+(macroexpand '(unless true (reverse "Hello World")))
+;; ->
+;; (if (clojure.core/not true) (do (reverse "Hello World")))
+
+;; (unless) mengembalikan body jika argument yang pertama adalah false.
+;; Jika tidak, (unless) akan memulangkan nil
+
+(unless true "Hello") ; -> nil
+(unless false "Hello") ; -> "Hello"
+
+;; Jika tidak berhati-hati, macro boleh memeningkan anda dengan mencampuradukkan nama variable
+(defmacro define-x []
+ '(do
+ (def x 2)
+ (list x)))
+
+(def x 4)
+(define-x) ; -> (2)
+(list x) ; -> (2)
+
+;; Untuk mengelakkan masalah ini, gunakan gensym untuk mendapatkan identifier yang berbeza
+(gensym 'x) ; -> x1281 (atau yang sama waktu dengannya)
+
+(defmacro define-x-safely []
+ (let [sym (gensym 'x)]
+ `(do
+ (def ~sym 2)
+ (list ~sym))))
+
+(def x 4)
+(define-x-safely) ; -> (2)
+(list x) ; -> (4)
+
+;; Anda boleh menggunakan # di dalam ` untuk menghasilkan gensym untuk setiap simbol secara automatik
+(defmacro define-x-hygienically []
+ `(do
+ (def x# 2)
+ (list x#)))
+
+(def x 4)
+(define-x-hygienically) ; -> (2)
+(list x) ; -> (4)
+
+;; Kebiasaannya helper function digunakan untuk membuat macro. Jom buat beberapa function untuk
+;; membuatkan program boleh memahami inline arithmetic. Saja suka-suka.
+(declare inline-2-helper)
+(defn clean-arg [arg]
+ (if (seq? arg)
+ (inline-2-helper arg)
+ arg))
+
+(defn apply-arg
+ "Diberi argument [x (+ y)], pulangkan (+ x y)"
+ [val [op arg]]
+ (list op val (clean-arg arg)))
+
+(defn inline-2-helper
+ [[arg1 & ops-and-args]]
+ (let [ops (partition 2 ops-and-args)]
+ (reduce apply-arg (clean-arg arg1) ops)))
+
+;; Kita boleh test terlebih dahulu tanpa membuat macro
+(inline-2-helper '(a + (b - 2) - (c * 5))) ; -> (- (+ a (- b 2)) (* c 5))
+
+; Tetapi, kita perlu membuat macro jika kita mahu jalankan code tersebut
+(defmacro inline-2 [form]
+ (inline-2-helper form))
+
+(macroexpand '(inline-2 (1 + (3 / 2) - (1 / 2) + 1)))
+; -> (+ (- (+ 1 (/ 3 2)) (/ 1 2)) 1)
+
+(inline-2 (1 + (3 / 2) - (1 / 2) + 1))
+; -> 3 (sepatutnya, 3N, sebab nombor tersebut ditukarkan kepada pecahan rasional menggunakan /)
+```
+
+### Bacaaan Lanjut
+
+Writing Macros daripada [Clojure for the Brave and True](http://www.braveclojure.com/)
+[http://www.braveclojure.com/writing-macros/](http://www.braveclojure.com/writing-macros/)
+
+Dokumen rasmi
+[http://clojure.org/macros](http://clojure.org/macros)
+
+Bila perlu guna macro?
+[http://dunsmor.com/lisp/onlisp/onlisp_12.html](http://dunsmor.com/lisp/onlisp/onlisp_12.html)
diff --git a/ms-my/common-lisp-my.html.markdown b/ms-my/common-lisp-my.html.markdown
new file mode 100644
index 00000000..f5914aae
--- /dev/null
+++ b/ms-my/common-lisp-my.html.markdown
@@ -0,0 +1,692 @@
+---
+
+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.
diff --git a/ms-my/elisp-my.html.markdown b/ms-my/elisp-my.html.markdown
new file mode 100644
index 00000000..73dff0f4
--- /dev/null
+++ b/ms-my/elisp-my.html.markdown
@@ -0,0 +1,347 @@
+---
+language: elisp
+contributors:
+ - ["Bastien Guerry", "https://bzg.fr"]
+ - ["Saurabh Sandav", "http://github.com/SaurabhSandav"]
+translators:
+ - ["Burhanuddin Baharuddin", "https://github.com/burhanloey"]
+lang: ms-my
+filename: learn-emacs-lisp-ms.el
+---
+
+```scheme
+;; Ini adalah pengenalan kepada Emacs Lisp dalam masa 15 minit (v0.2d)
+;;
+;; Mula-mula pastikan anda sudah membaca artikel daripada Peter Norvig ini:
+;; http://norvig.com/21-days.html
+;;
+;; Kemudian install GNU Emacs 24.3:
+;;
+;; Debian: apt-get install emacs (atau lihat arahan untuk distro anda)
+;; OSX: http://emacsformacosx.com/emacs-builds/Emacs-24.3-universal-10.6.8.dmg
+;; Windows: http://ftp.gnu.org/gnu/windows/emacs/emacs-24.3-bin-i386.zip
+;;
+;; Maklumat lanjut boleh didapati di:
+;; http://www.gnu.org/software/emacs/#Obtaining
+
+;; Amaran penting:
+;;
+;; Tutorial ini tidak akan merosakkan komputer anda melainkan jika anda berasa
+;; terlalu marah sehingga anda menghempap komputer anda ke lantai. Kalau begitu,
+;; saya dengan ini tidak akan bertanggungjawab terhadap apa-apa. Berseronoklah ya!
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Buka Emacs.
+;;
+;; Tekan `q' untuk tutup mesej selamat datang.
+;;
+;; Sekarang lihat garis kelabu di bahagian bawah window:
+;;
+;; "*scratch*" ialah nama ruangan untuk anda edit.
+;; Ruangan ini disebut sebagai "buffer".
+;;
+;; Buffer scratch ialah buffer yang default setiap kali Emacs dibuka.
+;; Anda bukannya edit file: anda edit buffer yang kemudiannya
+;; boleh save ke file.
+;;
+;; "Lisp interaction (interaksi)" merujuk kepada command yang wujud di sini.
+;;
+;; Emacs mempunyai beberapa command yang sedia ada dalam setiap buffer,
+;; dan sesetengah command yang lain boleh didapati jika sesetengah mode
+;; diaktifkan. Di sini kita menggunakan `lisp-interaction-mode', yang
+;; mempunyai command untuk menjalankan dan mengendalikan code Elisp.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Semicolon akan menjadikan comment sepanjang baris tersebut.
+;;
+;; Program Elisp mengandungi symbolic expressions ("sexps"):
+(+ 2 2)
+
+;; Symbolic expression di atas dibaca begini "Tambah 2 pada 2".
+
+;; Sexps dilitupi oleh parentheses, dan boleh dalam bentuk nested (parentheses
+;; dalam parentheses):
+(+ 2 (+ 1 1))
+
+;; Symbolic expression mengandungi atom atau symbolic expression
+;; yang lain. Untuk contoh di atas, 1 dan 2 ialah atom,
+;; (+ 2 (+ 1 1)) dan (+ 1 1) ialah symbolic expression.
+
+;; Dengan menggunakan `lisp-interaction-mode', anda boleh evaluate
+;; (mendapatkan hasil pengiraan) sexps. Letak cursor selepas parenthesis penutup
+;; kemudian tekan control dan j ("C-j").
+
+(+ 3 (+ 1 2))
+;; ^ cursor di sini
+;; `C-j' => 6
+
+;; `C-j' memasukkan jawapan pengiraan ke dalam buffer.
+
+;; `C-xC-e' memaparkan jawapan yang sama di bahagian bawah Emacs,
+;; yang dipanggil "minibuffer". Secara umumnya kita akan menggunakan `C-xC-e',
+;; sebab kita tidak mahu memenuhi buffer dengan teks yang tidak penting.
+
+;; `setq' menyimpan value ke dalam variable:
+(setq my-name "Bastien")
+;; `C-xC-e' => "Bastien" (terpapar di mini-buffer)
+
+;; `insert' akan memasukkan "Hello!" di tempat di mana cursor berada:
+(insert "Hello!")
+;; `C-xC-e' => "Hello!"
+
+;; Di atas, kita menggunakan `insert' dengan satu argument "Hello!", tetapi
+;; kita boleh meletakkan beberapa argument -- di sini kita letak dua:
+
+(insert "Hello" " world!")
+;; `C-xC-e' => "Hello world!"
+
+;; Anda boleh menggunakan variable selain string:
+(insert "Hello, I am " my-name)
+;; `C-xC-e' => "Hello, I am Bastien"
+
+;; Anda boleh menggabungkan sexps untuk membuat function:
+(defun hello () (insert "Hello, I am " my-name))
+;; `C-xC-e' => hello
+
+;; Anda boleh evaluate function:
+(hello)
+;; `C-xC-e' => Hello, I am Bastien
+
+;; Parentheses kosong di dalam function bermaksud function tersebut tidak
+;; terima argument. Sekarang kita tukar function untuk menerima satu argument.
+;; Di sini, argument tersebut dinamakan "name":
+
+(defun hello (name) (insert "Hello " name))
+;; `C-xC-e' => hello
+
+;; Sekarang panggil function tersebut dengan string "you" sebagai value
+;; untuk argument:
+(hello "you")
+;; `C-xC-e' => "Hello you"
+
+;; Yay!
+
+;; Tarik nafas.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Sekarang tukar ke buffer baru dengan nama "*test*" di window yang lain:
+
+(switch-to-buffer-other-window "*test*")
+;; `C-xC-e'
+;; => [The screen has two windows and cursor is in the *test* buffer]
+
+;; Gerakkan mouse ke window atas dan klik kiri untuk pergi balik ke buffer scratch.
+;; Cara lain adalah dengan menggunakan `C-xo' (i.e. tekan control-x kemudian
+;; tekan o) untuk pergi ke window yang lain.
+
+;; Anda boleh menggabungkan beberapa sexps menggunakan `progn':
+(progn
+ (switch-to-buffer-other-window "*test*")
+ (hello "you"))
+;; `C-xC-e'
+;; => [The screen has two windows and cursor is in the *test* buffer]
+
+;; Mulai dari sekarang saya tidak akan beritahu anda untuk tekan `C-xC-e' lagi:
+;; buat untuk setiap sexp yang akan datang.
+
+;; Pergi balik ke buffer *scratch* menggunakan mouse atau `C-xo'.
+
+;; Seelok-eloknya padam buffer tersebut:
+(progn
+ (switch-to-buffer-other-window "*test*")
+ (erase-buffer)
+ (hello "there"))
+
+;; Atau pergi balik ke window lain:
+(progn
+ (switch-to-buffer-other-window "*test*")
+ (erase-buffer)
+ (hello "you")
+ (other-window 1))
+
+;; Anda boleh menetapkan value dengan local variable menggunakan `let':
+(let ((local-name "you"))
+ (switch-to-buffer-other-window "*test*")
+ (erase-buffer)
+ (hello local-name)
+ (other-window 1))
+
+;; Tidak perlu menggunakan `progn', sebab `let' juga menggabungkan
+;; beberapa sexps.
+
+;; Jom format string:
+(format "Hello %s!\n" "visitor")
+
+;; %s ialah tempat untuk meletakkan string, digantikan dengan "visitor".
+;; \n ialah character untuk membuat baris baru.
+
+;; Jom tukar function kita menggunakan format:
+(defun hello (name)
+ (insert (format "Hello %s!\n" name)))
+
+(hello "you")
+
+;; Jom buat function lain menggunakan `let':
+(defun greeting (name)
+ (let ((your-name "Bastien"))
+ (insert (format "Hello %s!\n\nI am %s."
+ name ; argument untuk function
+ your-name ; variable "Bastien" daripada let
+ ))))
+
+;; Kemudian evaluate:
+(greeting "you")
+
+;; Sesetengah function adalah interaktif:
+(read-from-minibuffer "Enter your name: ")
+
+;; Function tersebut akan memulangkan kembali apa yang anda masukkan ke prompt.
+
+;; Jom jadikan function `greeting' untuk prompt nama anda:
+(defun greeting (from-name)
+ (let ((your-name (read-from-minibuffer "Enter your name: ")))
+ (insert (format "Hello!\n\nI am %s and you are %s."
+ from-name ; argument untuk function
+ your-name ; variable daripada let, yang dimasukkan dari prompt
+ ))))
+
+(greeting "Bastien")
+
+;; Jom siapkan function dengan memaparkan result di window yang lain:
+(defun greeting (from-name)
+ (let ((your-name (read-from-minibuffer "Enter your name: ")))
+ (switch-to-buffer-other-window "*test*")
+ (erase-buffer)
+ (insert (format "Hello %s!\n\nI am %s." your-name from-name))
+ (other-window 1)))
+
+;; Test function tersebut:
+(greeting "Bastien")
+
+;; Tarik nafas.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Jom simpan senarai nama:
+;; Jika anda ingin membuat list(senarai) data, guna ' untuk elak
+;; daripada list tersebut evaluate.
+(setq list-of-names '("Sarah" "Chloe" "Mathilde"))
+
+;; Dapatkan elemen pertama daripada list menggunakan `car':
+(car list-of-names)
+
+;; Dapatkan semua elemen kecuali yang pertama menggunakan `cdr':
+(cdr list-of-names)
+
+;; Tambah elemen di awal list menggunakan `push':
+(push "Stephanie" list-of-names)
+
+;; NOTA: `car' dan `cdr' tidak ubah suai list, tetapi `push' ya.
+;; Perbezaan ini penting: sesetengah function tiada side-effects (kesan sampingan)
+;; (seperti `car') dan yang lain ada side-effect (seperti `push').
+
+;; Jom panggil `hello' untuk setiap elemen dalam `list-of-names':
+(mapcar 'hello list-of-names)
+
+;; Tukar `greeting' supaya ucapkan hello kepada semua orang dalam `list-of-names':
+(defun greeting ()
+ (switch-to-buffer-other-window "*test*")
+ (erase-buffer)
+ (mapcar 'hello list-of-names)
+ (other-window 1))
+
+(greeting)
+
+;; Ingat lagi function `hello' di atas? Function tersebut mengambil satu
+;; argument, iaitu nama. `mapcar' memanggil `hello', kemudian menggunakan setiap
+;; nama dalam `list-of-names' sebagai argument untuk function `hello'.
+
+;; Sekarang kita susun sedikit untuk apa yang terpapar di buffer:
+
+(defun replace-hello-by-bonjour ()
+ (switch-to-buffer-other-window "*test*")
+ (goto-char (point-min))
+ (while (search-forward "Hello")
+ (replace-match "Bonjour"))
+ (other-window 1))
+
+;; (goto-char (point-min)) akan pergi ke permulaan buffer.
+;; (search-forward "Hello") akan mencari string "Hello".
+;; (while x y) evaluate sexp(s) y selagi x masih pulangkan sesuatu.
+;; Jika x pulangkan `nil', kita akan keluar daripada while loop.
+
+(replace-hello-by-bonjour)
+
+;; Anda akan dapat melihat semua "Hello" dalam buffer *test*
+;; ditukarkan dengan "Bonjour".
+
+;; Anda juga akan dapat error: "Search failed: Hello".
+;;
+;; Bagi mengelakkan error tersebut, anda perlu beritahu `search-forward' sama ada
+;; perlu berhenti mencari pada suatu ketika, dan sama ada perlu diam jika
+;; tidak jumpa apa yang dicari:
+
+;; (search-forward "Hello" nil 't) selesai masalah:
+
+;; Argument `nil' cakap: carian tidak mengikut kedudukan.
+;; Argument `'t' cakap: diam saja jika tidak jumpa apa yang dicari.
+
+;; Kita guna sexp ini di function berikut, barulah tidak keluar error:
+
+(defun hello-to-bonjour ()
+ (switch-to-buffer-other-window "*test*")
+ (erase-buffer)
+ ;; Ucap hello pada nama-nama dalam `list-of-names'
+ (mapcar 'hello list-of-names)
+ (goto-char (point-min))
+ ;; Ganti "Hello" dengan "Bonjour"
+ (while (search-forward "Hello" nil 't)
+ (replace-match "Bonjour"))
+ (other-window 1))
+
+(hello-to-bonjour)
+
+;; Jom jadikan nama-nama tersebut bold:
+
+(defun boldify-names ()
+ (switch-to-buffer-other-window "*test*")
+ (goto-char (point-min))
+ (while (re-search-forward "Bonjour \\(.+\\)!" nil 't)
+ (add-text-properties (match-beginning 1)
+ (match-end 1)
+ (list 'face 'bold)))
+ (other-window 1))
+
+;; Function ini memperkenalkan `re-search-forward': anda mencari menggunakan
+;; pattern iaitu "regular expression", bukannya mencari string "Bonjour".
+
+;; Regular expression tersebut ialah "Bonjour \\(.+\\)!" dan dibaca begini:
+;; string "Bonjour ", dan
+;; kumpulan | ini ialah \\( ... \\)
+;; mana-mana character | ini ialah .
+;; yang boleh berulang | ini ialah +
+;; dan string "!".
+
+;; Dah sedia? Test function tersebut!
+
+(boldify-names)
+
+;; `add-text-properties' tambah... ciri-ciri teks, seperti face.
+
+;; OK, kita sudah selesai. Selamat ber-hacking!
+
+;; Jika anda ingin tahu lebih mengenai variable atau function:
+;;
+;; C-h v a-variable RET
+;; C-h f a-function RET
+;;
+;; Jika anda ingin membaca manual Emacs Lisp menggunakan Emacs:
+;;
+;; C-h i m elisp RET
+;;
+;; Jika ingin membaca pengenalan kepada Emacs Lisp secara online:
+;; https://www.gnu.org/software/emacs/manual/html_node/eintr/index.html
+```