erlang入門筆記
2008-06-20
版權(quán)聲明
:轉(zhuǎn)載時(shí)請(qǐng)以超鏈接形式標(biāo)明文章原始出處和作者信息及
本聲明
http://cbkid.blogbus.com/logs/23258709.html
1.1.2 ? 其它方面
文章省略了以下幾個(gè)方面:
● 參考
● 本地錯(cuò)誤處理 (cache/throw)
● 單向連接 ( 顯示器 )
● 二進(jìn)制數(shù)據(jù)處理
● 列表相關(guān)
● 與外界如何通信,以及 / 或者 port 其它語言開發(fā)的軟件。當(dāng)然,有一些向?qū)е袝?huì)單
獨(dú)講解這個(gè)問題。 < 互操作向?qū)? >
● 涉及到的極少數(shù) Erlang 庫 ( 如,文件處理 )
● 關(guān)于 OTP 的問題完全被跳過,關(guān)于 Mnesia 數(shù)據(jù)庫的信息在結(jié)論中也被省略。
● ? Erlang 中的哈殺表。
● 運(yùn)行時(shí)改變代碼。
1.2.2 ? 模塊和函數(shù)
Erlang 程序?qū)懺谖募小C總€(gè)文件都包含一個(gè) Erlang 模塊 (Module) 。
文件名 tut.erl 這一點(diǎn)很重要,同時(shí)需要確定文件與 erl 在同一個(gè)目錄下 .
{ok,tut} 告訴你編譯成功。如果它提示 "error" ,你可能在輸入文本的時(shí)候出錯(cuò),并而錯(cuò)
誤信息可能會(huì)給你一些有關(guān)于如何糾正錯(cuò)誤的想法,依此你可以改變你的代碼,重新再試。
現(xiàn)在讓我們運(yùn)行這個(gè)程序。
4> tut:double(10).
Erlang 程序?qū)懺谖募小C總€(gè)文件都包含一個(gè)
Erlang 模塊 (Module) 。在文件中的第一行,就告訴我們模塊的名稱 (See the
chapter "Modules" in the Erlang Reference Manual) 。
-module(tut).
這告訴我們模塊的名稱是 tut 。注意本行結(jié)尾的 "." 。存放模塊代碼的文件的名字,也必須
和模塊同名但以 ".erl" 做為擴(kuò)展名。。當(dāng)我們使用另
一個(gè)模塊的函數(shù),我們使用語法,模塊名 : 函數(shù)名 ( 參數(shù) ) 。所以
4> tut:double(10).
意味著我們調(diào)用 tut 模塊中的 double 函數(shù),并使用 "10" 做為參數(shù)。
-export([double/1]).
說明 tut 模塊包含一個(gè)名稱為 double 的函數(shù),并且?guī)в幸粋€(gè)參數(shù) ( 在我們的例子中為 X)
并而這個(gè)函數(shù)可以在 tut 模塊以外被調(diào)用。
一個(gè)數(shù)字的階乘 ( 如: 4 的階乘是 4*3*2*1) 。在名為
tut1.erl 中輸入下面的代碼。
-module(tut1).
-export([fac/1]).
fac(1) ->
1;
fac(N) ->
N * fac(N - 1).
第一部分:
fac(1) ->
1;
說明 1 的階乘是 1 。注意我們以一個(gè) ";" 結(jié)束這一部分,這說明這個(gè)函數(shù)沒有結(jié)束。第二部
分:
fac(N) ->
N * fac(N - 1).
說明 N 的階乘是, N 與 N - 1 的階乘的乘積 (N! = N * (N - 1)!) 。注意這部分
以 "." 結(jié)束,以說明本函數(shù)沒有其它部分了。
-export([fac/1, mult/2]).
mult(X, Y) ->
X * Y.
注意,我們同時(shí)也擴(kuò)展了 -export 行,并給于它關(guān)于另一個(gè)帶有兩個(gè)參數(shù)的函數(shù)信息。
變量首字母必須是大寫 .
1.2.3 ? 元子 (Atoms)
元子是在 Erlang 中的另一個(gè)數(shù)據(jù)類型。元子以小寫字母開頭 .
元子只是一個(gè)簡(jiǎn)單的名字,其它什么都不是。他們不像變量可以帶有一個(gè)值。
1.2.4 ? 元組
元組以 "{" 和 "}" 括起來的。不過元組可以有很多部分,我們想要多就都可以。
例如:
{inch, X} {moscow, {c, -10}}
一組元組有一個(gè)固定的大小。我們稱元組中的東西為‘元素’。所以在元組 {moscow,{c,-
10}} 中,元素 1 是 moscow ,元素 2 是 {c, -10} 。
1.2.5 ? 列表
列表在 Erlang 中被括在 "[" 和 "]" 里。
Erlang 可允許分成多行,不過,不可以在元子或整數(shù)中間的某部分來分。
一個(gè)很有用的遍歷列表的方法是使用 "|" 。
[First |TheRest]= [1,2,3,4,5]
我們使用 | 來分隔列表中的第一個(gè)元素和后續(xù)的元素。
[E1, E2 | R] = [1,2,3,4,5,6,7]
這里的 | 是用來得到前兩個(gè)元素。當(dāng)然,如果我們?cè)囍鴱牧斜碇械玫奖攘斜碇卸x的元素個(gè)數(shù)更多的元素的話,我們會(huì)得到一個(gè)錯(cuò)誤。
我們通常所說的使用元組,在其它的編程語言中,我們可能會(huì)叫做“記錄”或“結(jié)構(gòu)體”。我們使用列表 ( 我們?cè)谄渌幊陶Z言中所什么的“鏈表” ) 。
Erlang 并沒有字符串類型,取而代之我們可以提供一個(gè)由 ASCII 字符組成的列表。
1.2.6 ? 標(biāo)準(zhǔn)模塊及用戶手冊(cè)
io:format/2 函數(shù)自身返回一個(gè)元子 ok ,注釋以 % 開始,直到本行結(jié)束。同時(shí)也要注意 -export([format_temps/1]). 一行只包含 format_temps/1 函數(shù),另一個(gè)函數(shù)是局部 (local) 函數(shù),他們無法被 tut5 模塊以外的東西訪問。
1.2.9 ? 變量的匹配、守衛(wèi)和作用域
首先注意我們這里有兩個(gè)同名的函數(shù) list_max 。雖然每個(gè)都帶有不同數(shù)據(jù)的參數(shù)。在 Erlang 這些都會(huì)被認(rèn)為是完全不同的函數(shù)。
我們需要通過“名稱 / 參數(shù)數(shù)量”區(qū)分這些我們 寫的函數(shù),比如在這里是 list_max/1 和 list_max/2 。
在 -> 之前的特定字符,表示--我們只會(huì)在這個(gè)特定的條件滿足的時(shí)候, 才會(huì)執(zhí)行函數(shù)的這個(gè)部分。我們叫這種類型的測(cè)試,叫守衛(wèi) (guard) 。如果守衛(wèi)不是‘真’ ? ( 我們稱它為守衛(wèi)失敗 ) ,我們會(huì)嘗試執(zhí)行函數(shù)的下一個(gè)部分。這種情況下如果 Head 不大于 Result_so_far 的話,它必定是小于等于它,所以我們?cè)谙乱徊糠种校恍枰偈褂檬匦l(wèi)。一些守衛(wèi)中常見的操作符有 < ? 小于, > ? 大于, == 等于, >= ? 大于等于, =< ? 小于等于, /= ? 不等于。
Result_so_far 給賦了很多值。那是因?yàn)椋覀兠看握{(diào)用 list_max/2 的時(shí)候,我 們建立了一個(gè)新的作用域, Result_so_far 在這個(gè)作用域中,都會(huì)被認(rèn)為是完全不同的變 量。
另一個(gè)建立和給一個(gè)變量賦值的方法是使用 ? = 。如果我寫 M = 5 ,那么一個(gè)名為 M 的變量 會(huì)被創(chuàng)建,并賦于 5 這個(gè)值。如果在同一下作用域下,當(dāng)我寫 M = 6 ,它就會(huì)發(fā)生錯(cuò)誤。
匹配操作符在通過一組元素創(chuàng)建新變量的時(shí)候很有用。
{X, Y} = {paris, {f, 28}}
這里我們看到 X 的值被賦于 paris ,而 Y 是 {f,28} 。
1.2.10 ? 更多關(guān)于列表
| 也可以用來在列表的頭部添加元素:
模塊 lists 包含了很多函數(shù)用于維護(hù)列表,例如反轉(zhuǎn)列表。所以在我們寫一個(gè)列表函數(shù)之 前,最好先看一下是否在庫中已經(jīng)存在了這樣一個(gè)函數(shù)。
1.2.11 If 和 Case
if
Condition 1 ->
Action 1;
Condition 2 ->
Action 2;
Condition 3 ->
Action 3;
Condition 4 ->
Action 4
end
注意,在結(jié)尾沒有 ";" !條件 (Conditions) 和守衛(wèi)一樣,測(cè)試成功或是失敗。 Erlang 會(huì) 從最頂上開始向下,直到找到一個(gè)成功的條件為止,并執(zhí)行行條件下面的動(dòng)作,并且乎略掉 其后面它的條件。如果沒有條件匹配,會(huì)發(fā)生一個(gè)運(yùn)行時(shí) (run-time) 錯(cuò)誤。元子 true 在條件中表示真,它常常被放在條件層,表示如果沒有匹配的條件的話,應(yīng)該做什么動(dòng)作。
convert_length(Length) ->
case Length of
{centimeter, X} ->
{inch, X / 2.54};
{inch, Y} ->
{centimeter, Y * 2.54}
end.
注意, case 和 if 都有返回值,即,在上面的例子中 case 返回 {inch,X/2.54} 或 ? {centimeter,Y*2.54} 。 case 的行為也可以使用守衛(wèi)來代替。
1.2.12 ? 內(nèi)建函數(shù) (BIFs)
內(nèi)建函數(shù) (BIFs) 是一個(gè)因?yàn)槟吃虮粌?nèi)嵌在 Erlang 虛擬機(jī)中的函數(shù)。 BIFs 經(jīng)常是一些 在 Erlang 上無法實(shí)現(xiàn)的功能,或在 Erlang 下實(shí)現(xiàn)起來效率不高的東西。有些 BIFs 可以 直接使用函數(shù)的名字,他們默認(rèn)是屬于 erlang 模塊下的函數(shù)。比如 trunc 函數(shù),和 ? erlang:trunc 調(diào)用是等同的。
只有很少一部分的內(nèi)建函數(shù),可以于用守衛(wèi),你也無 法在守衛(wèi)中使用你自己定義的函數(shù)。
(see the chapter "Guard Sequences"(http://www.erlang.org/doc/reference_manual/expressions.html) in the Erlang Reference Manual) 。
1.2.13 ? 復(fù)雜函數(shù)
convert_to_c 函數(shù),和以前定義的一樣,不過我們把它做為一個(gè)帶入函數(shù)來用: ? lists:map(fun convert_to_c/1, List)
當(dāng)我們使用一個(gè)要?jiǎng)e處定義的 fun 函數(shù)的話,我們可以能過函數(shù)名 / 參數(shù)個(gè)數(shù),來區(qū)別。
在 sort 函數(shù)中,我們使用的 fun 函數(shù) : fun({_, {c, Temp1}}, {_, {c, Temp2}}) -> Temp1 < Temp2 end,
這里,我們介紹匿名變量” _” 。這是一個(gè)簡(jiǎn)單化的,用于一個(gè)將得到值的變量,并且我們還 需要丟棄這個(gè)值的情況下。它可以用在任何合適的場(chǎng)合中,不只在 fun 中使用。 Temp1 < Temp2 返回 true 如果 Temp1 小于 Temp2 。
1.3 ? 并行編程
1.3.1 ? 進(jìn)程
并發(fā),我們的意思是一種可以處理掌握若干線程同時(shí)執(zhí)行這樣的程序。
”進(jìn)程”經(jīng)常指線程間沒有共享數(shù)據(jù),”線程”指他們之間,有共享的數(shù)據(jù)或共享內(nèi)存區(qū)
Erlang 內(nèi)建函數(shù) spawn 被用于建立一個(gè)新的進(jìn)程: spawn( 模塊 , ? 導(dǎo)出的函數(shù) , ? 參數(shù)列 表 ) 。
spawn 返回一個(gè)進(jìn)程 ID ,或 pid ,即一個(gè)進(jìn)程的唯一標(biāo)識(shí)。所以 <0.63.0> 是 spawn 函數(shù) 的 pid ,即進(jìn)程 ID 。
1.3.2 ? 信息傳遞
receive 構(gòu)造用于允許進(jìn)程等待來自其它進(jìn)程的消息。它的格式是:
receive
pattern1 ->
actions1;
pattern2 ->
actions2;
....
patternN
actionsN
end.
注意,在結(jié)尾沒有” ;” 。
在 Erlang 進(jìn)程間的消息是一個(gè)有效的 Erlang 字串,即,它們可以是列表、元組、整數(shù)、 元子、 pid 等等。
每個(gè)進(jìn)程對(duì)于它收到的消息,有一個(gè)自己的輸入隊(duì)列。新的消息會(huì)放在隊(duì)列的尾部。當(dāng)一個(gè) 進(jìn)程執(zhí)行了一個(gè) receive ,隊(duì)列中第一個(gè)與匹配條件一致的消息就會(huì)從隊(duì)列中移除,并匹 配的動(dòng)作會(huì)執(zhí)行。
如果第一個(gè)匹配試沒有被匹配的話,第二個(gè)就會(huì)被檢驗(yàn),如果與隊(duì)列中移除的條件匹配了的 話,相對(duì)于第二個(gè)式子中的運(yùn)作會(huì)被執(zhí)行。第三個(gè)也依次類推。如果沒有匹配的話,那么第 一個(gè)消息就會(huì)被放回隊(duì)列中,并用第二個(gè)消息來做對(duì)應(yīng)的操作。如果第二個(gè)消息匹配的話, 相應(yīng)的動(dòng)作就會(huì)被執(zhí)行,并把第二個(gè)消息從隊(duì)列中移除 ( 并保留第一個(gè)消息和其它的消息在 隊(duì)列中 ) 。如果第二個(gè)消息也不匹配,我們會(huì)試著用第三個(gè)消息,直到達(dá)到隊(duì)列尾。如果到 達(dá)到隊(duì)列尾部,進(jìn)程就會(huì)阻塞 ( 中止執(zhí)行 ) 并等待,直到一個(gè)新的消息被收到,上面的過程 重復(fù)。
注意,” !” 是如何發(fā)送消息的,以及” !” 的句法:
Pid ! Message
即, Message( 任何東西 ) 被發(fā)到給了標(biāo)識(shí)為 Pid 的進(jìn)程。
Pong_PID ! {ping, self()},
self() 返回當(dāng)前自在運(yùn)行的進(jìn)程 ID ,在這里是” ping” 的進(jìn)程 ID 。
1.3.3 ? 進(jìn)程名稱注冊(cè)
有些時(shí)候進(jìn)程 可能需要知道每一個(gè)與它不相關(guān)的,啟動(dòng)的進(jìn)程的標(biāo)識(shí)。
這是由 register 這個(gè)內(nèi)建函數(shù)完成的:
register(some_atom, Pid)
1.3.4 ? 分布式編程
分布式 Erlang 的實(shí)現(xiàn),提供了一個(gè)基本 的安全機(jī)制以防止來自其它計(jì)算機(jī)的非授權(quán)的訪問 (*manual*) 。
Erlang 系統(tǒng)要想互相通 信,必需要相同的 magic cookie 。最簡(jiǎn)單的獲取它的方法是在你的每臺(tái)需要 Erlang 通 信的機(jī)器中的 home 文件夾中建立一個(gè) .erlang.cookie 的文件 ( 在 Windows 系統(tǒng)中, ? home 文件夾由 $HOME 環(huán)境變量指定 - 你可能需要首先設(shè)定它。在 Linux 或 Unix 系統(tǒng) 中,你可以忽略這一步,只需要簡(jiǎn)單的在你用戶 home 文件夾中,建立 .erlang.cookie ? 就可以了 ) 。 .erlang.cookie 文件中需要包含相同的元子字串。如 Linux 或 Unix 系統(tǒng)
中:
$ cd
$ cat > .erlang.cookie
this_is_very_secret
$ chmod 400 .erlang.cookie
上面的 chmod 使 .erlang.cookie 文件只可以被它的所有者訪問。這是必需的。
當(dāng)你啟動(dòng)一個(gè)需要與其它機(jī)器上的 Erlang 系統(tǒng)交互的一個(gè) Erlang 系統(tǒng)時(shí),你必須給它一 個(gè)名字,如:
erl -sname my_name
如果你希望體驗(yàn)一下分布式 Erlang ,可是你 只有一機(jī)計(jì)算機(jī)的話,你只需要啟動(dòng)兩個(gè)獨(dú)立的 Erlang 系統(tǒng)在同一臺(tái)機(jī)器上,并給他們不 同的名字就可以了。每一個(gè)運(yùn)行于計(jì)算機(jī)上的 Erlang 系統(tǒng),被稱為一個(gè) Erlang 節(jié)點(diǎn)。
( 注意: erl -sname ? 假設(shè)所有節(jié)點(diǎn)都在同一個(gè) IP 域下,如果我們想要使用其它 IP 域上
的節(jié)點(diǎn),我們使用 -name 代替,并而需要給出完全的 IP 地址
Erlang 的 pid 已經(jīng)包 含它了有關(guān)于進(jìn)程運(yùn)行的相關(guān)信息,所以你只要知道 pid ,” !” 就可以用于發(fā)送消息,無論 目的地是在同一個(gè)節(jié)點(diǎn)上,還是其它節(jié)點(diǎn)上。
不同的是,我們?nèi)绾伟严l(fā)送到另一個(gè)節(jié)點(diǎn)上注冊(cè)的進(jìn)程中: ? {pong, Pong_Node} ! {ping, self()}, ? 我們使用元組 {regiester_name, node_name} 代替 注冊(cè)名。
一個(gè) Erlang 進(jìn)程理論上將一直運(yùn)行,直到收到不需要的信息。之所以我說“理論上”因?yàn)? ? Erlang 系統(tǒng)與活動(dòng)進(jìn)程運(yùn)享 CPU 時(shí)間。
當(dāng)沒有事可做的時(shí)候,一個(gè)進(jìn)程會(huì)終止,即,最后一個(gè)函數(shù)被調(diào)用,并返回一個(gè)值,而且不 再調(diào)用其它函數(shù)了。另一個(gè)中止進(jìn)程的方法是使用 exit/1 。 exit/1 的參數(shù)有特定的含意, 我們后面再說。
內(nèi)建函數(shù) whereis(RegisteredName) 檢測(cè)如果一個(gè)注冊(cè)的進(jìn)程名稱叫 ? RegisteredName 存在,如果存在,返回 pid 。否則返回 undefined 。
1.4 健壯性 (Robustness ? 魯棒性 )
-
超時(shí) (Timeouts)
超時(shí)設(shè) 置 在 這段代碼 中:
pong() ->
receive
{ ping , ? Ping_PID } ? ->
io:format( " Pong received ping ~ n ", ? []) ,
Ping_PID ? ! ? pong ,
pong()
after 5000 ->
io:format( " Pong timed out ~ n ", ? [])
end.
當(dāng) 我 們 進(jìn)入 receive 代碼段 時(shí), 啟 動(dòng) 了 超時(shí) 機(jī) 制 (after 5000) ? 如果接 受 到 了消 息 { ping , Ping_PID } 超時(shí)將 被 取 消 ,如果 沒 有 收 到 該消 息, 在 5000 毫秒 之后,超時(shí) 代碼段 的 操 作將 被 執(zhí) 行。 after 語句必須 是 receive 代碼段 的最后一個(gè)匹配 條 件。 ? 就 是 說 , 讓 receive 代碼段 中的其匹配 定 義優(yōu) 先 處理。 ? 當(dāng)然 ,我 們也 可以使用一個(gè) 返 回 一個(gè) 整 數(shù)的函數(shù) 來 作 為 超時(shí)的設(shè) 定值 : after pong_timeout() ? →
-
錯(cuò)誤處理
一個(gè)進(jìn)程如果使用 exit(normal) 退 出 或 者 運(yùn) 行完所有的 代碼然 后 退 出稱 為 正 常 (normal) 退 出 .
一個(gè)進(jìn)程如果發(fā) 生了運(yùn) 行時(shí)錯(cuò)誤 ( 例如 ? 除 以 0 ,錯(cuò)誤的匹配, 試圖調(diào) 用一個(gè)不 存 在的函數(shù) 等 . 將 因 為 錯(cuò)誤而 結(jié)束 , ? 也就 是 異 常 (abnormal) 退 出。
進(jìn)程 通過調(diào) 用 link(Other_Pid) ( * manual * ) 來 建 立自 身 和 Other_Pid 進(jìn)程的 雙向 鏈 接。 ? 當(dāng) 一個(gè)進(jìn)程中 止 的時(shí) 候 ,將 給每 一個(gè) 與 它建 立了 接的進(jìn)程發(fā) 送 一個(gè)信 號(hào) (signal) 。 這 個(gè)信 號(hào)包含了 發(fā) 送 者的 pid 和進(jìn)程 結(jié)束 的原 因 。
你可以將一個(gè) 事 務(wù) (transaction) 所 涉 及到的所有進(jìn)程 連 接在一 起 , 如果其中的一個(gè)進(jìn)程 異 常退 出,所有的 該事 務(wù) 中的進(jìn)程將 被 全部 殺 死 。
內(nèi) 嵌 函數(shù) spawn_link 在完成 spawn 函數(shù) 功 能的 同 時(shí)建 立了 一個(gè) 與 新 創(chuàng) 建的進(jìn)程的 鏈 接
我 們 可以 修 改 一個(gè)進(jìn)程接 收 到 異 常退 出信 號(hào) 時(shí)的缺 省退 出行 為 , ? 這 時(shí)所有的信 號(hào)都 將 被轉(zhuǎn)換 成一個(gè) 格
- 2011-02-12 11:15
- 瀏覽 1446
- 評(píng)論(0)
- 分類: 編程語言
- 相關(guān)推薦
發(fā)表評(píng)論
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元

評(píng)論