diff options
author | AlburIvan <albur.ivan@outlook.com> | 2018-10-28 03:00:26 -0400 |
---|---|---|
committer | AlburIvan <albur.ivan@outlook.com> | 2018-10-28 03:00:26 -0400 |
commit | df0a0fa2a6ea1b818e3c6aaa139f77fde41f0256 (patch) | |
tree | c912eebef5bf5bfd45d4c2b6c91f355157041de8 /ms-my | |
parent | 73b8cd9a39ab9b63dd5e2c1c123c363fd014753a (diff) | |
parent | 27fa7c50ce23def736a69711f827918acc726e37 (diff) |
Merge branch 'master' of github.com:adambard/learnxinyminutes-docs into matlab-es
Diffstat (limited to 'ms-my')
-rw-r--r-- | ms-my/clojure-macros-my.html.markdown | 154 | ||||
-rw-r--r-- | ms-my/common-lisp-my.html.markdown | 692 | ||||
-rw-r--r-- | ms-my/elisp-my.html.markdown | 347 |
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 +``` |