namespace/symbol/:keyword/::keyword in Clojure

此文已由做者張佃鵬受權網易雲社區發佈。
html

歡迎訪問網易雲社區,瞭解更多網易技術產品運營經驗。c++


1.關鍵字(:keyword/::keyword):json


  最近在學習使用clojure.spec(驗證數據的格式)這個庫,但在使用clojure.spec/def定義一個spec時,必須使用雙冒號以下:canvas


;;引入clojure.spec庫
    (use '[clojure.spec :as s]) ;;定義一個名爲::spec-test的spec (s/def ::spec-test even?)複製代碼


  當時比較懵逼,不知道爲何會這樣寫,還覺得是spec這個庫特有的,就急着去使用該庫了。後來才發現這也是一種關鍵字(::keyword),在某命名空間下的關鍵字(在其它語言c++中,::不是表示全局做用域嗎??),咱們可使用namespace函數和name函數解釋其以下:安全


(namespace ::spec-test)
    ;;=> "my-clj.core"
    (name ::spec-test)
    ;;=> "spec-test"複製代碼


  原來::keyword就是關鍵字,只是表示的是當前命名空間下的關鍵字,主要爲了解決關鍵字重名問題,也能夠在當前命名空間下顯式的定義namespace keyword:性能優化


;;定義一個帶有命名空間的關鍵字,該命名空間能夠隨意指定,不必定是已經存在命名空間
    (def ns-keyword :hello/A)
    ;;=> #'my-clj.core/ns-keyword

    ;;獲取其namespace是"hello"
    (namespace ns-keyword)
    ;;=> "hello"

    ;;獲取其name是"A"
    (name ns-keyword)
    ;;=> "A"複製代碼


  也就是說,有兩種關鍵字,一種是全局的單冒號關鍵字,一種命名空間下的關鍵字,對於後者,可使用::來定義關鍵字,表示是在當前命名空間下的關鍵字,也能夠顯式的寫出命名空間的定義方法。bash



2.符號(symbol):less


  符號是一個標誌,在使用時不對其進行解釋,前面須要加一個引號,阻止對其求值,以下:函數


;;定義一個變量a=3
    (def a 3)
    ;;=> #'my-clj.core/a
    ;;直接使用a會對其進行求值
    a
    ;;=> 3
    ;;若是使用symbol的方式則不對其進行求值,返回符號自己    'a ;;=> a複製代碼


  symbol與keyword同樣,也分全局的symbol和命名空間下的symbol,以下:性能


;;全局的symbol
    (namespace 'A) ;;=> nil ;;命名空間下的symbol,能夠避免重名 (namespace 'hello/A)
    ;;=> "hello"複製代碼

  符號(symbol)主要用於建立列表和宏定義時,以下:


;;建立一個新的列表時,爲了阻止其看成函數求值    '(1 2 3) ;;=> (1 2 3) ;;定義宏時,阻止對if看成函數計算 (defmacro unless [expr form] (list 'if expr nil form))
    ;;=> #'my-clj.core/unless複製代碼



3.symbol/keyword/string/namespace之間的轉換:


  在實際使用中,咱們常常會遇到keyword和string之間的轉換,尤爲是若是將map中的key由string變爲keyword的時候,會帶來不少好處,好比咱們使用的IDE中的keyword能夠高亮顯示,並且keyword還能夠做爲函數,更重要是的對map的結構會帶來不少方便,因此掌握keyword和string之間的轉換尤其重要。


  • 使用name函數將「關鍵字」轉換爲「字符串」(考慮namespace):


  首先可使用name函數獲取一個keyword的string形式:


(name :A)
    ;;=> "A"
    ;;若是一個keyword中遇到"/",name函數會將第一個"/"前的字符串做爲namespace,因此其返回的第一個"/"後的字符串
    (name :hello/A)
    ;;=> "A"複製代碼


  可是有時候咱們不但願把第一個斜槓"/"前的內容看成namespace,尤爲是在將map轉換爲json格式的字符串時,有些keyword中帶有「/」,可是它不是命名空間下的關鍵字,這時候若是使用name就會出錯,可是大部分相似map2json的函數都會使用name來解釋,因此會致使意想不到的錯誤發生。


  • 使用str函數將「關鍵字」轉換爲「字符串」(不考慮namespace):   鑑於以上狀況,咱們可使用str函數將「關鍵字」轉換爲「字符串」,以下:


;;str函數返回的字符串會帶有冒號
    (str :hello/A)
    ;;=> ":hello/A"

    ;;在str函數的基礎上調用.substring函數,則能夠正確轉換爲咱們想要的string
    (.substring (str :hello/A) 1)
    ;;=> "hello/A"複製代碼


  • 使用namespace和name函數將「關鍵字」轉換爲「字符串」(不考慮namespace)   可使用另一種方法獲取和str方法效果同樣,就是使用namespace函數和name函數的組合,namespace函數能夠獲取一個「關鍵字」或「符號」的命名空間的字符串表示,若是無命名空間則返回nil:


;;namespace函數的用法:
    (namespace :a)
    ;;=> nil
    (namespace :A/a)
    ;;=> "A"
    ;;它只會把第一個"/"前的字符串看成命名空間
    (namespace :A/B/a)
    ;;=> "A"

    ;;使用namespace和name函數將keyword轉換爲string
    (str (namespace :A/a) "/" (name :A/a))
    ;;=> "A/a"複製代碼


  特別注意的是,在clojurescript中,不要使用第三種方法,由於namespace函數在遇到多個斜槓時,會出現奇怪的問題,儘可能使用第二種方法。


  • 使用keyword函數將「字符串」轉換爲「關鍵字」   將「字符串」轉換爲關鍵字,直接使用keyword函數即可,keyword函數能夠接收一個參數或者兩個參數,若是是兩個參數的狀況下,會把第一個參數看成命名空間,而後在兩個參數中間加一個斜槓"/":


(keyword "a")
    ;;=> :a
    ;;keyword能夠接收有斜槓的字符串
    (keyword "A/a")
    ;;=> :A/a
    ;;其實接收兩個參數和接收一個參數本質上沒有太大的區別,能夠把兩個參數用str合併後再調用keyword,可能只是爲了突出強調namespace而已
    (keyword "A" "a")
    ;;=> :A/a
    (keyword "A/B" "a")
    ;;=> :A/B/a複製代碼


  • 與「符號」(symbol)之間的相關轉換:

    (1)其中symbol函數和keyword函數相似,能夠將「字符串」轉換爲「符號」,一樣也能夠接收一個或者兩個參數,以下:


(symbol "a")
    ;;=> a
    ;;若是兩個參數,第一個參數做爲命名空間
    (symbol "A" "a")
    ;;=> A/a複製代碼


   (2)「符號」也能夠做爲namespace函數和name函數的參數,用法和「關鍵字」做爲參數相似,能夠將一個「符號」轉換爲「字符串」,以下:


(namespace 'hello/A) ;;=> "hello" (name 'hello/A)
    ;;=> "A"
    ;;無命名空間時,namespace老是返回nil
    (namespace 'A) ;;=> nil (name 'A)
    ;;=> "A"複製代碼


參考文章:


kotka.de/blog/2010/0…

stackoverflow.com/questions/2…


免費體驗雲安全(易盾)內容安全、驗證碼等服務

更多網易技術、產品、運營經驗分享請點擊




相關文章:
【推薦】 canvas 動畫庫 CreateJs 之 EaselJS(上篇)
【推薦】 搜索實時個性化模型——基於FTRL和個性化推薦的搜索排序優化
【推薦】 針對低網速的性能優化

相關文章
相關標籤/搜索