clojure 語法

Clojure 是一種運行在 Java 平臺上的 LISP 方言,LISP 是一種以表達性和功能強大著稱的編程語言。java

 

; 分號做爲註釋的開始
; clojure 用括號把元素括起來,元素之間用空格隔開。clojure 解釋器把第一個元素當作是函數或者宏調用,其餘的元素都做爲參數
; 下面這個函數用於設置當前的命名空間
(ns myclojure)
; 一些簡單的例子:
(str "Hello" " " "World") ; => "Hello World"
(+ 2 1) ; => 3
(- 2 1) ; => 1
(* 2 2) ; => 4
(/ 2 1) ; => 2
;  比較使用 "="
(= 1 1) ; => true
(= 2 1) ; => false
; 邏輯運算
(not true) ; => false
; 嵌套預算
(+ 1 (- 3 2)) ; = 1 + (3 - 2) => 2
; defn 定義一個函數
; doseq 相似 for 循環
(defn Example [] (doseq [n [
0 1 2]] (println n))) (Example)
; 使用let 來建立臨時綁定
(let [a 1 b 2]
  (> a b)) ; => false
; clojure 把布爾值、字符串、數字、向量、列表轉化爲 java 對象
(class 1) ; 整形字面值默認是 java 中的 Long 類型
(class 1.); 浮點字面值對應着 java 中的 Double 類型
(class ""); 字符串老是用雙引號括起來,而且對應着 java 中的 Sring 類型
(class false) ;布爾值對應着 java 中的 Boolean 類型
(class nil); null值被稱爲 nil
(class [1 2 3]) ; => clojure.lang.PersistentVector
(class '(1 2 3)) ; => clojure.lang.PersistentList
; 若是想建立一列數據字面值, 使用一個單引號 ' 來防表達式被解析執行
'(+ 1 2) ; => (+ 1 2) ;這裏沒有返回3
; (上面表達式和(quote (+ 1 2)) 等價,不過更簡潔

; 能夠運算一個引用列表
(eval '(+ 1 2)) ; => 3
;書寫一個列表如(1 2 3)同樣簡單, 可是咱們不得不把它「引」(前面加個單引號)起來
;這樣就能防止解釋器把它當作一個函數來解析
;另外,(list 1 2 3) 和 '(1 2 3) 等價
;列表和向量都是集合:
(coll? '(1 2 3)) ; => true
(coll? [1 2 3]) ; => true

; 只有列表是序列.(序列是有順序的)
(seq? '(1 2 3)) ; => true
(seq? [1 2 3]) ; => false

; 序列是列表一種邏輯上的接口,能夠懶加載.
; "" 意味着能夠定義無窮序列,就像下面同樣:
(range 4) ; => (0 1 2 3)
(range) ; => (0 1 2 3 4 ...) (一個無窮序列)
(take 4 (range)) ;  (0 1 2 3)

; 使用cons 來追加一個元素到列表或者向量的頭部
(cons 4 [1 2 3]) ; => (4 1 2 3)
(cons 4 '(1 2 3)) ; => (4 1 2 3)

; 使用conj追加一個元素到列表的頭部,或者向量的尾部,
(conj [1 2 3] 4) ; => [1 2 3 4]
(conj '(1 2 3) 4) ; => (4 1 2 3)

; 使用concat來鏈接列表和向量
(concat [1 2] '(3 4)) ; => (1 2 3 4)

; 使用filter, map 來進行列表計算
(map inc [1 2 3]) ; => (2 3 4)
(filter even? [1 2 3]) ; => (2)

; 使用reduce 來進行化繁爲簡  (map/reduce 思想就來自於lisp)
(reduce + [1 2 3 4])
; = (+ (+ (+ 1 2) 3) 4)
; => 10

; Reduce 可使用一個初始值
(reduce conj [] '(3 2 1))
; = (conj (conj (conj [] 3) 2) 1)
; => [3 2 1]

; 使用 fn 來建立一個函數。全部的函數都有返回值,就是它的最後一個表達式
(fn [] "Hello World") ; => fn

; (你須要額外的括號去調用它)
((fn [] "Hello World")) ; => "Hello World"

;你可使用def來建立變量
(def x 1)
x ; => 1

; 將函數賦值給一個變量
(def hello-world (fn [] "Hello World"))
(hello-world) ; => "Hello World"

; 你可使用defn來簡化定義過程
(defn hello-world [] "Hello World")

;[] 是函數的參數列表
(defn hello [name]
  (str "Hello " name))
(hello "Steve") ; => "Hello Steve"

; 你也可使用下面這種簡寫方式
(def hello2 #(str "Hello " %1))
(hello2 "Fanny") ; => "Hello Fanny"

; 你能夠建立擁有可變參數的函數
(defn hello3
  ([] "Hello World")
  ([name] (str "Hello " name)))
(hello3 "Jake") ; => "Hello Jake"
(hello3) ; => "Hello World"

; 函數容許將參數打包成列表 (有點相似python中的*)
(defn count-args [& args]
  (str "You passed " (count args) " args: " args))
(count-args 1 2 3) ; => "You passed 3 args: (1 2 3)"

; 你能夠將普通參數和列表參數混合使用
(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"


(class {:a 1 :b 2 :c 3}) ; => clojure.lang.PersistentArrayMap

; 關鍵字相似字符串,可是作了一些性能上的優化
(class :a) ; => clojure.lang.Keyword

; Maps 的鍵能夠是任意類型,可是一般推薦使用keywords
(def stringmap (hash-map "a" 1, "b" 2, "c" 3))
stringmap  ; => {"a" 1, "b" 2, "c" 3}

(def keymap (hash-map :a 1 :b 2 :c 3))
keymap ; => {:a 1, :c 3, :b 2} (不保證順序)

; 從一個map中檢索一個值,能夠直接把這個map當作函數調用(這個NB)
(stringmap "a") ; => 1
(keymap :a) ; => 1

; 關鍵字也能夠當作函數來調用,從一個map中檢索值(這個更NB)
(:b keymap) ; => 2

; stings 可沒有這個功能,因此下面會拋出異常。(這也是爲何推薦使用keywords)
;("a" stringmap)
; => Exception: java.lang.String cannot be cast to clojure.lang.IFn

; 檢索一個不存在的值會返回nil
(stringmap "d") ; => nil

; 使用assoc 向一個map中添加新的鍵值對。
(assoc keymap :d 4) ; => {:a 1, :b 2, :c 3, :d 4}

; 請記住, clojure 類型是不可變的!
keymap ; => {:a 1, :b 2, :c 3}

; 使用dissoc 來刪除key(能夠刪除多個)
(dissoc keymap :a :b) ; => {:c 3}

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

; 使用con來添加新值
(conj #{1 2 3} 4) ; => #{1 2 3 4}

; 使用disj刪除原有值
(disj #{1 2 3} 1) ; => #{2 3}

; 直接將set當作函數來測試是否包含某個值(NB)
(#{1 2 3} 1) ; => 1  (有就返回原有的值)
(#{1 2 3} 4) ; => nil (沒有就返回nil)

; clojure.sets 命名空間包含更多的函數

; clojure中的邏輯結構都是宏, 看起來也沒什麼不一樣
(if false "a" "b") ; => "b"
(if false "a") ; => nil

; 執行多條語句,返回最後一條語句
(do
  (print "Hello")
  "World") ; => "World" (prints "Hello")

; 全部的函數都包含一個隱式的do
(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綁定也是
(let [name "Urkel"]
  (print "Saying hello to " name)
  (str "Hello " name)) ; => "Hello Urkel" (prints "Saying hello to Urkel")

; 使用「use」來得到一個模塊中全部的函數
(use 'clojure.set)

; 如今咱們可使用集合操做
(intersection #{1 2 3} #{2 3 4}) ; => #{2 3}  求交集
(difference #{1 2 3} #{2 3 4}) ; => #{1}   求差集

; 你能夠只導入一個函數子集(例以下面只包含交集函數)
(use '[clojure.set :only [intersection]])

; 使用reqire來導入一個模塊
(require 'clojure.string)

; 使用/從一個模塊中調用函數
(clojure.string/blank? "") ; => true

; 你能夠在導入模塊的時候自定義名稱
(require '[clojure.string :as str])  
(str/replace "This is a test." #"[a-o]" str/upper-case) ; => "THIs Is A tEst."
; (#"" denotes a regular expression literal)

; 你可使用":require" 從一個命名空間中引入模塊(use也能夠,可是別這麼作)
; 若是你使用:require的話,就不必把模塊「引」(前面加個單引號)起來了.
(ns test
  (:require
    [clojure.string :as str]
    [clojure.set :as set]))

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

; java 擁有一個龐大的各類用途的標準庫,你必定火燒眉毛想學習如何在clojure中使用這些庫

; 使用import類引入java模塊(這個還好沒變化)
(import java.util.Date)

; 你也能夠從一個命名空間中引入
(ns test
  (:import java.util.Date
           java.util.Calendar))

; 類名字後加個」."用來建立一個對象
(Date.) ; <a date object>

; 使用. 來調用方法. 或者使用「.方法名"簡寫的方式
(. (Date.) getTime) ; <a timestamp>
(.getTime (Date.)) ; 和上面同樣哦

; 使用/ 來調用靜態方法
(System/currentTimeMillis) ; <a timestamp> (system is always present)

; 使用 doto 來處理可變的類,全部的函數始終用最初的那個對象值,最後仍是返回最初的那個對象  (import java.util.Calendar)
(doto (Calendar/getInstance)
  (.set 2000 1 1 0 0 0)
  .getTime) ; => A Date. set to 2000-01-01 00:00:00
相關文章
相關標籤/搜索