作為一門函數式程式設計語言,深入了解函數的定義和使用自然是十分重要的事情,下面我們一起來學習吧!
定義文法
示例
其實<code>defn</code>是個macro,最終會展開為<code>fn</code>這種定義方式。是以後面的均以<code>fn</code>這種形式作說明。
注意:
Lambda表達式的函數體隻允許使用一個表達式,是以要通過special form<code>do</code>來運作多個表達式;
入參symbol為<code>%1,%2,...%n</code>,當有且隻有一個入參時可以使用<code>%</code>來指向該入參。
Symbol和集合均支援附加metadata,以便向編譯器提供額外資訊(如類型提示等),而我們也可以通過metadata來标記源碼、通路政策等資訊。
對于命名函數我們自然要賦予它Symbol,自然就可以附加中繼資料了。
其中附加<code>:private</code>和<code>defn-</code>定義函數目的是一樣的,就是将函數的通路控制設定為private(預設為public),但可惜的是cljs現在還不支援<code>:private</code>,是以還是要用名稱來區分通路控制政策。
示例:
若隻打算設定document string而已,那麼可以簡寫為
雖然cljs隻支援<code>:doc</code>
cljs為我們提供強大無比的入參解構能力,也就是通過聲明方式萃取入參
通過<code>&</code>定義可變入參,可變入參僅能作為最後一個入參來使用
通過組合可變入參和參數解構,我們可以得到命名入參
Multi-Arity函數中我們可以通過入參數目來調用不同的函數實作,但有沒有一種如C#、Java那樣根據入參類型來調用不同的函數實作呢?clj/cljs為我們提供Multimethods這一殺技——不但可以根據類型調用不同的函數實作,還可以根據以下内容呢!
類型
值
屬性
中繼資料
入參間關系
想說"Talk is cheap, show me the code"嗎?在看代碼前,我們先看看到底Multimethods的組成吧
1.dispatching function
用于對函數入參作操作,如擷取類型、值、運算入參關系等,然後将傳回值作為dispatching value,然後根據dispatching value調用具體的函數實作。
2.method
具體函數實作
3.hierarchy object
存儲層級關系的對象,預設情況下所有相關的Macro和函數均采用全局hierarchy object,若要采用私有則需要通過<code>(make-hierarchy)</code>來建立。
還是一頭霧水?上示例吧!
示例1 —— 根據第二個入參的層級關系
示例2 -- 根據第一個入參的值
示例3 -- 根據兩入參數值比較的大小
删除method
先對dispatching value和method的dispatching-value進行<code>=</code>的等于操作,若不比對則對兩者進行<code>isa?</code>的層級關系判斷操作,就這樣周遊所有注冊到該multimethod的method,得到一組符合的method。若這組method的元素個數有且僅有一個,則執行該method;若沒有則執行<code>:default</code> method,若還是沒有則抛異常。若這組method的元素個數大于1,且沒有人工設定優先級,則抛異常。
通過<code>prefer-method</code>我們可以設定method的優先級
層級關系相關的函數如下:
上述函數當省略<code>h?</code>時,則操作的層級關系存儲在全局的hierarchy object中。
注意:層級關系存儲在全局的hierarchy object中時,Symbole、Keyword均要包含命名空間部分(即使這個命名空間并不存在),否則會拒絕。
另外還有<code>parent</code>、<code>ancestors</code>和<code>descendants</code>
通過<code>(make-hierarchy)</code>可以建立一個用于實作局部層級關系的hierarchy object
注意:局部層級關系中的Symbol和Keyword是可以包含也可以不包含命名空間部分的哦!
對于動态類型語言而言,當入參不符合函數定義所期待時,是将入參格式化為符合期待值,還是直接報錯呢?我想這是每個JS的工程師必定面對過的問題。面對這個問題我們應該分階段分子產品來處理。
開發階段,對于核心子產品,讓問題盡早暴露;
生産階段,對于與使用者互動的子產品,應格式化輸入,并在背景記錄跟蹤問題。
而clj/cljs函數中的<code>condition map</code>就是為我們在開發階段提供對函數入參、函數傳回值合法性的斷言能力,讓我們盡早發現問題。
在pre-exprs中我們可以直接指向函數的入參,在post-exprs中則通過<code>%</code>來指向函數的傳回值。
現在我們可以安心把玩函數了,oh yeah!