---
language: clojure
filename: learnclojure-ms.clj
contributors:
    - ["Adam Bard", "http://adambard.com/"]
translators:
    - ["Burhanuddin Baharuddin", "https://github.com/burhanloey"]
lang: ms-my
---

Clojure ialah salah satu bahasa pengaturcaraan dalam keluarga Lisp yang dibangunkan untuk Java Virtual Machine. Ia lebih
menekankan kepada konsep [functional programming](https://en.wikipedia.org/wiki/Functional_programming) jika dibandingkan
dengan Common Lisp, tetapi juga menyediakan kemudahan [STM](https://en.wikipedia.org/wiki/Software_transactional_memory) 
untuk mengendalikan *state* apabila diperlukan.

Gabungan tersebut membolehkan Clojure untuk mengendalikan beberapa proses serentak (*concurrency*) dengan mudah,
dan kebiasaannya secara automatik.

(Anda perlukan Clojure versi 1.2 ke atas)


```clojure
; Komen bermula dengan koma bertitik (semicolon).

; Clojure ditulis dalam bentuk yang seragam, iaitu
; senarai perkataan di dalam kurungan (parentheses), dipisahkan dengan ruang kosong (whitespace).
;
; Pembaca Clojure akan menganggap bahawa perkataan pertama dalam senarai tersebut
; sebagai `function` atau `macro` untuk digunakan, dan yang selebihnya sebagai arguments.

; Panggilan pertama di dalam fail Clojure mestilah bermula dengan ns, untuk menentukan `namespace`
(ns learnclojure)

; Contoh-contoh asas yang lain:

; str akan mewujudkan sebuah string daripada beberapa `argument`
(str "Hello" " " "World") ; => "Hello World"

; Operasi matematik pun mudah
(+ 1 1) ; => 2
(- 2 1) ; => 1
(* 1 2) ; => 2
(/ 2 1) ; => 2

; Tanda = boleh digunakan untuk membuat perbandingan yang sama
(= 1 1) ; => true
(= 2 1) ; => false

; Gunakan not untuk mengubah lojik
(not true) ; => false

; Bentuk `nested` berfungsi seperti yang dijangkakan
(+ 1 (- 3 2)) ; = 1 + (3 - 2) => 2

; Type (Jenis Data)
;;;;;;;;;;;;;

; Clojure menggunakan jenis `object` dari Java untuk `boolean`, `string` dan nombor.
; Gunakan `class` untuk memeriksa jenis sesebuah data.
(class 1) ; Secara default jenis data untuk `Integer` ialah java.lang.Long
(class 1.); Jenis data untuk Float pula ialah java.lang.Double
(class ""); `String` sentiasa berada dalam tanda petikan (quotation mark), dan merupakan java.lang.String
(class false) ; `Boolean` ialah java.lang.Boolean
(class nil); Nilai "null" dipanggil nil

; Jika mahu membuat senarai data secara harfiah, gunakan ' untuk elakkan senarai tersebut
; daripada terus berfungsi
'(+ 1 2) ; => (+ 1 2)
; (singkatan untuk (quote (+ 1 2)))

; Senarai data secara harfiah boleh berfungsi menggunakan eval
(eval '(+ 1 2)) ; => 3

; Collection & Sequence (Koleksi & Urutan)
;;;;;;;;;;;;;;;;;;;

; `List` ialah struktur data `linked-list`, manakala `Vector` pula berasaskan `array`.
; `Vector` dan `List` juga merupakan class dari Java!
(class [1 2 3]); => clojure.lang.PersistentVector
(class '(1 2 3)); => clojure.lang.PersistentList

; Sesebuah list boleh ditulis seperti (1 2 3), tetapi kita perlu meletakkan '
; untuk mengelakkannya daripada berfungsi.
; Juga, (list 1 2 3) adalah sama dengan '(1 2 3)

; "Collections" hanyalah kumpulan data
; Kedua-dua list dan vector ialah collection:
(coll? '(1 2 3)) ; => true
(coll? [1 2 3]) ; => true

; "Sequences" (seq) ialah kriteria untuk sesebuah senarai data.
; Hanya list yang dikira sebagai seq.
(seq? '(1 2 3)) ; => true
(seq? [1 2 3]) ; => false

; Sesebuah seq hanya perlukan satu kemasukan data untuk diakses.
; Jadi, seq yang boleh jadi `lazy` (malas) -- boleh menjadi tidak terkira (infinite):
(range 4) ; => (0 1 2 3)
(range) ; => (0 1 2 3 4 ...) (tiada penghujung)
(take 4 (range)) ;  (0 1 2 3)

; Gunakan cons untuk menambah sesuatu di awal sesebuah list atau vector
(cons 4 [1 2 3]) ; => (4 1 2 3)
(cons 4 '(1 2 3)) ; => (4 1 2 3)

; Conj akan menambah sesuatu ke dalam collection dengan paling berkesan.
; Untuk list, data tersebut dimasukkan di permulaan. Untuk vector, dimasukkan di pengakhiran.
(conj [1 2 3] 4) ; => [1 2 3 4]
(conj '(1 2 3) 4) ; => (4 1 2 3)

; Gunakan concat untuk menggabungkan list atau vector
(concat [1 2] '(3 4)) ; => (1 2 3 4)

; Gunakan filter dan map untuk berinteraksi dengan data di dalam collection
(map inc [1 2 3]) ; => (2 3 4)
(filter even? [1 2 3]) ; => (2)

; Gunakan reduce untuk dikecilkan (kepada satu nilai)
(reduce + [1 2 3 4])
; = (+ (+ (+ 1 2) 3) 4)
; => 10

; Reduce boleh diberi nilai permulaan
(reduce conj [] '(3 2 1))
; = (conj (conj (conj [] 3) 2) 1)
; => [3 2 1]

; Function
;;;;;;;;;;;;;;;;;;;;;

; Gunakan fn untuk membuat `function`. Sesebuah function pasti memulangkan semula
; hasil daripada barisan yang terakhir.
(fn [] "Hello World") ; => fn

; (Anda perlukan satu lagi kurungan supaya function tersebut dikira)
((fn [] "Hello World")) ; => "Hello World"

; Anda boleh membuat var menggunakan def
(def x 1)
x ; => 1

; Tetapkan sebuah function ke dalam var
(def hello-world (fn [] "Hello World"))
(hello-world) ; => "Hello World"

; Proses di atas boleh diringkaskan menggunakan defn
(defn hello-world [] "Hello World")

; Tanda [] merupakan senarai argument untuk function tersebut.
(defn hello [name]
  (str "Hello " name))
(hello "Steve") ; => "Hello Steve"

; Cara ini juga boleh digunakan untuk membuat function dengan lebih ringkas:
(def hello2 #(str "Hello " %1))
(hello2 "Fanny") ; => "Hello Fanny"

; Anda juga boleh membuat satu function yang mempunyai beberapa bilangan argument
(defn hello3
  ([] "Hello World")
  ([name] (str "Hello " name)))
(hello3 "Jake") ; => "Hello Jake"
(hello3) ; => "Hello World"

; Function boleh diberi argument ekstra dalam bentuk seq
(defn count-args [& args]
  (str "You passed " (count args) " args: " args))
(count-args 1 2 3) ; => "You passed 3 args: (1 2 3)"

; Anda boleh letakkan sekali argument biasa dan argument ekstra
(defn hello-count [name & args]
  (str "Hello " name ", you passed " (count args) " extra args"))
(hello-count "Finn" 1 2 3)
; => "Hello Finn, you passed 3 extra args"


; Map
;;;;;;;;;;

; Hash map dan array map menggunakan `interface` yang sama. Hash map lebih laju untuk diakses
; tetapi tidak mengekalkan urutan.
(class {:a 1 :b 2 :c 3}) ; => clojure.lang.PersistentArrayMap
(class (hash-map :a 1 :b 2 :c 3)) ; => clojure.lang.PersistentHashMap

; Arraymap akan bertukar menjadi hashmap secara automatik untuk kebanyakan operasi
; apabila mereka menjadi semakin besar, jadi anda tidak perlu bimbang.

; Map boleh menggunakan apa-apa sahaja jenis data sebagai key, tetapi kebiasaannya keyword adalah yang terbaik
; Keyword adalah sama seperti string cuma lebih efisyen
(class :a) ; => clojure.lang.Keyword

(def stringmap {"a" 1, "b" 2, "c" 3})
stringmap  ; => {"a" 1, "b" 2, "c" 3}

(def keymap {:a 1, :b 2, :c 3})
keymap ; => {:a 1, :c 3, :b 2}

; Oh, sebelum terlupa, tanda koma di atas hanya dianggap seperti whitespace, tak buat apa-apa.
; Dapatkan nilai daripada map dengan menggunakannya seperti function
(stringmap "a") ; => 1
(keymap :a) ; => 1

; Keyword juga boleh digunakan untuk mendapatkan nilai daripada map tersebut!
(:b keymap) ; => 2

; Jangan cuba teknik di atas menggunakan string, tak jadi.
;("a" stringmap)
; => Exception: java.lang.String cannot be cast to clojure.lang.IFn

; Apabila key yang digunakan tidak wujud, map akan memberi nil
(stringmap "d") ; => nil

; Gunakan assoc untuk menambah key yang baru ke dalam hash-map
(def newkeymap (assoc keymap :d 4))
newkeymap ; => {:a 1, :b 2, :c 3, :d 4}

; Tetapi ingat, data dalam clojure adalah `immutable` (tidak berubah)!
keymap ; => {:a 1, :b 2, :c 3}

; Gunakan dissoc untuk membuang key
(dissoc keymap :a :b) ; => {:c 3}

; Set
;;;;;;

(class #{1 2 3}) ; => clojure.lang.PersistentHashSet
(set [1 2 3 1 2 3 3 2 1 3 2 1]) ; => #{1 2 3}

; Tambah data menggunakan conj
(conj #{1 2 3} 4) ; => #{1 2 3 4}

; Buang data menggunakan disj
(disj #{1 2 3} 1) ; => #{2 3}

; Periksa kewujudan data dengan menggunakan set tersebut sebagai function:
(#{1 2 3} 1) ; => 1
(#{1 2 3} 4) ; => nil

; Ada pelbagai lagi function untuk set di namespace clojure.sets.

; Form yang berguna
;;;;;;;;;;;;;;;;;

; Lojik dalam clojure hanyalah sebuah macro, dan kelihatan seperti
; yang lain
(if false "a" "b") ; => "b"
(if false "a") ; => nil

; Gunakan let untuk membuat binding sementara
(let [a 1 b 2]
  (> a b)) ; => false

; Kumpulkan beberapa statement sekali menggunakan do
(do
  (print "Hello")
  "World") ; => "World" (prints "Hello")

; Function sebenarnya ada do secara tersirat
(defn print-and-say-hello [name]
  (print "Saying hello to " name)
  (str "Hello " name))
(print-and-say-hello "Jeff") ;=> "Hello Jeff" (prints "Saying hello to Jeff")

; Let pun sama
(let [name "Urkel"]
  (print "Saying hello to " name)
  (str "Hello " name)) ; => "Hello Urkel" (prints "Saying hello to Urkel")


; Gunakan `threading macro` (-> dan ->>) untuk menulis penggubahan data
; dengan lebih jelas.

; Macro "thread-first" (->) memasukkan hasil perkiraan ke setiap form
; yang selepasnya, sebagai argument pertama (item yang kedua)
(->  
   {:a 1 :b 2} 
   (assoc :c 3) ;=> (assoc {:a 1 :b 2} :c 3)
   (dissoc :b)) ;=> (dissoc (assoc {:a 1 :b 2} :c 3) :b)

; Code di atas boleh ditulis seperti ini:
; (dissoc (assoc {:a 1 :b 2} :c 3) :b)
; dan hasilnya ialah {:a 1 :c 3}

; Yang dua anak panah pula membuat benda yang sama, tetapi memasukkan hasil perkiraan 
; setiap baris ke pengakhiran form selepasnya. Cara ini berguna untuk operasi 
; yang melibatkan collection:
(->>
   (range 10)
   (map inc)     ;=> (map inc (range 10)
   (filter odd?) ;=> (filter odd? (map inc (range 10))
   (into []))    ;=> (into [] (filter odd? (map inc (range 10)))
                 ; Result: [1 3 5 7 9]

; Jika anda mahu lebih fleksibel untuk meletakkan hasil perkiraan,
; anda boleh menggunakan macro `as->`. Dengan menggunakan macro tersebut,
; anda boleh menentukan nama untuk output dan menggunakannya semula
; ke dalam operasi berangkai:

(as-> [1 2 3] input
  (map inc input);=> You can use last transform's output at the last position
  (nth input 2) ;=>  and at the second position, in the same expression
  (conj [4 5 6] input [8 9 10])) ;=> or in the middle !



; Module
;;;;;;;;;;;;;;;

; Gunakan "use" untuk mendapatkan semua function daripada sesebuah module
(use 'clojure.set)

; Sekarang kita boleh menggunakan operasi untuk set
(intersection #{1 2 3} #{2 3 4}) ; => #{2 3}
(difference #{1 2 3} #{2 3 4}) ; => #{1}

; Anda juga boleh memilih sebahagian daripada function untuk diimport
(use '[clojure.set :only [intersection]])

; Gunakan require untuk mengimport sesebuah module
(require 'clojure.string)

; Gunakan / untuk menggunakan function daripada module
; Di sini, nama module tersebut ialah clojure.string dan function-nya ialah blank?
(clojure.string/blank? "") ; => true

; Anda juga boleh memberi nama yang lebih ringkas untuk module semasa import
(require '[clojure.string :as str])
(str/replace "This is a test." #"[a-o]" str/upper-case) ; => "THIs Is A tEst."
; (#"" ialah ungkapan untuk regular expression, regex)

; Anda boleh menggunakan require (dan use, tetapi elakkan) daripada namespace menggunakan :require.
; Anda tidak perlu menulis semula nama module dengan cara ini.
(ns test
  (:require
    [clojure.string :as str]
    [clojure.set :as set]))

; Java
;;;;;;;;;;;;;;;;;

; Java mengandungi banyak standard library yang kita boleh manfaatkan, jadi
; anda patut tahu bagaimana untuk menggunakannya.

; Gunakan import untuk load module java
(import java.util.Date)

; Anda juga boleh import menggunakan ns.
(ns test
  (:import java.util.Date
           java.util.Calendar))

; Gunakan nama class berserta "." di hujungnya untuk membuat object baru
(Date.) ; <object date>

; Gunakan . untuk menggunakan method. Atau gunakan shortcut seperti ".method"
(. (Date.) getTime) ; <sebuah timestamp>
(.getTime (Date.)) ; sama sahaja.

; Gunakan / untuk menggunakan static method
(System/currentTimeMillis) ; <sebuah timestamp> (System sentiasa wujud dalam Java)

; Gunakan doto untuk menjadikan proses yang melibatkan class mutable (boleh berubah) lebih mudah
(import java.util.Calendar)
(doto (Calendar/getInstance)
  (.set 2000 1 1 0 0 0)
  .getTime) ; => Sebuah Date. yang ditetapkan kepada 2000-01-01 00:00:00

; STM
;;;;;;;;;;;;;;;;;

; Software Transactional Memory ialah mekanisme dalam Clojure untuk mengendalikan
; state yang kekal berterusan. Ada beberapa kaedah dalam Clojure yang menggunakan teknik tersebut.

; Atom adalah yang paling mudah. Letakkannya sewaktu meletakkan nilai permulaan.
(def my-atom (atom {}))

; Kemas kini sebuah atom menggunakan swap!.
; swap! mengambil satu function dan menggunakannya menggunakan nilai asal atom
; sebagai argument pertama, dan argument selebihnya sebagai argument kedua
(swap! my-atom assoc :a 1) ; Tetapkan my-atom kepada hasil perkiraan (assoc {} :a 1)
(swap! my-atom assoc :b 2) ; Tetapkan my-atom kepada hasil perkiraan (assoc {:a 1} :b 2)

; Gunakan '@' untuk mendapatkan nilai daripada atom
my-atom  ;=> Atom<#...> (memberi object atom itu sendiri)
@my-atom ; => {:a 1 :b 2}

; Ini adalah contoh untuk mengira menggunakan atom
(def counter (atom 0))
(defn inc-counter []
  (swap! counter inc))

(inc-counter)
(inc-counter)
(inc-counter)
(inc-counter)
(inc-counter)

@counter ; => 5

; Kaedah lain yang menggunakan STM ialah ref dan agent.
; Ref: http://clojure.org/refs
; Agent: http://clojure.org/agents
```

### Bacaan Lanjut

Ini masih belum lengkap, tetapi harap-harap cukup untuk membuatkan anda lebih bersedia.

Clojure.org mempunyai banyak artikel:
[http://clojure.org/](http://clojure.org/)

Clojuredocs.org mempunyai dokumentasi berserta contoh untuk menggunakan kebanyakan function teras:
[http://clojuredocs.org/quickref/Clojure%20Core](http://clojuredocs.org/quickref/Clojure%20Core)

4Clojure ialah cara yang baik untuk mengasah skill Clojure dan functional programming:
[https://4clojure.oxal.org/](https://4clojure.oxal.org/)

Clojure-doc.org (yup, serius) juga mengandungi beberapa artikel sebagai permulaan:
[http://clojure-doc.org/](http://clojure-doc.org/)
