前言

?每逢學(xué)習(xí)一個新的語言時總要先了解這門語言支持的數(shù)據(jù)類型,因為數(shù)據(jù)類型決定這門語言所針對的問題域,像Bash那樣內(nèi)置只支持字符串的腳步明顯就是用于文本處理啦。而數(shù)據(jù)類型又分為標(biāo)量類型(Scalar)、結(jié)構(gòu)類型(Struct)和集合類型(Collection),標(biāo)題中的簡單類型實質(zhì)就是指標(biāo)量類型。
?cljs中內(nèi)置的標(biāo)量類型比js的豐富得多,一方面方便了操作,另一個方面增加了學(xué)習(xí)成本,因此從js轉(zhuǎn)向cljs時可能會略感不適,下面我們一起來認(rèn)識吧!

標(biāo)量類型一覽

;; 空值/空集nil;; 字符串,必須使用雙引號包裹"I am a string!";; 字符,以斜桿開頭
\&
\newline

;; 布爾類型(Boolean),nil隱式類型轉(zhuǎn)換為false,0和空字符串等均隱式類型轉(zhuǎn)換為truetruefalse;; 長整型(Long)1;; 浮點型(Float)1.2;; 整型十六進(jìn)制0x0000ff;; 指數(shù)表示法1.2e3

;; 鍵(Keyword),以:為首字符,一般用于Map作為key:i-am-a-key;; Symbol,標(biāo)識符
i-am-symbol

;; Var
i-am-var

;; Special Form
;; 如if, let, do等
(if pred then else?)
(let [a 1] expr1 expr2)
(do expr*)

;; 函數(shù)
(fn [a]
    (println a))

;; 宏
(defmacro out [s]    `(println ~s))

Keyword真心不簡單??!

?位于cljs.core/Keyword的關(guān)鍵字并不是僅僅如上述那樣簡單,其實一共有3種定義方式:
1.所見即所得

;; 通過literal來定義:i-am-a-keyword:i-am-a-namespace/i-am-a-keyword;; 通過keyword函數(shù)來定義
(keyword "i-am-a-keyword")
(keyword "i-am-a-namespace" "i-am-a-keyword")

2.自動擴(kuò)展為以當(dāng)前命名空間為前綴

(ns cljs.user)
;; 自動擴(kuò)展為以當(dāng)前命名空間為前綴的keywork::keyword ;;=> :cljs.user/keyword

3.自動擴(kuò)展為

;; 自動查找以aliased-ns為別名的命名空間,并以找到的命名空間作為前綴創(chuàng)建keyword
;; 因此需要先通過require 引入命名空間才能通過別名解析出原來的命名空間
(ns cljs.user
  (:require '[test.core :as test]))
::test/keyword ;;=> :test.core/my-keyword

另外Keyword還可以作為函數(shù)使用呢!

(def person {:name "fsjohnhuang", "sex" "male"})

(:name person) ;;=> "fsjohnhuang"("sex" person) ;;=> 報錯(get person "sex") ;;=> "male"

什么是Symbol?

?在任何Lisp方言中Symbol作為標(biāo)識符(Identity),如命名空間名稱、函數(shù)名稱、變量名稱、Special Form名稱等等。而凡是標(biāo)識符均會被限制可使用的字符集范圍,那么合法的cljs.core/Symbol需遵守以下規(guī)則:

  1. 首字符不能是[0-9:]

  2. 后續(xù)字符可為[a-zA-Z0-9*+-_!?|:=<>$&]

  3. 末尾字符不能是:

  4. 區(qū)分大小寫

?命名習(xí)慣:

  1. 全小寫

  2. 單詞間以-分隔

  3. 常量和全局標(biāo)識,首尾為*,如*main-cli-fn*

  4. *x,標(biāo)識內(nèi)置變量,且經(jīng)常值變化

  5. x?,標(biāo)識斷言函數(shù)

  6. x!,標(biāo)識產(chǎn)生副作用的函數(shù)

  7. x-,標(biāo)識其將產(chǎn)生私有方法,如defn-deftest-

  8. _,標(biāo)識可忽略的symbol

既然Symbol僅僅作為標(biāo)識符來使用,為何不見JS、C#等會將標(biāo)識符獨立出來作為一種類型呢?原因十分簡單但又難以理解——Lisp中代碼即數(shù)據(jù),數(shù)據(jù)即代碼。作為Lisp的方言cljs自然傳承了這一耀眼的特性!

;; 定義一個List實例,其元素為a和b兩個Symbol實例(def symbol-list (list 'a 'b))

?大家有沒有注意到'這個符號???由于symbol根據(jù)它在列表中的位置解析為Special Form或Var,為阻止這一過程需要通過quote函數(shù)來處理,而'就是quote的reader macro。不信大家試試(cljs.reader/read-string "'a")它會擴(kuò)展為(cljs.core/quote a)
另外

;; 判斷是否為cljs.core/Symbol類型(symbol? 'a) ;;=> true;; symbol可以作為函數(shù)使用(def a {'b 1})
('b a) ;;=> 1

Var又是什么呢?

?在clj/cljs中Var是一個容器,其內(nèi)容為指向?qū)嶋H值的地址,當(dāng)其內(nèi)容為nil時稱之為unbound,非nil時則稱為bound。而一個Var可以對應(yīng)1~N個Symbol。

;; Symbol a和b都對應(yīng)同一個Var,這個Var指向1所在的內(nèi)存地址(def a 1)
(def b 1)

這個和JAVA、C#中的String是一樣的。另外Clojure還有一個十分有趣的特性就是Symbol直接綁定值,中間沒有Var,因此就不存在重新賦值的可能

(defn say [s]
    (println s))

(defn say1 [s]
    (def s 2)
    (println s))

(say "say")   ;;=> say(say1 "say1") ;;=> say1

和Symbol同樣,Var可以作為數(shù)據(jù)處理,不過由于Var會根據(jù)其所在列表中的位置解析為是Macro還是函數(shù)還是值,因此需要通過#'來阻止,而#'就是var的reader macro。

(def b 1)
(def c 2)
(def a (list #'b #'c))

注意:#'var操作前必須要先定義好同名變量、內(nèi)置或第三方庫已定義的變量,否則會報錯。

Special Form又是什么鬼?

?實質(zhì)上就是語言原語,其他函數(shù)和Macro均基于它們來構(gòu)造,當(dāng)解析器遇到一個Symbol時會解析的順序是Special Form -> Var
if就是一個原語,即使是Macro也沒有辦法從無來構(gòu)造一個,不信大家自己試試吧!

部分常用的Special Form如下:

(def symbol init?)
(if test then else?)
(do exprs*)
(let [binding*] exprs*)
(quote form)
(var symbol)
(fn name? [params*]
  exprs*)
(fn name?
  ([params*]
   exprs*)+)
(fn name? [params*]
  condition-map? exprs*)
(fn name?
  ([params*]
   condition-map?
   exprs*)+)
(loop [binding*]
  exprs*)
(recur exprs*)
(throw expr)
(try expr* catch-clause* finally-clause?)

怎么函數(shù)也納入標(biāo)量呢?

?函數(shù)式編程當(dāng)中第一條規(guī)則就是“函數(shù)是一等公民”,就是函數(shù)和String、Integer等一樣可以作入?yún)?、函?shù)返回值,更確切來說函數(shù)的構(gòu)造不依賴其他類型或類型實例。而面向?qū)ο笾?,沒有函數(shù)只有方法,而方法的構(gòu)造前必須先構(gòu)建其所依賴的類型或類型實例。
?另外cljs中確實是用定義變量的方式來定義函數(shù)

(defn a [x] (println x));; defn是macro,實質(zhì)上會展開成(def a (fn [x] (println x)))

是不是清楚多了??!

總結(jié)

?本文較詳盡地介紹了Keyword,然后稍微介紹了Symbol、Var和Special Form,而Lisp中“代碼即數(shù)據(jù),數(shù)據(jù)即代碼”需要結(jié)合Symbol的解釋過程說明效果才有所體現(xiàn),這個由于篇幅較大,就打算日后再另起一篇來描述了。
?作為函數(shù)式編程語言,cljs的函數(shù)定義又怎么會只有(defn name [params*] exprs*)呢?下一篇(cljs/run-at (JSVM. :all) "細(xì)說函數(shù)"),我們一起細(xì)說吧!
尊重原創(chuàng),轉(zhuǎn)載請注明來自:http://www.cnblogs.com/fsjohnhuang/p/7119333.html ^_^肥仔John

REF

http://www.cnblogs.com/or2-/p/3579745.html

如果您覺得本文的內(nèi)容有趣就掃一下吧!捐贈互勉!
??大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

http://www.cnblogs.com/fsjohnhuang/p/7119333.html