ErLang中的標點符號

 


ErLang中的標點符號 
ErLang語法中充滿了一些約定。大寫字母開頭的名字(好比Address),表示一個變量,包括參數、局部變量等;小寫字母開頭的單詞(好比ok),表示一個常量,叫作atom(原子的意思),包括常量名、函數名、模塊名等。 
ErLang的註釋用%開頭。ErLang用下劃線「_」表示任意變量,相似於Java的switch語法裏面的default選項。 
ErLang脫胎於Prolog,不過,我以爲,ErLang語法和Haskell語法比較象,都是採用 -> 定義函數。 
ErLang語句中的標點符號用法很象文章的標點符號。 
整個函數定義結束用一個句號「.」;同一個函數中,並列的邏輯分支之間,用分號「;」分界;順序語句之間,用逗號「,」分隔。 
ErLang中,{ }不是表示程序塊的開頭和結尾,而是表示一種特殊的數據結構類型——Tuple(元組),好比,{12, 3, ok}。咱們能夠把Tuple理解爲定長數組。 
[ ] 則表示最基本的函數式編程的數據結構類型——List。List數據結構很基本,寫法和用法也有必定的複雜度,不是表面上看起來那麼簡單,後面講解Closure的章節會詳細介紹List的最基本的構造原理。 
下面咱們來看一個簡單的例子。 
咱們首先定義一個最簡單的函數,把一個參數乘以10,而後加1。 
times10( Number ) –> 
Temp = 10 * Number, 
Temp + 1. 

爲了說明問題,上面的代碼把乘法操做和加法操做分紅兩個步驟。Temp = 10 * Number語句後面是逗號,由於這是兩條順序執行的語句。 Temp + 1語句後面是句號,表示整個函數定義結束。並且,能夠看出,ErLang沒有return語句,最後執行的那條語句的執行結果就是返回 值。 
下面,咱們把這個函數優化一下。當參數等於0的時候,直接返1;不然,就乘以10,而後加1,而後返回。這時候,咱們就要用到case of邏輯分支語句,至關於java的switch語句。 
times10( Number ) –> 
case Number of 
0 -> 1; 
_ -> 
Temp = 10 * Number, 
Temp + 1 
end. 

咱們來仔細觀察這段ErLang程序。 
當Number等於0的時候,直接返回1。因爲這是一條分支語句,和後面的分支是並列的關係,因此,1的後面的標點符號是分號。後面這個分支,下劃線「_」表示任何其它值,這裏就表示除了1以外的任何其它數值。 
須要注意的一點是,case of語句須要用end結尾,end以前不須要有標點符號。 
上述代碼中的case of 語句,其實就是Pattern Match的一種。ErLang的Pattern Match很強大,可以大幅度簡化程序邏輯,後面進行專門介紹。 
Pattern Match 
Pattern Match主要有兩個功能——比較分派和變量賦值。 
其中,比較分派是最主要的功能。比較分派的意思是,根據參數值進行條件分支的分派。能夠把比較分派功能看做是一種相似於if, else等條件分支語句的簡潔強大寫法。 
上面的例子中,case Number of 就是根據Number的值進行比較分派。更常見的寫法是,能夠把Pattern Match部分提到函數定義分支的高度。因而,上述代碼能夠寫成下面的形式: 
times10( 0 ) –> 1; 
times10( Number ) –> 
Temp = 10 * Number, 
Temp + 1. 

這段代碼由兩個函數定義分支構成,因爲兩個函數分支的函數名相同,並且參數個數相同,並且兩個函數定義分支之間採用分號「;」分隔,說明這是同一個函數的定義。函數式編程語言中,這種定義方式很常見,看起來形式很整齊,宛如數學公式。 
這段代碼的含義是,當參數值等於0的時候,那麼,程序走第一個函數定義分支(即分號「;」結尾的「times10( 0 ) –> 1;」),不然,走下面的函數定義分支(即「times10( Number ) –>…」)。 
第二個分支中的參數不是一個常數,而是一個變量Number,表示這個分支能夠接受任何除了0以外的參數值,好比,一、二、12等等,這些值將賦給變量Number。 
所以,這個地方也體現了Pattern Match的第二個功能——變量賦值。 

Pattern Match的形式能夠很複雜,下面舉幾個典型的例子。 
(1)數據結構拆解賦值 
前面將到了ErLang語言有一種至關於定長數組的Tuple類型,咱們能夠很方便地根據元素的位置進行並行賦值。好比, 
{First, Second} = {1, 2} 
咱們還能夠對複合Tuple數據結構進行賦值,好比 
{A, {B, C}, D} = { 1, {2, 3}, 4 } 
List數據結構的賦值也是相似。因爲List的寫法和用法不是那麼簡單,三言兩語也說不清楚,還徒增困擾,這裏再也不贅述。 
(2)assertEquals語句 
在Java等語言中,咱們寫單元測試的時候,會寫一些assert語句,驗證程序運行結果。這些assert語句一般是以API的方式提供,好比,assertTrue()、assertEquals()等。 
在ErLang中,能夠用簡單的語句達到相似於assertTrue()、assertEquals()等API的效果。 
好比,ErLang中,true = testA() 這樣的語句表示testA的返回結果必須是true,不然就會拋出異常。這個用法很巧妙。這裏解釋一下。 
前面講過,ErLang語法約定,小寫字母開頭的名字,都是常量名。這裏的true天然也是一個常量,既然是常量,咱們不可能對它賦值,那麼true = testA()的意思就不是賦值,而是進行匹配比較。 
(3)匹配和賦值同時進行 
咱們來看這樣一段代碼。 
case Result of 
{ok, Message} -> save(Message); 
{error, ErrorMessage} -> log(ErrorMessage) 
end. 

這段代碼中,Result是一個Tuple類型,包含兩個元素,第一個元素表示成功(ok)或者失敗(error),第二個元素表示具體的信息。 
能夠看到,這兩個條件分支中,同時出現了常量和變量。第一個條件分支中的ok是常量,Message是變量;第二個條件分支中的error是常量,ErrorMessage是變量。 
這兩個條件分支都既有比較判斷,也有變量賦值。首先,判斷ResultTuple中的第一個元素和哪個分支的第一個元素匹配,若是相配,那麼把 ResultTuple中的第二個元素賦給這個分支的第二個變量元素。即,若是Result的第一個元素是ok,那麼走第一個條件分支,而且把 Result的第二個元素賦給Message變量;若是Result的第二個元素是error,那麼走第二個條件分支,而且把Result的第二個元素賦 給ErrorMessage變量。 

在Java等語言中,實現上述的條件分支邏輯,則須要多寫幾條語句ErLang語法能夠從形式上美化和簡化邏輯分支分派複雜的程序。 
除了支持數相等比較,Pattern Match還能夠進行範圍比較、大小比較等,須要用到關鍵字when,不過用到when的狀況,就比if else簡潔不了多少,這裏再也不贅述。 
匿名函數 
ErLang容許在一個函數體內部定義另外一個匿名函數,這是函數式編程的最基本的功能。這樣,函數式語言才能夠支持Closure。咱們來看一個ErLang的匿名函數的例子。 
outer( C ) –> 
Inner = fun(A, B) -> A + B + C end, 
Inner(2, 3). 

這段代碼首先定義了一個命名函數outer,而後在outer函數內部定義了一個匿名函數。能夠看到,這個匿名函數採用關鍵字fun來定義。前面講過,函 數式編程的函數就至關於面向對象編程的類實例對象,匿名函數天然也是這樣,也至關於類實例,咱們能夠把這個匿名函數賦給一個變量Inner,而後咱們還可 以把這個變量看成函數來調用,好比,Inner(2, 3)。 
fun是ErLang用來定義匿名函數的關鍵字。這個關鍵字很重要。fun定義匿名函數的用法不是很複雜,和命名函數定義相似。
函數分支的定義也是相似,只是須要用end結尾,而不是用句號「.」結尾,並且fun只須要寫一次,不須要向命名函數那樣,每一個分支都要寫。好比, 
MyFunction = fun(0) -> 0; 
(Number) -> Number * 10 + 1 end, 
MyFunction(3), 
函數做爲變量 
匿名函數能夠看成對象賦給變量,命名函數一樣也能夠賦給變量。具體用法仍是須要藉助重要的fun關鍵字。好比, 
MyFunction = fun outer / 1 

就能夠把上述定義的outer函數賦給MyFunction變量。後面的 / 0表示這個outer函數只有一個參數。由於ErLang容許有多個同名函數的定義,只要參數個數不一樣,就是不一樣的函數。 
咱們能夠看到,任何函數均可以做爲變量,也能夠做爲參數和返回值傳來傳去,這些變量也能夠隨時做爲函數進行調用,因而就具備了必定的動態性。 
函數的動態調用 
ErLang有一個apply函數,能夠動態調用某一個函數變量。 
基本用法是 apply( 函數變量,函數參數列表 )。好比,上面的MyFunciton函數變量,就能夠這麼調用,apply( MyFunction, [ 5 ])。 
那麼咱們可否根據一個字符串做爲函數名獲取一個函數變量呢?這樣咱們就能夠根據一個字符串來動態調用某個函數了。 
ErLang中,作到這一點很簡單。前面講過,函數名一旦定義了,天然就固定了,這也相似於常量名,屬於不可變的atom(原子)。全部的atom均可以 轉換成字符串,也能夠從字符串轉換過來。ErLang中的字符串實質上都是List。字符串和atom之間的轉換經過list_to_atom和 atom_to_list來轉換。 
因而咱們能夠這樣獲取MyFunciton:MyFunction = list_to_atom(「outer」) 
若是outer函數已經定義,那麼MyFucntion就等於outer函數,若是outer函數沒有定義,那麼list_to_atom(「outer」)會產生一個新的叫作outer的atom,MyFucntion就等於這個新產生的atom。 
若是須要強制產生一個已經存在的atom,那麼咱們須要調用list_to_existing_atom轉換函數,這個函數不會產生新的atom,而是返回一個已經存在了的atom。 
Tuple做爲數據成員集合 
前面講解函數式編程特性的時候,提到了函數式編程沒有面向對象編程的成員變量,這是一個限制。 
ErLang的Tuple類型能夠必定程度克服這個限制。Tuple能夠必定程度上擔當容納成員變量的職責。 
面向對象的類定義,其實就是一羣數據和函數的集合,只是集合的成員之間都有一個this指針相關聯,能夠相互找到。 
ErLang的Tuple類型就是數據的集合,能夠很天然地發揮成員變量的做用,好比,{Member1, Member2}。 
讀者可能會說,ErLang的函數也能夠做爲變量,也能夠放到Tuple裏面,好比, { Memer1, Member2, Funtion1, Function2}。這不就和麪向對象編程同樣了嗎? 
遺憾的是,這樣作是得不償失的。由於函數式編程沒有面向對象的那種內在的this指針支持,天然也沒有內在的多態和繼承支持,硬把數據和函數糅合在一個Tuple裏面,一點好處都沒有,並且還喪失了函數做爲實例對象的靈活性。 
因此,函數式編程的最佳實踐(Best Practice)應該是:Tuple用來容納成員數據,函數操做Tuple。Tuple定義和函數定義加在一 起,就構成了鬆散的數據結構,功能上相似於面向對象的類定義。Tuple + 函數的數據結構,具備多態的特性,由於函數自己可以做爲變量替換;可是不具 有繼承的特性,由於沒有this指針的內在支持。 
正是由於Tuple在數據類型構造方面的重大做用,因此,ErLang專門引入了一種叫作Record的宏定義,能夠對Tuple的數組下標位置命名。比 如,把第一個元素叫作Address,第二個元素叫作Zipcode,這樣程序員就能夠這些名字訪問Tuple裏面的元素,而不須要按照數組下標位置來訪 問。 
Tuple和Record的具體用法仍是有必定複雜度,限於篇幅,本章沒有展開說明,只提了一些原理方面的要點。 
其它 
ErLang還有其它語法特性和細節,再也不一一贅述。有興趣的讀者,能夠自行去ErLang網站(www.erlang.org)進行研究。java

相關文章
相關標籤/搜索