Erlang 快速指南



Erlang - 概述

Erlang 是一種函數語言程式設計語言,同時也是一個執行時環境。它的設計具有對併發、分散式和容錯的整合支援。Erlang 最初是為愛立信的多個大型電信系統而開發的。

Erlang 的第一個版本由 Joe Armstrong、Robert Virding 和 Mike Williams 於 1986 年開發。它最初是愛立信內部的專有語言。後來於 1998 年釋出為開源語言。Erlang 以及 OTP(Erlang 中的一套中介軟體和庫)現在由愛立信的 OTP 產品部門支援和維護,通常被稱為 **Erlang/OTP**。

為什麼選擇 Erlang?

如果您有以下需求,則應使用 Erlang 開發您的應用程式:

  • 應用程式需要處理大量併發活動。

  • 它應該易於分佈在計算機網路上。

  • 應該具備使應用程式能夠容忍軟體和硬體錯誤的功能。

  • 應用程式應該是可擴充套件的。這意味著它應該能夠跨多個伺服器執行,而幾乎無需更改。

  • 它應該易於升級和重新配置,而無需停止和重新啟動應用程式本身。

  • 應用程式應該在某些嚴格的時間範圍內對使用者做出響應。

Erlang 的官方網站是 https://www.erlang.org/

Erlang Official Website

Erlang - 環境

現在,在您可以開始使用 Erlang 之前,您需要確保您的系統上執行著功能齊全的 Erlang 版本。本節將介紹在 Windows 機器上安裝 Erlang 及其後續配置,以便開始使用 Erlang。

在繼續安裝之前,請確保滿足以下系統要求。

系統要求

記憶體 2 GB RAM(推薦)
磁碟空間 無最低要求。最好有足夠的儲存空間來儲存將使用 Erlang 建立的應用程式。
作業系統版本 Erlang 可以安裝在 Windows、Ubuntu/Debian、Mac OS X 上。

下載 Erlang

要下載 Erlang,必須訪問以下網址:www.erlang.org/downloads

此頁面提供各種下載以及在 Linux 和 Mac 平臺上下載和安裝該語言所需的步驟。

Erlang Download

單擊“OTP 18.3 Windows 32 位二進位制檔案”開始下載 Erlang Windows 安裝檔案。

Erlang 安裝

以下步驟詳細說明如何在 Windows 上安裝 Erlang:

**步驟 1** - 啟動在前面部分下載的安裝程式。安裝程式啟動後,單擊“執行”。

Erlang Installation

**步驟 2** - 單擊下一個螢幕上的“下一步”以接受將安裝的預設元件。

Select Components

**步驟 3** - 接受預設安裝路徑並單擊“下一步”。

Installation Path

**步驟 4** - 接受將建立的預設“開始”選單項,然後單擊“下一步”。

Start Menu Item

**步驟 5** - 安裝完成後,單擊“關閉”以完成安裝。

Installation Completed

Erlang 配置

安裝完成後,需要進行以下配置以確保 Erlang 在系統上開始工作。

作業系統 輸出
Windows 將字串;C:\Program Files(x86)\erl7.2.1\bin 或 C:\Program Files\erl7.2.1\bin 附加到系統變數 PATH 的末尾。

如果您現在開啟命令提示符並鍵入 **erl**,您應該能夠獲得 erl 命令提示符。

Erlang Configuration

恭喜,您現在已在筆記型電腦上成功配置了 erl。

在流行的 IDE 上安裝外掛

Erlang 作為一種程式語言,也適用於流行的 IDE,例如 **Eclipse 和 IntelliJ**。讓我們看看如何在這些 IDE 中獲取所需的外掛,以便您在使用 Erlang 時有更多選擇。

在 Eclipse 中安裝

**步驟 1** - 開啟 Eclipse 並單擊選單項 **幫助 → 安裝新軟體**。

Installation in Eclipse

**步驟 2** - 將“使用”連結輸入為 https://download.erlide.org/update

然後單擊“新增”。

Enter Work

**步驟 3** - 然後系統將提示您為外掛輸入名稱,請輸入名稱為 **Erlide**。單擊“確定”。

Enter Plugin Name

**步驟 4** - Eclipse 將掃描提供的連結並獲取所需的外掛。選中外掛並單擊“下一步”。

Get Required Plugin

**步驟 5** - 在下一個對話方塊中,Eclipse 將顯示所有將安裝的元件。單擊“下一步”。

Installed Components

**步驟 6** - 在下一個對話方塊中,Eclipse 只需您檢視正在安裝的元件。單擊“下一步”。

Review Installed Components

**步驟 7** - 在下一個對話方塊中,您只需要接受許可協議。最後,單擊“完成”按鈕。

Accept License Agreement

然後將開始安裝,安裝完成後,它將提示您重新啟動 Eclipse。

重新啟動 Eclipse 後,當您建立專案時,您也將能夠看到 Erlang 作為選項。

Erlang Option

在 IntelliJ 中安裝

請按照以下步驟在您的計算機上安裝 IntelliJ。

**步驟 1** - 開啟 IntelliJ 並單擊“配置”→“外掛”。

IntelliJ

**步驟 2** - 在搜尋框中鍵入 Erlang。您將在螢幕右側看到 Erlang 外掛。單擊“安裝”按鈕。

Erlang Plugin

**步驟 3** - 安裝 Erlang 外掛後,系統將提示您重新啟動 IDE。

Restart IDE

重新啟動 IDE 並嘗試建立新專案時,您將看到建立 Erlang 專案的選項。

Erlang Project Option

Erlang - 基本語法

為了理解 Erlang 的基本語法,讓我們首先看看一個簡單的 **Hello World** 程式。

示例

% hello world program
-module(helloworld). 
-export([start/0]). 

start() -> 
   io:fwrite("Hello, world!\n").

關於上述程式,需要注意以下幾點:

  • % 符號用於向程式添加註釋。

  • module 語句就像在任何程式語言中新增名稱空間一樣。因此,在這裡,我們提到這段程式碼將是名為 **helloworld** 的模組的一部分。

  • export 函式用於定義程式中任何可以使用的函式。我們定義了一個名為 start 的函式,為了使用 start 函式,我們必須使用 export 語句。** /0 ** 表示我們的函式“start”接受 0 個引數。

  • 我們最終定義了 start 函式。在這裡,我們使用了另一個名為 **io** 的模組,其中包含 Erlang 中所有必需的輸入輸出函式。我們使用 **fwrite** 函式將“Hello World”輸出到控制檯。

上述程式的輸出將是:

輸出

Hello, world!

語句的一般形式

在 Erlang 中,您已經看到在 Erlang 語言中使用了不同的符號。讓我們透過一個簡單的 Hello World 程式來了解我們所看到的:

  • 連字元符號 **(-)** 通常與 module、import 和 export 語句一起使用。連字元符號用於為每個語句賦予相應的含義。因此,來自 Hello world 程式的示例如下所示:

-module(helloworld).
-export([start/0]).

每個語句都以點 **(.)** 符號分隔。Erlang 中的每個語句都必須以這個分隔符結尾。來自 Hello world 程式的示例如下所示:

io:fwrite("Hello, world!\n").
  • 斜槓 **(/)** 符號與函式一起使用,用於定義函式接受的引數數量。

-export([start/0]).

模組

在 Erlang 中,所有程式碼都分為模組。模組由一系列屬性和函式宣告組成。它就像其他程式語言中的名稱空間概念一樣,用於邏輯上分離不同的程式碼單元。

定義模組

模組是用模組識別符號定義的。一般語法和示例如下。

語法

-module(ModuleName)

**ModuleName** 必須與檔名相同,只是缺少副檔名 **.erl**。否則,程式碼載入將無法按預期工作。

示例

-module(helloworld)

這些模組將在後續章節中詳細介紹,這只是為了讓您對如何定義模組有一個基本的瞭解。

Erlang 中的 import 語句

在 Erlang 中,如果要使用現有的 Erlang 模組的功能,可以使用 import 語句。import 語句的一般形式如下圖所示:

示例

-import (modulename, [functionname/parameter]).

其中:

  • **Modulename** - 這是需要匯入的模組的名稱。

  • **functionname/parameter** - 需要匯入的模組中的函式。

讓我們更改編寫 hello world 程式的方式以使用 import 語句。示例如下所示:

示例

% hello world program
-module(helloworld).
-import(io,[fwrite/1]).
-export([start/0]).

start() ->
   fwrite("Hello, world!\n").

在上面的程式碼中,我們使用 import 關鍵字匯入庫“io”以及 **fwrite** 函式。因此,每當我們呼叫 fwrite 函式時,就不必在任何地方都提及 **io** 模組名稱。

Erlang 中的關鍵字

關鍵字是 Erlang 中的保留字,不應將其用於與其預期用途不同的任何其他用途。以下是 Erlang 中的關鍵字列表。

after and andalso band
begin bnot bor bsl
bsr bxor case catch
cond div end fun
if let not of
or orelse receive rem
try when xor

Erlang 中的註釋

註釋用於記錄您的程式碼。單行註釋透過在行的任何位置使用 **%** 符號來標識。以下是一個示例:

示例

% hello world program
-module(helloworld).
% import function used to import the io module
-import(io,[fwrite/1]).
% export function used to ensure the start function can be accessed.
-export([start/0]).

start() ->
   fwrite("Hello, world!\n").

Erlang - Shell

Erlang shell 用於表示式測試。因此,在應用程式中實際測試之前,可以在 shell 中非常輕鬆地進行測試。

以下示例展示瞭如何在 shell 中使用加法表示式。需要注意的是,表示式需要以點(.)分隔符結尾。

Shell

命令執行後,shell 會列印另一個提示符,這次是命令編號 2(因為每次輸入新命令時,命令編號都會遞增)。

以下函式是 Erlang shell 中最常用的函式。

  • b() − 列印當前變數繫結。

  • 語法 − b()。

  • 例如 − 下面是一個函式使用方法的示例。首先定義一個名為 Str 的變數,其值為 abcd。然後使用 b() 顯示所有繫結的變數。

Erlang Shell b()
  • f() − 刪除所有當前變數繫結。

  • 語法 − f()。

  • 例如 − 下面是一個函式使用方法的示例。首先定義一個名為 Str 的變數,其值為 abcd。然後使用 f() 刪除 Str 變數繫結。然後呼叫 b() 以確保繫結已成功刪除。

Erlang Shell f()
  • f(x) − 刪除特定變數的繫結。

  • 語法 − f(x)。其中,x – 是需要刪除其繫結的變數。

  • 例如 − 下面是一個函式使用方法的示例。首先定義名為 Str 和 Str1 的變數。然後使用 f(Str) 刪除 Str 變數繫結。然後呼叫 b() 以確保繫結已成功刪除。

Erlang Shell f(x)
  • h() − 列印 shell 中執行的所有命令的歷史列表。

  • 語法 − h()。

  • 例如 − 下面的螢幕截圖顯示了 h() 命令的示例,該命令列印在 shell 中執行的命令歷史記錄。

Erlang Shell h()
  • history(N) − 將歷史列表中保留的先前命令數量設定為 N。返回之前的數量。預設數量為 20。

  • 語法 − history(N)。其中,N – 是命令歷史列表需要限制到的數量。

  • 例如 − 下面的螢幕截圖顯示了 history(N) 命令的示例。

Erlang Shell history(N)
  • e(N) − 如果 N 為正數,則重複命令 N。如果為負數,則重複第 N 個先前命令(即,e(-1) 重複先前命令)。

  • 語法 − e(N)。其中,N – 是列表中第 N 個位置的命令。

  • 例如 − 下面顯示了 e(N) 命令的示例。由於我們已執行 e(-1) 命令,它將執行之前的命令 history(5)。

Erlang Shell e(N)

Erlang - 資料型別

在任何程式語言中,都需要使用多個變數來儲存各種型別的資訊。變數只不過是保留的記憶體位置,用於儲存值。這意味著,當建立變數時,會保留一些記憶體空間來儲存與該變數關聯的值。

您可能希望儲存各種資料型別的資訊,例如字串、字元、寬字元、整數、浮點數、布林值等。根據變數的資料型別,作業系統會分配記憶體並決定可以在保留的記憶體中儲存什麼。

內建資料型別

Erlang 提供了各種內建資料型別。以下是 Erlang 中定義的資料型別列表:

  • 數字 − 在 Erlang 中,有兩種型別的數字文字:整數和浮點數。

  • 原子 − 原子是文字,是具有名稱的常量。如果原子不以小寫字母開頭,或者包含除字母數字字元、下劃線 (_) 或 @ 之外的其他字元,則需要將其括在單引號(')中。

  • 布林值 − Erlang 中的布林資料型別是兩個保留原子:true 和 false。

  • 位串 − 位串用於儲存未型別化記憶體區域。

  • 元組 − 元組是一種複合資料型別,具有固定數量的項。元組中的每一項都稱為元素。元素的數量稱為元組的大小。

  • 對映 − 對映是一種複合資料型別,具有可變數量的鍵值關聯。對映中的每個鍵值關聯都稱為關聯對。對的鍵和值部分稱為元素。關聯對的數量稱為對映的大小。

  • 列表 − 列表是一種複合資料型別,具有可變數量的項。列表中的每一項都稱為元素。元素的數量稱為列表的長度。

注意 − 您可能會驚訝地發現,在上面的列表中找不到字串型別。這是因為 Erlang 中沒有專門定義的字串資料型別。但我們將在後續章節中瞭解如何使用字串。

以下是每種資料型別的使用方法示例。同樣,在接下來的章節中將詳細討論每種資料型別。這只是為了讓您初步瞭解上述資料型別。

數字

以下程式顯示瞭如何使用數字資料型別的示例。此程式顯示了兩個整數的加法。

示例

-module(helloworld).
-export([start/0]).

start() ->
   io:fwrite("~w",[1+1]).

上述程式的輸出將是:

輸出

2

原子

原子應以小寫字母開頭,可以包含小寫和大寫字元、數字、下劃線 (_) 和“at”符號 (@)。我們也可以將原子括在單引號中。

以下程式顯示瞭如何使用原子資料型別的示例。在此程式中,我們正在建立一個名為 atom1 的原子。

示例

-module(helloworld).
-export([start/0]).

start() ->
   io:fwrite(atom1).

上述程式的輸出將是:

輸出

atom1

布林值

以下程式顯示瞭如何使用布林資料型別的示例。此示例比較兩個整數,並將結果布林值列印到控制檯。

示例

-module(helloworld).
-export([start/0]).

start() ->
   io:fwrite(2 =< 3).

上述程式的輸出將是:

輸出

true

位串

以下程式顯示瞭如何使用位串資料型別的示例。此程式定義了一個由 2 位組成的位串。binary_to_list 是 Erlang 中定義的內建函式,可用於將位串轉換為列表。

示例

-module(helloworld).
-export([start/0]).

start() ->
   Bin1 = <<10,20>>,
   X = binary_to_list(Bin1),
   io:fwrite("~w",[X]).

上述程式的輸出將是:

輸出

[10,20]

元組

以下程式顯示瞭如何使用元組資料型別的示例。

這裡我們定義了一個包含 3 個項的 元組 Ptuple_size 是 Erlang 中定義的內建函式,可用於確定元組的大小。

示例

-module(helloworld). 
-export([start/0]). 

start() -> 
   P = {john,24,{june,25}} , 
   io:fwrite("~w",[tuple_size(P)]).

上述程式的輸出將是:

輸出

3

對映

以下程式顯示瞭如何使用對映資料型別的示例。

這裡我們定義了一個包含 2 個對映的 對映 M1map_size 是 Erlang 中定義的內建函式,可用於確定對映的大小。

示例

-module(helloworld). 
-export([start/0]). 

start() -> 
   M1 = #{name=>john,age=>25}, 
   io:fwrite("~w",[map_size(M1)]).

上述程式的輸出將是:

輸出

2

列表

以下程式顯示瞭如何使用列表資料型別的示例。

這裡我們定義了一個包含 3 個項的 列表 L。length 是 Erlang 中定義的內建函式,可用於確定列表的大小。

示例

-module(helloworld). 
-export([start/0]). 

start() -> 
   L = [10,20,30] , 
   io:fwrite("~w",[length(L)]).

上述程式的輸出將是:

輸出

3

Erlang - 變數

在 Erlang 中,所有變數都使用 ‘=’ 語句繫結。所有變數都必須以大寫字元開頭。在其他程式語言中,‘=’ 符號用於賦值,但在 Erlang 中並非如此。如前所述,變數是使用 ‘=’ 語句定義的。

在 Erlang 中需要注意的一點是,變數是不可變的,這意味著為了更改變數的值,需要先銷燬它,然後再重新建立。

最後一章解釋了以下 Erlang 基本變數:

  • 數字 − 用於表示整數或浮點數。例如 10。

  • 布林值 − 表示布林值,可以是 true 或 false。

  • 位串 − 位串用於儲存未型別化記憶體區域。例如 <<40,50>>。

  • 元組 − 元組是一種複合資料型別,具有固定數量的項。例如 {40,50}。

  • 對映 − 對映是一種複合資料型別,具有可變數量的鍵值關聯。對映中的每個鍵值關聯都稱為關聯對。例如 {type=>person,age=>25}。

  • 列表 − 列表是一種複合資料型別,具有可變數量的項。例如 [40,40]。

變數宣告

定義變數的一般語法如下:

語法

var-name = var-value

其中:

  • var-name − 這是變數的名稱。

  • var-value − 這是繫結到變數的值。

以下是一個變數宣告的示例:

示例

-module(helloworld). 
-export([start/0]). 

start() -> 
   X = 40, 
   Y = 50, 
   Result = X + Y, 
   io:fwrite("~w",[Result]).

在上面的示例中,我們有兩個變數,一個是 X,繫結到值 40,另一個是 Y,繫結到值 50。另一個名為 Result 的變數繫結到 X 和 Y 的和。

上述程式的輸出將是:

輸出

90

變數命名

如前所述,變數名必須以大寫字母開頭。讓我們來看一下用小寫字母宣告的變數的示例。

示例

-module(helloworld). 
-export([start/0]). 

start() -> 
   X = 40, 
   Y = 50, 
   result = X + Y, 
   io:fwrite("~w",[Result]).

如果嘗試編譯上述程式,則會收到以下編譯時錯誤。

輸出

helloworld.erl:8: variable 'Result' is unbound

其次,所有變數只能賦值一次。讓我們來看一下多次賦值變數的示例。

示例

-module(helloworld). 
-export([start/0]). 

start() -> 
   X = 40, 
   Y = 50, 
   X = 60, 
   io:fwrite("~w",[X]).

如果嘗試編譯上述程式,則會收到以下編譯時錯誤。

輸出

helloworld.erl:6: Warning: variable 'Y' is unused
helloworld.erl:7: Warning: no clause will ever match
helloworld.erl:7: Warning: the guard for this clause evaluates to 'false'

列印變數

在本節中,我們將討論如何使用各種列印變數的函式。

使用 io:fwrite 函式

您可能已經在上述所有程式中看到過這個 (io:fwrite)。fwrite 函式是 Erlang 的“io”模組的一部分,可用於輸出程式中變數的值。

以下示例顯示了可與 fwrite 語句一起使用的更多引數。

示例

-module(helloworld). 
-export([start/0]). 

start() -> 
   X = 40.00, 
   Y = 50.00, 
   io:fwrite("~f~n",[X]), 
   io:fwrite("~e",[Y]).

上述程式的輸出將是:

輸出

40.000000
5.00000e+1

關於上述程式,應注意以下幾點:

  • ~ − 此字元表示需要對輸出進行一些格式化。

  • ~f − 引數是一個浮點數,寫為 [-]ddd.ddd,其中精度是小數點後的位數。預設精度為 6,不能小於 1。

  • ~n − 這是換行符 println

  • ~e − 引數是一個浮點數,寫為 [-]d.ddde+-ddd,其中精度是寫入的位數。預設精度為 6,不能小於 2。

Erlang - 運算子

運算子是告訴編譯器執行特定數學或邏輯運算的符號。

Erlang 具有以下型別的運算子:

  • 算術運算子
  • 關係運算符
  • 邏輯運算子
  • 位運算子

算術運算子

Erlang 語言支援與任何其他語言相同的標準算術運算子。以下是 Erlang 中可用的算術運算子。

示例

運算子 描述 示例
+ 兩個運算元相加 1 + 2 結果為 3
- 從第一個運算元中減去第二個運算元 1 - 2 結果為 -1
* 兩個運算元相乘 2 * 2 結果為 4
/ 分子除以分母 2 / 2 結果為 1
rem 第一個數字除以第二個數字的餘數 3 rem 2 結果為 1
div div 運算子將執行除法並返回整數部分。 3 div 2 結果為 1

關係運算符

關係運算符允許比較物件。以下是 Erlang 中可用的關係運算符。

示例

運算子 描述 示例
== 測試兩個物件是否相等 2 = 2 結果為真
/= 測試兩個物件是否不同 3 /= 2 結果為真
< 檢查左側物件是否小於右側運算元。 2 < 3 結果為真
=< 檢查左側物件是否小於或等於右側運算元。 2 =<3 結果為真
> 檢查左側物件是否大於右側運算元。 3 > 2 結果為真
>= 檢查左側物件是否大於或等於右側運算元。 3 >= 2 結果為真

邏輯運算子

這些邏輯運算子用於評估布林表示式。以下是 Erlang 中可用的邏輯運算子。

示例

運算子 描述 示例
or 這是邏輯“或”運算子 true or true 結果為 true
and 這是邏輯“與”運算子 true and false 結果為 false
not 這是邏輯“非”運算子 not false 結果為 true
xor 這是邏輯異或運算子 true xor false 結果為 true

位運算子

Erlang 提供四個位運算子。以下是 Erlang 中可用的位運算子。

示例

序號 運算子和描述
1

band

這是按位“與”運算子

2

bor

這是按位“或”運算子

3

bxor

這是按位“異或”或“異或”運算子

4

bnot

這是按位取反運算子

以下是展示這些運算子的真值表:

p q p & q p | q p ^ q
0 0 0 0 0
0 1 0 1 1
1 1 1 1 0
1 0 0 1 1

運算子優先順序

下表按降序優先順序顯示 Erlang 運算子的運算子優先順序及其結合性。運算子優先順序和結合性用於確定無括號表示式中的求值順序。

運算子 結合性
:
#
bnot, not
/, *, div, rem, band, and 左結合
+,-, bor, bxor, or, xor 左結合
==,/=,=<,<,>=,>

Erlang - 迴圈

Erlang 是一種函數語言程式設計語言,關於所有函數語言程式設計語言需要注意的是,它們不提供任何迴圈結構。相反,函數語言程式設計依賴於稱為遞迴的概念。

while 語句的實現

由於 Erlang 中沒有直接的 while 語句,因此必須使用 Erlang 中可用的遞迴技術來執行 while 語句的實現。

我們將嘗試遵循與其他程式語言中相同的 while 迴圈實現方式。以下是將遵循的通用流程。

while Statement Implementation

讓我們來看一個如何使用遞迴在 Erlang 中實現 **while** 迴圈的示例。

示例

-module(helloworld). 
-export([while/1,while/2, start/0]). 

while(L) -> while(L,0). 
while([], Acc) -> Acc;

while([_|T], Acc) ->
   io:fwrite("~w~n",[Acc]), 
   while(T,Acc+1). 
   
   start() -> 
   X = [1,2,3,4], 
   while(X).

關於上述程式,需要注意以下幾點:

  • 定義一個名為 while 的遞迴函式,該函式將模擬 while 迴圈的實現。

  • 例如,將變數 X 中定義的值列表作為輸入傳遞給 while 函式。

  • while 函式獲取每個列表值並將中間值儲存在變數“Acc”中。

  • 然後針對列表中的每個值遞迴呼叫 while 迴圈。

上述程式碼的輸出將是:

輸出

0
1
2
3

for 語句

由於 Erlang 中沒有直接的 **for** 語句,因此必須使用 Erlang 中可用的遞迴技術來執行 **for** 語句的實現。

我們將嘗試遵循與其他程式語言中相同的 **for** 迴圈實現方式。以下是應遵守的通用流程。

for Statement

讓我們來看一個如何使用遞迴在 Erlang 中實現 **for** 迴圈的示例。

示例

-module(helloworld). 
-export([for/2,start/0]). 

for(0,_) -> 
   []; 
   
   for(N,Term) when N > 0 -> 
   io:fwrite("Hello~n"), 
   [Term|for(N-1,Term)]. 
   
start() -> 
   for(5,1).

關於上述程式,需要注意以下幾點:

  • 我們正在定義一個遞迴函式,該函式將模擬 **for 迴圈** 的實現。

  • 我們在“for”函式中使用保護條件來確保 N 或限制的值為正值。

  • 我們透過在每次遞迴中減少 N 的值來遞迴呼叫 for 函式。

上述程式碼的輸出將是:

輸出

Hello
Hello
Hello
Hello
Hello

Erlang - 決策

決策結構要求程式設計師指定一個或多個條件供程式評估或測試,以及如果確定條件為 **true** 則要執行的語句,以及可選地,如果確定條件為 **false** 則要執行的其他語句。

以下是大多數程式語言中常見的典型決策結構的通用形式:

Decision Making

Erlang 程式語言提供以下型別的決策語句。

序號 語句和描述
1

if 語句

**if 語句** 由一個布林表示式後跟一個或多個語句組成。

2

多個表示式

**if** 表示式也允許同時評估多個表示式。

3

巢狀 if 語句

您可以在另一個 **if** 或 **else if** 語句中使用一個 **if** 或 **else if** 語句。

4

case 語句

它可以根據 case 語句的輸出執行表示式。

Erlang - 函式

Erlang 被稱為函數語言程式設計語言,因此您期望看到很多關於 Erlang 中函式如何工作的重點。本章介紹了在 Erlang 中可以使用函式執行的所有操作。

定義函式

函式宣告的語法如下:

語法

FunctionName(Pattern1… PatternN) ->
Body;

其中:

  • **FunctionName** - 函式名是一個原子。

  • **Pattern1…PatternN** - 每個引數都是一個模式。引數的數量 N 是函式的元數。函式由模組名、函式名和元數唯一定義。也就是說,具有相同名稱和在同一模組中但具有不同元數的兩個函式是兩個不同的函式。

  • **Body** - 子句體由逗號 (,) 分隔的一系列表示式組成。

以下程式是函式使用的簡單示例:

示例

-module(helloworld). 
-export([add/2,start/0]). 

add(X,Y) -> 
   Z = X+Y, 
   io:fwrite("~w~n",[Z]). 
   
start() -> 
   add(5,6).

關於上述程式,需要注意以下幾點:

  • 我們正在定義兩個函式,一個是名為 **add** 的函式,它接受 2 個引數,另一個是 **start** 函式。

  • 這兩個函式都是使用 export 函式定義的。如果我們不這樣做,我們將無法使用該函式。

  • 一個函式可以在另一個函式內部呼叫。在這裡,我們從 start 函式呼叫 add 函式。

上述程式的輸出將是:

輸出

11

匿名函式

匿名函式是一個沒有與其關聯名稱的函式。Erlang 具有定義匿名函式的功能。以下程式是匿名函式的示例。

示例

-module(helloworld). 
-export([start/0]). 

start() -> 
   Fn = fun() -> 
      io:fwrite("Anonymous Function") end, 
   Fn().

關於上述示例,需要注意以下幾點:

  • 匿名函式是用 **fun()** 關鍵字定義的。

  • 函式被分配給名為 Fn 的變數。

  • 函式透過變數名呼叫。

上述程式的輸出將是:

輸出

Anonymous Function

具有多個引數的函式

Erlang 函式可以定義為零個或多個引數。函式過載也是可能的,您可以多次定義同名函式,只要它們具有不同的引數數量即可。

在下面的示例中,函式 demo 為每個函式定義都定義了多個引數。

示例

-module(helloworld). 
-export([add/2,add/3,start/0]). 

add(X,Y) -> 
   Z = X+Y, 
   io:fwrite("~w~n",[Z]). 
   
add(X,Y,Z) -> 
   A = X+Y+Z, 
   io:fwrite("~w~n",[A]). 
 
start() ->
   add(5,6), 
   add(5,6,6).

在上面的程式中,我們兩次定義了 add 函式。但是,第一個 add 函式的定義接受兩個引數,第二個接受三個引數。

上述程式的輸出將是:

輸出

11
17

帶有保護條件序列的函式

Erlang 中的函式也具有保護條件序列的功能。這些只不過是只有在計算結果為 true 時才會導致函式執行的表示式。

帶有保護條件序列的函式的語法顯示在以下程式中。

語法

FunctionName(Pattern1… PatternN) [when GuardSeq1]->
Body;

其中:

  • **FunctionName** - 函式名是一個原子。

  • **Pattern1…PatternN** - 每個引數都是一個模式。引數的數量 N 是函式的元數。函式由模組名、函式名和元數唯一定義。也就是說,具有相同名稱和在同一模組中但具有不同元數的兩個函式是兩個不同的函式。

  • **Body** - 子句體由逗號 (,) 分隔的一系列表示式組成。

  • **GuardSeq1** - 這是在呼叫函式時將計算的表示式。

以下程式是使用帶有保護條件序列的函式的簡單示例。

示例

-module(helloworld). 
-export([add/1,start/0]). 

add(X) when X>3 -> 
   io:fwrite("~w~n",[X]). 

start() -> 
   add(4).

上述程式的輸出為:

輸出

4

如果 add 函式被呼叫為 **add(3)**,則程式將導致錯誤。

Erlang - 模組

模組是一組在單個檔案中、使用單個名稱重新組合的函式。此外,Erlang 中的所有函式都必須在模組中定義。

大多數基本功能(如算術、邏輯和布林運算子)已經可用,因為在執行程式時會載入預設模組。您將使用的模組中定義的每個其他函式都需要使用 **Module:Function** (Arguments) 的形式呼叫。

定義模組

使用模組,您可以宣告兩種東西:函式和屬性。屬性是描述模組本身的元資料,例如其名稱、應該對外部世界可見的函式、程式碼的作者等等。這種元資料很有用,因為它向編譯器提供了有關如何執行其工作的提示,而且因為它允許人們在無需查閱原始碼的情況下從編譯程式碼中檢索有用的資訊。

函式宣告的語法如下:

語法

-module(modulename)

其中,**modulename** 是模組的名稱。這必須是模組中程式碼的第一行。

以下程式顯示名為 **helloworld** 的模組的示例。

示例

-module(helloworld). 
-export([start/0]). 

start() -> 
   io:fwrite("Hello World").

上述程式的輸出為:

輸出

Hello World

模組屬性

模組屬性定義了模組的特定屬性。模組屬性由標籤和值組成。

屬性的通用語法如下:

語法

-Tag(Value)

屬性使用方法的示例如下程式所示:

示例

-module(helloworld). 
-author("TutorialPoint"). 
-version("1.0"). 
-export([start/0]). 

start() -> 
   io:fwrite("Hello World").

上述程式定義了兩個自定義屬性,名為 author 和 version,分別包含程式作者和程式版本號。

上述程式的輸出為:

輸出

Hello World

預構建屬性

Erlang 有一些可以附加到模組的預構建屬性。讓我們來看看它們。

匯出

exports 屬性將採用函式和元數列表,以便其他模組使用。它將定義模組介面。我們之前的所有示例中都已看到這一點。

語法

export([FunctionName1/FunctionArity1,.,FunctionNameN/FunctionArityN])

其中:

  • FunctionName - 這是程式中函式的名稱。

  • FunctionArity - 這是與函式關聯的引數數量。

示例

-module(helloworld). 
-author("TutorialPoint"). 
-version("1.0"). 
-export([start/0]). 

start() -> 
   io:fwrite("Hello World").

上述程式的輸出將是:

輸出

Hello World

匯入

import 屬性用於從另一個模組匯入函式,以便將其用作區域性函式。

語法

-import (modulename , [functionname/parameter]).

其中:

  • **Modulename** - 這是需要匯入的模組的名稱。

  • functionname/parameter - 需要匯入的模組中的函式。

示例

-module(helloworld). 
-import(io,[fwrite/1]). 
-export([start/0]). 

start() -> 
   fwrite("Hello, world!\n").

在上面的程式碼中,我們使用 import 關鍵字匯入庫“io”以及特定的 fwrite 函式。因此,現在每當我們呼叫 fwrite 函式時,就不必在任何地方都提及 io 模組名稱。

上述程式的輸出將是:

輸出

Hello, world! 

Erlang - 遞迴

遞迴是 Erlang 的重要組成部分。首先讓我們看看如何透過實現階乘程式來實現簡單的遞迴。

示例

-module(helloworld). 
-export([fac/1,start/0]). 

fac(N) when N == 0 -> 1; 
fac(N) when N > 0 -> N*fac(N-1). 

start() -> 
   X = fac(4), 
   io:fwrite("~w",[X]).

關於上述程式,需要注意以下幾點:

  • 我們首先定義一個名為 fac(N) 的函式。

  • 我們可以透過遞迴呼叫 fac(N) 來定義遞迴函式。

上述程式的輸出為:

輸出

24

遞迴的實用方法

在本節中,我們將詳細瞭解不同型別的遞迴及其在 Erlang 中的用法。

長度遞迴

透過一個簡單的示例可以看出遞迴的更實用的方法,該示例用於確定列表的長度。列表可以包含多個值,例如 [1,2,3,4]。讓我們使用遞迴來了解如何獲取列表的長度。

示例

-module(helloworld). 
-export([len/1,start/0]). 

len([]) -> 0; 
len([_|T]) -> 1 + len(T). 

start() -> 
   X = [1,2,3,4], 
   Y = len(X), 
   io:fwrite("~w",[Y]).

關於上述程式,需要注意以下幾點:

  • 第一個函式 len([]) 用於列表為空時的特殊情況。

  • [H|T] 模式與一個或多個元素的列表匹配,長度為一的列表將定義為 [X|[]],長度為二的列表將定義為 [X|[Y|[]]]。請注意,第二個元素本身就是一個列表。這意味著我們只需要計算第一個元素,並且函式可以自身呼叫第二個元素。列表中的每個值都算作長度為 1。

上述程式的輸出將是:

輸出

4

尾遞迴

為了理解尾遞迴的工作原理,讓我們瞭解上一節中以下程式碼的工作原理。

語法

len([]) -> 0; 
len([_|T]) -> 1 + len(T).

1 + len(Rest) 的答案需要找到 len(Rest) 的答案。然後,len(Rest) 函式本身需要找到另一個函式呼叫的結果。加法將一直堆疊到找到最後一個為止,只有這樣才能計算最終結果。

尾遞迴旨在透過在發生時減少操作來消除這種操作堆疊。

為了實現這一點,我們需要在函式中將一個額外的臨時變數作為引數。上述臨時變數有時稱為累加器,它充當一個儲存計算結果的地方,這些結果會在發生時限制我們呼叫的增長。

讓我們來看一個尾遞迴的示例:

示例

-module(helloworld).
-export([tail_len/1,tail_len/2,start/0]). 

tail_len(L) -> tail_len(L,0). 
tail_len([], Acc) -> Acc; 
tail_len([_|T], Acc) -> tail_len(T,Acc+1). 

start() -> 
   X = [1,2,3,4], 
   Y = tail_len(X), 
   io:fwrite("~w",[Y]).

上述程式的輸出為:

輸出

4

複製

讓我們來看一個遞迴的例子。這次讓我們編寫一個函式,該函式以整數作為其第一個引數,然後以任何其他項作為其第二個引數。然後,它將根據整數建立多個項的列表。

讓我們看看一個這樣的例子:

-module(helloworld). 
-export([duplicate/2,start/0]). 

duplicate(0,_) -> 
   []; 
duplicate(N,Term) when N > 0 ->
   io:fwrite("~w,~n",[Term]),
   [Term|duplicate(N-1,Term)]. 
start() -> 
   duplicate(5,1).

上述程式的輸出將是:

輸出

1,
1,
1,
1,
1,

列表反轉

在 Erlang 中使用遞迴沒有限制。現在讓我們快速瞭解一下如何使用遞迴反轉列表的元素。可以使用以下程式來完成此操作。

示例

-module(helloworld). 
-export([tail_reverse/2,start/0]). 

tail_reverse(L) -> tail_reverse(L,[]).

tail_reverse([],Acc) -> Acc; 
tail_reverse([H|T],Acc) -> tail_reverse(T, [H|Acc]).

start() -> 
   X = [1,2,3,4], 
   Y = tail_reverse(X), 
   io:fwrite("~w",[Y]).

上述程式的輸出將是:

輸出

[4,3,2,1]

關於上述程式,需要注意以下幾點:

  • 我們再次使用臨時變數的概念將列表的每個元素儲存在一個名為 Acc 的變數中。

  • 然後我們遞迴呼叫 tail_reverse,但這次我們確保最後一個元素首先放入新列表中。

  • 然後我們遞迴地為列表中的每個元素呼叫 tail_reverse。

Erlang - 數字

在 Erlang 中,有兩種型別的數字文字,分別是整數和浮點數。以下是展示如何在 Erlang 中使用整數和浮點數的一些示例。

整數 - 下面的程式顯示瞭如何將數字資料型別用作整數。此程式顯示了兩個整數的加法。

示例

-module(helloworld). 
-export([start/0]). 

start() -> 
   io:fwrite("~w",[1+1]).

上述程式的輸出如下:

輸出

2

浮點數 - 下面的程式顯示瞭如何將數字資料型別用作浮點數。此程式顯示了兩個整數的加法。

示例

-module(helloworld).
-export([start/0]). 

start() -> 
   io:fwrite("~w",[1.1+1.2]).

上述程式的輸出如下:

輸出

2.3

顯示浮點數和指數數

使用 fwrite 方法將值輸出到控制檯時,有一些可用的格式化引數可用於將數字輸出為浮點數或指數數。讓我們看看如何實現這一點。

示例

-module(helloworld). 
-export([start/0]). 

start() -> 
   io:fwrite("~f~n",[1.1+1.2]), 
   io:fwrite("~e~n",[1.1+1.2]).

上述程式的輸出如下:

輸出

2.300000
2.30000e+0

關於上述程式,需要注意以下幾點:

  • 當指定 ~f 選項時,這意味著引數是一個浮點數,寫為 [-]ddd.ddd,其中精度是小數點後的位數。預設精度為 6。

  • 當指定 ~e 選項時,這意味著引數是一個浮點數,寫為 [-]d.ddde+-ddd,其中精度是寫入的位數。預設精度為 6。

數字的數學函式

Erlang 為數字提供了以下數學函式。請注意,Erlang 的所有數學函式都存在於 math 庫中。因此,以下所有示例都將使用 import 語句匯入 math 庫中的所有方法。

序號 數學函式和描述
1

sin

此方法返回指定值的正弦。

2

cos

此方法返回指定值的餘弦。

3

tan

此方法返回指定值的正切。

4

asin

此方法返回指定值的反正弦。

5

acos

此方法返回指定值的反餘弦。

6

atan

此方法返回指定值的反正切。

7 exp

此方法返回指定值的指數。

8

log

此方法返回指定值的對數。

9

abs

此方法返回指定數字的絕對值。

10

float

此方法將數字轉換為浮點值。

11

Is_float

此方法檢查數字是否為浮點值。

12

Is_Integer

此方法檢查數字是否為整數值。

Erlang - 字串

字串文字在 Erlang 中透過將字串文字括在引號中來構造。Erlang 中的字串需要使用雙引號來構造,例如“Hello World”。

以下是 Erlang 中字串用法的示例:

示例

-module(helloworld). 
-export([start/0]). 

start() ->
   Str1 = "This is a string", 
   io:fwrite("~p~n",[Str1]).

上面的示例建立一個名為 Str1 的字串變數。字串“This is a string”被賦值給該變數並相應地顯示。

上述程式的輸出將是:

輸出

“This is a string”

接下來,我們將討論可用於字串的各種操作。請注意,對於字串操作,您還需要包含字串庫。

序號 字串方法和描述
1

len

此方法返回特定字串的長度。

2

equal

此方法返回一個布林值,表示一個字串是否等於另一個字串。

3

concat

此方法連線兩個字串並返回連線後的字串。

4

chr

此方法返回字串中字元的索引位置。

5

str

此方法返回字串中子字串的索引位置。

6

substr

此方法根據起始位置和從起始位置開始的字元數返回原始字串中的子字串。

7

left

此方法根據起始位置和從起始位置開始的字元數返回原始字串中的子字串。

帶尾隨字元的 left

此方法根據字元數返回字串左側的子字串。但如果數字大於字串的長度,則可以選擇包含尾隨字元。

語法

left(str1,number,$character)

引數

  • str1 - 這是需要從中提取子字串的字串。

  • Number - 這是子字串中需要存在的字元數。

  • $Character - 要作為尾隨字元包含的字元。

返回值

根據字串的左側和數字返回原始字串中的子字串。

例如

-module(helloworld). 
-import(string,[left/3]). 
-export([start/0]). 

start() -> 
   Str1 = "hello", 
   Str2 = left(Str1,10,$.), 
   io:fwrite("~p~n",[Str2]).

輸出

執行上述程式時,我們將得到以下結果。

"hello....."

right

此方法根據字元數返回字串右側的子字串。

語法

right(str1,number)

引數

  • str1 - 這是需要從中提取子字串的字串。

  • Number - 這是子字串中需要存在的字元數。

返回值

根據字串的右側和數字返回原始字串中的子字串。

例如

-module(helloworld). 
-import(string,[right/2]). 
-export([start/0]). 

start() -> 
   Str1 = "hello World", 
   Str2 = right(Str1,2), 
   io:fwrite("~p~n",[Str2]).

輸出

執行上述程式時,我們將得到以下結果。

“ld”

帶尾隨字元的 right

此方法根據字元數返回字串右側的子字串。但如果數字大於字串的長度,則可以選擇包含尾隨字元。

語法

right(str1,number,$character)

引數

  • str1 - 這是需要從中提取子字串的字串。

  • Number - 這是子字串中需要存在的字元數。

  • $Character - 要作為尾隨字元包含的字元。

返回值

根據字串的右側和數字返回原始字串中的子字串。

例如

-module(helloworld). 
-import(string,[right/3]). 
-export([start/0]). 

start() -> 
   Str1 = "hello", 
   Str2 = right(Str1,10,$.), 
   io:fwrite("~p~n",[Str2]).

輸出

執行上述程式時,我們將得到以下結果。

".....hello"

to_lower

此方法返回小寫字串。

語法

to_lower(str1)

引數

  • str1 - 這是需要轉換為小寫的字串。

返回值

返回小寫字串。

例如

-module(helloworld). 
-import(string,[to_lower/1]). 
-export([start/0]). 

start() -> 
   Str1 = "HELLO WORLD", 
   Str2 = to_lower(Str1), 
   io:fwrite("~p~n",[Str2]).

輸出

執行上述程式時,我們將得到以下結果。

"hello world"

to_upper

此方法返回大寫字串。

語法

to_upper(str1)

引數

  • str1 - 這是需要轉換為大寫的字串。

  • 返回值 - 返回大寫字串。

例如

-module(helloworld). 
-import(string,[to_upper/1]). 
-export([start/0]). 

start() -> 
   Str1 = "hello world", 
   Str2 = to_upper(Str1), 
   io:fwrite("~p~n",[Str2]).

輸出

執行上述程式時,我們將得到以下結果。

"HELLO WORLD"

sub_string

返回字串的子字串,從 Start 位置開始到字串的末尾,或到 Stop 位置(包括 Stop 位置)。

語法

sub_string(str1,start,stop)

引數

  • str1 - 這是需要從中返回子字串的字串。

  • start - 這是子字串的起始位置

  • stop - 這是子字串的停止位置

返回值

返回字串的子字串,從 Start 位置開始到字串的末尾,或到 Stop 位置(包括 Stop 位置)。

例如

-module(helloworld). 
-import(string,[sub_string/3]). 
-export([start/0]). 

start() -> 
   Str1 = "hello world", 
   Str2 = sub_string(Str1,1,5), 
   io:fwrite("~p~n",[Str2]).

輸出

執行上述程式時,我們將得到以下結果。

"hello"

Erlang - 列表

列表是一種用於儲存資料項集合的結構。在 Erlang 中,列表透過將值括在方括號中來建立。

以下是在 Erlang 中建立數字列表的一個簡單示例。

示例

-module(helloworld). 
-export([start/0]). 

start() -> 
   Lst1 = [1,2,3], 
   io:fwrite("~w~n",[Lst1]).

上述示例的輸出將是:

輸出

[1 2 3]

現在讓我們討論可用於列表的各種方法。請注意,需要匯入列表庫才能使這些方法起作用。

序號 方法和描述
1

all

如果 Pred(Elem) 對 List 中的所有元素 Elem 返回 true,則返回 true,否則返回 false。

2

any

如果 List 中至少有一個元素 Elem 使得 Pred(Elem) 返回 true,則返回 true。

3

追加

返回一個新的列表 List3,該列表由 List1 的元素後跟 List2 的元素組成。

4

刪除

從列表中刪除一個元素並返回一個新的列表。

5

刪除最後一個元素

刪除 List 的最後一個元素。

6

複製

返回一個包含 N 個 Elem 項的列表。

7

最後一個元素

返回列表的最後一個元素。

8

最大值

返回列表中具有最大值的元素。

9

成員

檢查列表中是否存在某個元素。

10

最小值

返回列表中具有最小值的元素。

11

合併

返回由合併 ListOfLists 的所有子列表形成的排序列表。

12

第 N 個元素

返回 List 的第 N 個元素。

13

第 N 個尾部

返回 List 的第 N 個尾部。

14

反轉

反轉元素列表。

15

排序

對元素列表進行排序。

16

子列表

返回元素的子列表。

17

求和

返回列表中元素的總和。

Erlang - 檔案 I/O

Erlang 在處理 I/O 時提供了許多方法。它擁有更簡單的類來為檔案提供以下功能:

  • 讀取檔案
  • 寫入檔案
  • 檢視檔案是檔案還是目錄

Erlang 中的檔案操作方法

讓我們探索 Erlang 提供的一些檔案操作。出於這些示例的目的,我們將假設存在一個名為 **NewFile.txt** 的檔案,其中包含以下幾行文字:

示例1

示例2

示例3

此檔案將用於以下示例中的讀寫操作。

一次讀取檔案內容的一行

檔案的一般操作是透過使用檔案庫中提供的方法來執行的。對於檔案的讀取,我們需要首先使用開啟操作,然後使用檔案庫中提供的讀取操作。以下是這兩種方法的語法:

語法

  • 開啟檔案 – Open(File,Mode)
  • 讀取檔案 – read(FileHandler,NumberofBytes)

引數

  • **File** – 需要開啟的檔案位置。

  • **Mode** – 需要開啟檔案的模式。

以下是可用的模式:

  • **Read** – 開啟檔案以進行讀取(檔案必須存在)。

  • **Write** – 開啟檔案以進行寫入。如果檔案不存在,則建立檔案。如果檔案存在,並且 write 沒有與 read 組合使用,則檔案將被截斷。

  • **Append** – 開啟檔案以進行寫入,如果檔案不存在,則建立檔案。對以追加模式開啟的檔案的每次寫入操作都將發生在檔案的末尾。

  • **Exclusive** – 開啟檔案以進行寫入時,如果檔案不存在,則建立檔案。如果檔案存在,open 將返回 {error, exist}。

  • **FileHandler** – 檔案控制代碼。使用 **file:open** 操作時將返回此控制代碼。

  • **NumberofByte** – 需要從檔案中讀取的位元組數。

返回值

  • **Open(File,Mode)** – 如果操作成功,則返回檔案控制代碼。

  • **read(FileHandler,NumberofBytes)** – 從檔案中返回請求的讀取資訊。

例如

-module(helloworld). 
-export([start/0]). 

start() -> 
   {ok, File} = file:open("Newfile.txt",[read]),
   Txt = file:read(File,1024 * 1024), 
   io:fwrite("~p~n",[Txt]).

**輸出** – 執行上述程式時,我們將得到以下結果。

Example1

現在讓我們討論一些可用於檔案操作的其他方法:

序號 方法和描述
1

file_read

允許一次讀取檔案的所有內容。

2

write

用於將內容寫入檔案。

3

copy

用於複製現有檔案。

4

刪除

此方法用於刪除現有檔案。

5

list_dir

此方法用於列出特定目錄的內容。

6

make_dir

此方法用於建立新目錄。

7

rename

此方法用於重新命名現有檔案。

8

file_size

此方法用於確定檔案的大小。

9

is_file

此方法用於確定檔案是否確實是檔案。

10

is_dir

此方法用於確定目錄是否確實是目錄。

Erlang - 原子

原子是一個文字,一個帶有名稱的常量。如果原子不以小寫字母開頭,或者包含除字母數字字元、下劃線 (_) 或 @ 之外的其他字元,則應將其括在單引號 ('') 中。

以下程式是原子如何在 Erlang 中使用的示例。此程式分別聲明瞭 3 個原子,分別是 atom1、atom_1 和 'atom 1'。因此您可以看到宣告原子的不同方式。

示例

-module(helloworld). 
-export([start/0]). 

start() -> 
   io:fwrite(atom1), 
   io:fwrite("~n"), 
   io:fwrite(atom_1), 
   io:fwrite("~n"), 
   io:fwrite('atom 1'), 
   io:fwrite("~n").

上述程式的輸出如下:

輸出

atom1

atom_1

atom 1

讓我們看看 Erlang 中一些可用於處理原子的方法。

序號 方法和描述
1

is_atom

此方法用於確定術語是否確實是原子。

2

atom_to_list

此方法用於將原子轉換為列表。

3

list_to_atom

此方法用於將列表項轉換為原子。

4

atom_to_binary

此方法用於將原子轉換為二進位制值。

5

binary_to_atom

此方法用於將二進位制值轉換為原子值。

Erlang - 對映

對映是一種複合資料型別,具有可變數量的鍵值關聯。對映中的每個鍵值關聯稱為關聯對。對的鍵和值部分稱為元素。關聯對的數量稱為對映的大小。

以下程式顯示瞭如何使用對映資料型別的示例。

在這裡,我們定義了一個具有 2 個對映的對映 M1。**map_size** 是 Erlang 中定義的內建函式,可用於確定對映的大小。

示例

-module(helloworld). 
-export([start/0]). 

start() -> 
   M1 = #{name=>john,age=>25}, 
   io:fwrite("~w",[map_size(M1)]).

上述程式的輸出如下。

輸出

2

對映的一些其他方法如下所示。

序號 方法和描述
1

from_list

此方法用於根據列表生成對映。

2

find

此方法用於查詢對映中是否存在特定鍵。

3

get

此方法用於獲取對映中特定鍵的值。

4

is_key

此方法用於確定特定鍵是否在對映中定義為鍵。

5

keys

此方法用於返回對映中的所有鍵。

6

合併

此方法用於合併兩個對映。

7

put

此方法用於向對映新增鍵值對。

8

values

此方法用於返回對映中的所有值。

9

remove

此方法用於從對映中移除鍵值。

Erlang - 元組

元組是一種複合資料型別,具有固定數量的項。元組中的每一項稱為元素。元素的數量稱為元組的大小。

以下程式顯示瞭如何使用元組資料型別的示例。

在這裡,我們定義了一個具有 3 個項的 **元組 P**。**tuple_size** 是 Erlang 中定義的內建函式,可用於確定元組的大小。

示例

-module(helloworld). 
-export([start/0]). 

start() ->
   P = {john,24,{june,25}} , 
   io:fwrite("~w",[tuple_size(P)]).

上述程式的輸出如下。

輸出

3

讓我們看看元組中還有一些可用的操作。

序號 方法和描述
1

is_tuple

此方法用於確定提供的術語是否確實是元組。

2

list_to_tuple

此方法用於將列表轉換為元組。

3

tuple_to_list

此方法用於將元組轉換為列表。

Erlang - 記錄

Erlang 具有建立記錄的額外功能。這些記錄由欄位組成。例如,您可以定義一個個人記錄,它有兩個欄位,一個是 id 欄位,另一個是 name 欄位。在 Erlang 中,您可以建立此記錄的各種例項來定義具有各種名稱和 ID 的多個人。

讓我們探索如何使用記錄。

建立記錄

使用記錄識別符號建立記錄。在此記錄識別符號中,您指定構成記錄的各個欄位。下面給出一般語法和示例。

語法

record(recordname , {Field1,Field2 ..Fieldn})

引數

  • **recordname** – 這是賦予記錄的名稱。

  • **Field1,Field2 ..Fieldn** – 這些是構成記錄的各個欄位的列表。

返回值

例如

-module(helloworld). 
-export([start/0]). 
-record(person, {name = "", id}). 

start() -> 
   P = #person{name="John",id = 1}.

以上示例顯示了具有 2 個欄位的記錄的定義,一個是 id,另一個是 name。此外,記錄的構造方式如下:

語法

#recordname {fieldName1 = value1, fieldName2 = value2 .. fieldNameN = valueN}

在定義記錄的例項時,您將值分配給各個欄位。

訪問記錄的值

要訪問特定記錄的欄位和值,應使用以下語法。

語法

#recordname.Fieldname

引數

  • **recordname** – 這是賦予記錄的名稱。

  • **Fieldname** – 這是需要訪問的欄位的名稱。

返回值

分配給欄位的值。

例如

-module(helloworld). 
-export([start/0]). 
-record(person, {name = "", id}). 

start() -> 
   P = #person{name = "John",id = 1}, 
   io:fwrite("~p~n",[P#person.id]), 
   io:fwrite("~p~n",[P#person.name]).

輸出

上述程式的輸出如下。

1
“John”

更新記錄的值

記錄值的更新是透過將值更改為特定欄位,然後將記錄分配給新的變數名來完成的。下面給出一般語法和示例。

語法

#recordname.Fieldname = newvalue

引數

  • **recordname** – 這是賦予記錄的名稱。

  • **Fieldname** – 這是需要訪問的欄位的名稱。

  • **newvalue** – 這是需要分配給欄位的新值。

返回值

具有分配給欄位的新值的新記錄。

例如

-module(helloworld). 
-export([start/0]). 
-record(person, {name = "", id}). 

start() -> 
   P = #person{name = "John",id = 1}, 
   P1 = P#person{name = "Dan"}, 
   
   io:fwrite("~p~n",[P1#person.id]), 
   io:fwrite("~p~n",[P1#person.name]).

輸出

上述程式的輸出如下:

1
“Dan”

巢狀記錄

Erlang 還具有巢狀記錄的功能。以下示例顯示瞭如何建立這些巢狀記錄。

例如

-module(helloworld). 
-export([start/0]). 
-record(person, {name = "", address}). 
-record(employee, {person, id}). 

start() -> 
   P = #employee{person = #person{name = "John",address = "A"},id = 1}, 
   io:fwrite("~p~n",[P#employee.id]).

在上述示例中,需要注意以下幾點:

  • 我們首先建立一個人的記錄,其欄位值為姓名和地址。

  • 然後我們定義一個員工記錄,它將人作為欄位,還有一個名為 id 的附加欄位。

輸出

上述程式的輸出如下。

1

Erlang - 異常

任何程式語言都需要異常處理來處理執行時錯誤,以便維護應用程式的正常流程。異常通常會中斷應用程式的正常流程,這就是為什麼我們需要在應用程式中使用異常處理的原因。

通常,當 Erlang 中發生異常或錯誤時,將顯示以下訊息。

{"init terminating in do_boot", {undef,[{helloworld,start,[],[]}, 
{init,start_it,1,[]},{init,start_em,1,[]}]}}

崩潰轉儲將寫入 -

erl_crash.dump
init terminating in do_boot ()

在 Erlang 中,有三種類型的異常 -

  • 錯誤 (Error) - 呼叫 erlang:error(Reason) 將終止當前程序的執行,並在捕獲時包含最後呼叫的函式及其引數的堆疊跟蹤。這些是導致上述執行時錯誤的異常型別。

  • 退出 (Exit) - 有兩種型別的退出:'內部' 退出和 '外部' 退出。內部退出是透過呼叫函式 exit/1 觸發的,並使當前程序停止執行。外部退出是使用 exit/2 呼叫的,與 Erlang 併發方面的多個程序有關。

  • 丟擲 (Throw) - 丟擲是一種異常型別,用於程式設計師可以預期處理的情況。與退出和錯誤相比,它們並沒有真正帶有任何“崩潰程序!”的意圖,而是控制流程。由於您在預期程式設計師處理丟擲時使用它們,因此通常最好使用它們記錄模組中的使用情況。

try ... catch 是一種評估表示式的方 式,同時允許您處理成功的情況以及遇到的錯誤。

try catch 表示式的通用語法如下。

語法

try Expression of 
SuccessfulPattern1 [Guards] -> 
Expression1; 
SuccessfulPattern2 [Guards] -> 
Expression2 

catch 
TypeOfError:ExceptionPattern1 -> 
Expression3; 
TypeOfError:ExceptionPattern2 -> 
Expression4 
end

try 和 of 之間的表示式被稱為受保護的。這意味著在該呼叫中發生的任何型別的異常都將被捕獲。try ... of 和 catch 之間的模式和表示式與 case ... of 的行為完全相同。

最後是 catch 部分 – 在這裡,您可以將 TypeOfError 替換為 error、throw 或 exit,分別對應我們在本章中看到的每種型別。如果未提供型別,則假定為 throw。

以下是 Erlang 中的一些錯誤和錯誤原因 -

錯誤 錯誤型別
badarg 引數錯誤。引數的資料型別錯誤,或者格式錯誤。
badarith 算術表示式中的引數錯誤。
{badmatch,V} 匹配表示式的評估失敗。值 V 不匹配。
function_clause 在評估函式呼叫時找不到匹配的函式子句。
{case_clause,V} 在評估 case 表示式時找不到匹配的分支。值 V 不匹配。
if_clause 在評估 if 表示式時找不到 true 分支。
{try_clause,V} 在評估 try 表示式的 of 部分時找不到匹配的分支。值 V 不匹配。
undef 在評估函式呼叫時找不到函式。
{badfun,F} fun F 有問題。
{badarity,F} fun 應用於錯誤數量的引數。F 描述了 fun 和引數。
timeout_value receive..after 表示式中的超時值計算結果不是整數或無窮大。
noproc 嘗試連結到不存在的程序。

以下是這些異常如何使用以及如何操作的示例。

  • 第一個函式生成所有可能的異常型別。

  • 然後我們編寫一個包裝器函式,在 try...catch 表示式中呼叫 generate_exception

示例

-module(helloworld). 
-compile(export_all). 

generate_exception(1) -> a; 
generate_exception(2) -> throw(a); 
generate_exception(3) -> exit(a); 
generate_exception(4) -> {'EXIT', a}; 
generate_exception(5) -> erlang:error(a). 

demo1() -> 
   [catcher(I) || I <- [1,2,3,4,5]]. 
catcher(N) -> 
   try generate_exception(N) of 
      Val -> {N, normal, Val} 
   catch 
      throw:X -> {N, caught, thrown, X}; 
      exit:X -> {N, caught, exited, X}; 
      error:X -> {N, caught, error, X} 
   end. 
      
demo2() -> 
   [{I, (catch generate_exception(I))} || I <- [1,2,3,4,5]]. 
demo3() -> 
   try generate_exception(5) 
   catch 
      error:X -> 
         {X, erlang:get_stacktrace()} 
   end. 
   
lookup(N) -> 
   case(N) of 
      1 -> {'EXIT', a}; 
      2 -> exit(a) 
   end.

如果我們執行程式 helloworld:demo(),我們將獲得以下輸出 -

輸出

[{1,normal,a},
{2,caught,thrown,a},
{3,caught,exited,a},
{4,normal,{'EXIT',a}},
{5,caught,error,a}]

Erlang - 宏

宏通常用於內聯程式碼替換。在 Erlang 中,宏透過以下語句定義。

  • -define(Constant, Replacement).
  • -define(Func(Var1, Var2,.., Var), Replacement).

以下是使用第一種語法的宏示例 -

示例

-module(helloworld). 
-export([start/0]). 
-define(a,1). 

start() -> 
   io:fwrite("~w",[?a]).

從上面的程式中您可以看到,宏是透過使用“?”符號展開的。常量在原處被宏中定義的值替換。

上述程式的輸出將是:

輸出

1

使用函式類宏的示例如下 -

示例

-module(helloworld). 
-export([start/0]). 
-define(macro1(X,Y),{X+Y}). 

start() ->
   io:fwrite("~w",[?macro1(1,2)]).

上述程式的輸出將是:

輸出

{3}

以下附加語句可用於宏 -

  • undef(Macro) - 取消定義宏;此後您無法呼叫該宏。

  • ifdef(Macro) - 僅當已定義 Macro 時才評估以下行。

  • ifndef(Macro) - 僅當 Macro 未定義時才評估以下行。

  • else - 允許在 ifdef 或 ifndef 語句之後使用。如果條件為假,則評估 else 之後的語句。

  • endif - 標記 ifdef 或 ifndef 語句的結尾。

使用上述語句時,應按照以下程式中所示的方式正確使用。

-ifdef(<FlagName>).

-define(...).
-else.
-define(...).
-endif.

Erlang - 標頭檔案

標頭檔案就像任何其他程式語言中的包含檔案一樣。它對於將模組拆分為不同的檔案然後在單獨的程式中訪問這些標頭檔案很有用。為了檢視標頭檔案的實際作用,讓我們看看我們前面關於記錄的示例之一。

讓我們首先建立一個名為 user.hrl 的檔案並新增以下程式碼 -

-record(person, {name = "", id}).

現在在我們的主程式檔案中,讓我們新增以下程式碼 -

示例

-module(helloworld). 
-export([start/0]). 
-include("user.hrl"). 

start() -> 
   P = #person{name = "John",id = 1}, 
   io:fwrite("~p~n",[P#person.id]), 
   io:fwrite("~p~n",[P#person.name]).

正如您從上面的程式中看到的,我們實際上只是包含 user.hrl 檔案,它會自動插入 –record 程式碼。

如果您執行上述程式,您將獲得以下輸出。

輸出

1
“John”

您也可以對宏執行相同的操作,您可以在標頭檔案中定義宏並在主檔案中引用它。讓我們看一個這樣的例子 -

讓我們首先建立一個名為 user.hrl 的檔案並新增以下程式碼 -

-define(macro1(X,Y),{X+Y}).

現在在我們的主程式檔案中,讓我們新增以下程式碼 -

示例

-module(helloworld). 
-export([start/0]). 
-include("user.hrl"). 

start() -> 
   io:fwrite("~w",[?macro1(1,2)]).

如果您執行上述程式,您將獲得以下輸出 -

輸出

{3}

Erlang - 預處理器

在編譯 Erlang 模組之前,它會由 Erlang 預處理器自動處理。預處理器將展開原始檔中可能存在的任何宏,並插入任何必要的包含檔案。

通常,您不需要檢視預處理器的輸出,但在特殊情況下(例如,除錯有故障的宏時),您可能希望儲存預處理器的輸出。要檢視預處理模組 some_module.erl 的結果,請使用 OS shell 命令。

erlc -P some_module.erl

例如,假設我們有以下程式碼檔案 -

示例

-module(helloworld). 
-export([start/0]). 
-include("user.hrl"). 

start() -> 
   io:fwrite("~w",[?macro1(1,2)]).

如果我們從命令列執行以下命令 -

erlc –P helloworld.erl

將生成一個名為 helloworld.P 的檔案。如果您開啟此檔案,您將找到以下內容,這就是預處理器將編譯的內容。

-file("helloworld.erl", 1). -module(helloworld).

-export([start/0]).
-file("user.hrl", 1).
-file("helloworld.erl", 3).

start() ->
   io:fwrite("~w", [{1 + 2}]).

Erlang - 模式匹配

模式看起來與術語相同——它們可以是簡單的文字,如原子和數字,也可以是複合的,如元組和列表,或者兩者的混合。它們還可以包含變數,這些變數是開頭為大寫字母或下劃線的字母數字字串。一個特殊的“匿名變數” _(下劃線)用於您不關心要匹配的值,並且不會使用它。

如果模式具有與要匹配的術語相同的“形狀”,並且遇到的原子相同,則模式匹配成功。例如,以下匹配成功 -

  • B = 1.
  • 2 = 2.
  • {ok, C} = {ok, 40}.
  • [H|T] = [1, 2, 3,4].

請注意,在第四個示例中,管道(|)表示列表的首部和尾部,如術語中所述。另請注意,左側應與右側匹配,這是模式的正常情況。

以下模式匹配示例將失敗。

  • 1 = 2.
  • {ok, A} = {failure, "Don't know the question"}.
  • [H|T] = [].

在模式匹配運算子的情況下,失敗會生成錯誤,並且程序退出。如何在錯誤中捕獲和處理這一點將在錯誤中介紹。模式用於選擇將執行哪個函式子句。

Erlang - 保護條件

保護條件是我們用來增強模式匹配功能的結構。使用保護條件,我們可以對模式中的變數執行簡單的測試和比較。

保護語句的通用語法如下 -

function(parameter) when condition ->

其中:

  • Function(parameter) - 這是在保護條件中使用的函式宣告。

  • Parameter - 通常,保護條件基於引數。

  • Condition - 應評估的條件,以檢視是否應執行該函式。

  • 指定保護條件時必須使用 when 語句。

讓我們快速瞭解一下如何使用保護條件 -

示例

-module(helloworld). 
-export([display/1,start/0]). 

display(N) when N > 10 ->   
   io:fwrite("greater then 10"); 
display(N) when N < 10 -> io:fwrite("Less 
   than 10"). 

start() -> 
   display(11).

關於上述示例,需要注意以下幾點 -

  • 顯示函式與保護條件一起定義。第一個顯示宣告有一個保護條件,即引數 N 大於 10。因此,如果引數大於 10,則將呼叫該函式。

  • 再次定義顯示函式,但這次的保護條件是小於 10。透過這種方式,您可以多次定義相同的函式,每個函式都有一個單獨的保護條件。

上述程式的輸出如下:

輸出

greater than 10

保護條件也可以用於 if elsecase 語句。讓我們看看如何在這些語句上執行保護操作。

‘if’ 語句的保護條件

保護條件也可以用於 if 語句,以便執行的語句序列基於保護條件。讓我們看看如何實現這一點。

示例

-module(helloworld). 
-export([start/0]). 

start() -> 
   N = 9, 
   if 
      N > 10 -> 
         io:fwrite("N is greater than 10"); 
      true -> 
         io:fwrite("N is less than 10") 
   end.

關於上述示例,需要注意以下幾點 -

  • 保護函式與 if 語句一起使用。如果保護函式評估結果為真,則顯示語句“N 大於 10”。

  • 如果保護函式評估結果為假,則顯示語句“N 小於 10”。

上述程式的輸出如下:

輸出

N is less than 10

‘case’ 語句的保護條件

保護條件也可以用於 case 語句,以便執行的語句序列基於保護條件。讓我們看看如何實現這一點。

示例

-module(helloworld). 
-export([start/0]). 

start() -> 
   A = 9, 
   case A of {A} when A>10 -> 
      io:fwrite("The value of A is greater than 10"); _ -> 
      io:fwrite("The value of A is less than 10") 
   end.

關於上述示例,需要注意以下幾點 -

  • 保護函式與 case 語句一起使用。如果保護函式評估結果為真,則顯示語句“A 的值大於 10”。

  • 如果保護函式評估結果為其他任何值,則顯示語句“A 的值小於 10”。

上述程式的輸出如下:

輸出

The value of A is less than 10

多個保護條件

還可以為函式指定多個保護條件。具有多個保護條件的保護語句的通用語法如下所示 -

function(parameter) when condition1 , condition1 , .. conditionN ->

其中:

  • Function(parameter) - 這是使用保護條件的函式宣告。

  • Parameter - 通常,保護條件基於引數。

  • condition1, condition1, .. conditionN - 這些是應用於函式的多個保護條件。

  • 指定保護條件時必須使用 when 語句。

讓我們快速瞭解一下如何使用多個保護條件 -

示例

-module(helloworld). 
-export([display/1,start/0]). 

display(N) when N > 10 , is_integer(N) -> 
   io:fwrite("greater then 10"); 
display(N) when N < 10 -> 
   io:fwrite("Less than 10"). 
   
start() -> 
   display(11).

關於上述示例,需要注意以下幾點 -

  • 你會注意到,對於第一個顯示函式宣告,除了 N>10 的條件外,還指定了 **is_integer** 條件。因此,只有當 N 的值是整數且大於 10 時,才會執行此函式。

上述程式的輸出如下:

輸出

Greater than 10

Erlang - BIFS (內建函式)

BIF 是內置於 Erlang 的函式。它們通常執行 Erlang 中無法程式設計完成的任務。例如,不可能將列表轉換為元組,也無法查詢當前時間和日期。要執行此類操作,我們呼叫 BIF。

讓我們來看一個 BIF 的使用方法示例:

示例

-module(helloworld). 
-export([start/0]). 

start() ->   
   io:fwrite("~p~n",[tuple_to_list({1,2,3})]), 
   io:fwrite("~p~n",[time()]).

關於上述示例,需要注意以下幾點 -

  • 在第一個示例中,我們使用名為 **tuple_to_list** 的 BIF 將元組轉換為列表。

  • 在第二個 BIF 函式中,我們使用 **time 函式** 輸出系統時間。

上述程式的輸出如下:

輸出

[1,2,3]
{10,54,56}

讓我們來看一下 Erlang 中可用的其他一些 BIF 函式。

序號 BIF 函式及說明
1

date

此方法返回當前系統日期。

2

byte_size

此方法返回 Bitstring 中包含的位元組數。

3

element

此方法返回元組中的第 N 個元素。

4

float

此方法返回特定數字的浮點值。

5

get

此方法將程序字典作為列表返回。

6

put

此方法用於在程序字典中放入 **鍵值對**。

7

localtime

此方法用於提供系統中的本地日期和時間。

8

memory

返回一個列表,其中包含有關 Erlang 模擬器動態分配的記憶體的資訊。

9

now

此方法返回元組 {MegaSecs, Secs, MicroSecs},它是自 1970 年 1 月 1 日 00:00 GMT 以來經過的時間。

10

ports

返回本地節點上的所有埠列表。

11

processes

返回與當前在本地節點上存在的所有程序對應的程序識別符號列表。

12

universaltime

根據協調世界時 (UTC) 返回當前日期和時間。

Erlang - 二進位制

使用名為二進位制的資料結構來儲存大量原始資料。與列表或元組相比,二進位制以更節省空間的方式儲存資料,並且執行時系統針對二進位制的高效輸入和輸出進行了最佳化。

二進位制被寫入和列印為整數或字串序列,括在雙小於和大於括號中。

以下是 Erlang 中二進位制的示例:

示例

-module(helloworld). 
-export([start/0]). 

start() -> 
   io:fwrite("~p~n",[<<5,10,20>>]), 
   io:fwrite("~p~n",[<<"hello">>]).

執行上述程式時,我們將得到以下結果。

輸出

<<5,10,20>>
<<"hello">>

讓我們來看一下 Erlang 中可用於處理二進位制的函式:

序號 方法和描述
1

list_to_binary

此方法用於將現有列表轉換為二進位制列表。

2

split_binary

此方法用於根據指定的索引位置拆分二進位制列表。

3

term_to_binary

此方法用於將項轉換為二進位制。

4

is_binary

此方法用於檢查位元串是否確實是二進位制值。

5

binary_part

此方法用於提取二進位制字串的一部分。

6

binary_to_float

此方法用於將二進位制值轉換為浮點值。

7

binary_to_integer

此方法用於將二進位制值轉換為整數值。

8

binary_to_list

此方法用於將二進位制值轉換為列表。

9

binary_to_atom

此方法用於將二進位制值轉換為原子。

Erlang - Fun (匿名函式)

Fun 用於在 Erlang 中定義匿名函式。匿名函式的一般語法如下:

語法

F = fun (Arg1, Arg2, ... ArgN) ->
   ...
End

其中

  • **F** - 這是分配給匿名函式的變數名。

  • **Arg1, Arg2, ... ArgN** - 這些是傳遞給匿名函式的引數。

以下示例展示瞭如何使用匿名函式。

示例

-module(helloworld). 
-export([start/0]). 

start() -> 
   A = fun() -> io:fwrite("Hello") end, 
   A().

關於上述程式,需要注意以下幾點:

  • 匿名函式被賦值給變數 A。

  • 透過變數 A() 呼叫匿名函式。

執行上述程式後,我們將得到以下結果。

“Hello”

另一個匿名函式示例如下,但這是使用引數的。

-module(helloworld). 
-export([start/0]). 

start() -> 
   A = fun(X) -> 
      io:fwrite("~p~n",[X]) 
      end, 
   A(5).

執行上述程式後,我們將得到以下結果。

輸出

5

使用變數

匿名函式能夠訪問匿名函式作用域之外的變數。讓我們來看一個例子:

示例

-module(helloworld). 
-export([start/0]). 

start() -> 
   B = 6, 
   A = fun(X) -> 
      io:fwrite("~p~n",[X]), 
      io:fwrite("~p~n",[B]) 
      end, 
   A(5).

關於上述程式,需要注意以下幾點:

  • 變數 B 在匿名函式的作用域之外。

  • 匿名函式仍然可以訪問在全域性作用域中定義的變數。

執行上述程式後,我們將得到以下結果。

輸出

5

6

函式中的函式

高階函式的另一個最強大的方面是,你可以在一個函式中定義另一個函式。讓我們來看一個如何實現這一點的例子。

示例

-module(helloworld). 
-export([start/0]). 

start() -> 
   Adder = fun(X) -> fun(Y) -> io:fwrite("~p~n",[X + Y]) end end, 
   A = Adder(6), 
   A(10).

關於上述程式,需要注意以下幾點:

  • Adder 是一個定義為 fun(X) 的高階函式。

  • Adder 函式 fun(X) 引用另一個函式 fun(Y)。

執行上述程式後,我們將得到以下結果。

輸出

16

Erlang - 程序

Erlang 中併發性的粒度是程序。程序是一個與其他程序併發執行且獨立於其他程序的活動/任務。Erlang 中的這些程序與大多數人熟悉的程序和執行緒不同。Erlang 程序是輕量級的,在 (記憶體) 中與其他程序隔離執行,並由 Erlang 的虛擬機器 (VM) 排程。程序的建立時間非常低,剛剛生成的程序的記憶體佔用非常小,單個 Erlang VM 可以執行數百萬個程序。

程序是藉助 spawn 方法建立的。該方法的一般語法如下所示。

語法

spawn(Module, Name, Args)

引數

  • **Module** - 這是一個預定義的原子值,必須是 ?MODULE。

  • **Name** - 這是在定義程序時要呼叫的函式的名稱。

  • **Args** - 這些是要傳送到函式的引數。

返回值

返回新建立程序的程序 ID。

例如

以下程式顯示了 spawn 方法的示例。

-module(helloworld). 
-export([start/0, call/2]). 

call(Arg1, Arg2) -> 
   io:format("~p ~p~n", [Arg1, Arg2]). 
start() -> 
   Pid = spawn(?MODULE, call, ["hello", "process"]), 
   io:fwrite("~p",[Pid]).

關於上述程式,需要注意以下幾點:

  • 定義了一個名為 call 的函式,將用於建立程序。

  • spawn 方法使用引數 hello 和 process 呼叫 call 函式。

輸出

執行上述程式後,我們將得到以下結果。

<0.29.0>"hello" "process"

現在讓我們來看一下程序的其他可用函式。

序號 方法和描述
1

is_pid

此方法用於確定程序 ID 是否存在。

2

is_process_alive

這稱為 is_process_alive(Pid)。Pid 必須引用本地節點上的程序。

3

pid_to_list

它將程序 ID 轉換為列表。

4

registered

返回一個列表,其中包含所有已註冊程序的名稱。

5

self

最常用的 BIF 之一,返回呼叫程序的 pid。

6

register

這用於在系統中註冊程序。

7

whereis

它被稱為 whereis(Name)。返回以該名稱註冊的程序的 pid。

8

unregister

這用於在系統中登出程序。

Erlang - 電子郵件

要使用 Erlang 傳送電子郵件,你需要使用 **github** 上提供的軟體包。github 連結為:https://github.com/Vagabond/gen_smtp

此連結包含一個 **smtp 實用程式**,可用於從 Erlang 應用程式傳送電子郵件。請按照以下步驟操作,即可從 Erlang 傳送電子郵件。

**步驟 1** - 從 **github 站點**下載 **erl 檔案**。應將檔案下載到你的 **helloworld.erl** 應用程式所在的目錄。

**步驟 2** - 使用 **erlc 命令**編譯以下列表中顯示的所有 **與 smtp 相關的檔案**。需要編譯以下檔案:

  • smtp_util
  • gen_smtp_client
  • gen_smtp_server
  • gen_smtp_server_session
  • binstr
  • gen_smtp_application
  • socket

**步驟 3** - 可以編寫以下程式碼來使用 smtp 傳送電子郵件。

示例

-module(helloworld). 
-export([start/0]). 

start() -> 
   gen_smtp_client:send({"sender@gmail.com", ["receiver@gmail.com"], "Subject: testing"},
   
   [{relay, "smtp.gmail.com"}, {ssl, true}, {username, "sender@gmail.com"}, 
      {password, "senderpassword"}]).

關於上述程式,需要注意以下幾點:

  • 上述 smtp 函式與 Google 提供的 smtp 伺服器一起使用。

  • 由於我們想使用安全的 smtp 傳送,因此我們將 ssl 引數指定為 true。

  • 你需要將中繼指定為 **smtp.gmail.com**。

  • 你需要提及具有傳送電子郵件許可權的使用者名稱和密碼。

配置上述所有設定並執行程式後,接收方將成功收到電子郵件。

Erlang - 資料庫

Erlang 能夠連線到傳統的資料庫,例如 SQL Server 和 Oracle。Erlang 有一個 **內建的 odbc 庫**,可用於處理資料庫。

資料庫連線

在我們的示例中,我們將使用 Microsoft SQL Server。在連線到 Microsoft SQL Server 資料庫之前,請確保檢查以下要點。

  • 你已建立資料庫 TESTDB。

  • 你已在 TESTDB 中建立表 EMPLOYEE。

  • 此表具有欄位 FIRST_NAME、LAST_NAME、AGE、SEX 和 INCOME。

  • 已設定使用者 ID“testuser”和密碼“test123”以訪問 TESTDB。

  • 確保你已建立名為 **usersqlserver** 的 ODBC DSN,它建立到資料庫的 ODBC 連線。

建立連線

要建立到資料庫的連線,可以使用以下程式碼示例。

示例

-module(helloworld). 
-export([start/0]). 

start() ->
   odbc:start(), 
   {ok, Ref} = odbc:connect("DSN = usersqlserver;UID = testuser;PWD = test123", []), 
   io:fwrite("~p",[Ref]).

上述程式的輸出如下:

輸出

<0.33.0>

關於上述程式,需要注意以下幾點:

  • odbc 庫的 start 方法用於指示資料庫操作的開始。

  • connect 方法需要 DSN、使用者名稱和密碼才能連線。

建立資料庫表

連線到資料庫後的下一步是在資料庫中建立表。以下示例顯示瞭如何使用 Erlang 在資料庫中建立表。

示例

-module(helloworld). 
-export([start/0]). 

start() -> 
   odbc:start(), 
   {ok, Ref} = odbc:connect("DSN = usersqlserver; UID = testuser;PWD = test123, []), 
   odbc:sql_query(Ref, "CREATE TABLE EMPLOYEE (FIRSTNAME char varying(20), 
   LASTNAME char varying(20), AGE integer, SEX char(1), INCOME integer)")

如果你現在檢查資料庫,你將看到已建立名為 **EMPLOYEE** 的表。

將記錄插入資料庫

當你想將記錄建立到資料庫表中時,這是必需的。

以下示例將記錄插入 employee 表中。如果表成功更新,則記錄和語句將返回更新記錄的值以及已更新的記錄數。

示例

-module(helloworld). 
-export([start/0]). 

start() -> 
   odbc:start(), 
   {ok, Ref} = odbc:connect("DSN = usersqlserver; UID = testuser;PWD = test123", []), 
   io:fwrite("~p",[odbc:sql_query(Ref, 
   "INSERT INTO EMPLOYEE VALUES('Mac', 'Mohan', 20, 'M', 2000)")]).

上述程式的輸出將是:

輸出

{updated,1}

從資料庫中提取記錄

Erlang 還能夠從資料庫中提取記錄。這是透過 **sql_query 方法**完成的。

以下程式顯示了一個示例:

示例

-module(helloworld). 
-export([start/0]). 

start() ->
   odbc:start(), 
   {ok, Ref} = odbc:connect("DSN = usersqlserver; UID = testuser;PWD = test123", []), 
   io:fwrite("~p",[odbc:sql_query(Ref, "SELECT * FROM EMPLOYEE") ]).

上述程式的輸出如下:

輸出

{selected,["FIRSTNAME","LASTNAME","AGE","SEX","INCOME"],
[{"Mac","Mohan",20,"M",2000}]}

因此,你可以看到上一節中的插入命令有效,並且選擇命令返回了正確的資料。

基於引數從資料庫中提取記錄

Erlang 還能夠根據某些篩選條件從資料庫中提取記錄。

示例如下:

示例

-module(helloworld). 
-export([start/0]). 

start() -> 
   odbc:start(), 
   {ok, Ref} = odbc:connect("DSN=usersqlserver; UID=testuser;PWD=test123", []), 
   io:fwrite("~p",[ odbc:param_query(Ref, "SELECT * FROM EMPLOYEE WHERE SEX=?", 
   [{{sql_char, 1}, ["M"]}])]).

上述程式的輸出將是:

輸出

{selected,["FIRSTNAME","LASTNAME","AGE","SEX","INCOME"],
         [{"Mac","Mohan",20,"M",2000}]}

更新資料庫中的記錄

Erlang 還能夠更新資料庫中的記錄。

相同的示例如下:

示例

-module(helloworld). 
-export([start/0]). 

start() -> 
   odbc:start(), 
   {ok, Ref} = odbc:connect("DSN = usersqlserver; UID = testuser;PWD = test123", []), 
   
   io:fwrite("~p",[ odbc:sql_query(Ref, "
      UPDATE EMPLOYEE SET AGE = 5 WHERE INCOME= 2000")]).

上述程式的輸出將是:

輸出

{updated,1}

從資料庫中刪除記錄

Erlang 也能夠從資料庫中刪除記錄。

相同的示例如下:

示例

-module(helloworld). 
-export([start/0]). 

start() -> 
   odbc:start(), 
   {ok, Ref} = odbc:connect("DSN = usersqlserver; UID = testuser;PWD = test123", []), 
   io:fwrite("~p",[ odbc:sql_query(Ref, "DELETE EMPLOYEE WHERE INCOME= 2000")]).

上述程式的輸出如下:

輸出

{updated,1}

表結構

Erlang 也能夠描述表結構。

示例如下:

示例

-module(helloworld). 
-export([start/0]). 

start() -> 
   odbc:start(), 
   {ok, Ref} = odbc:connect("DSN = usersqlserver; UID = testuser;PWD = test123", []), 
   io:fwrite("~p",[odbc:describe_table(Ref, "EMPLOYEE")]).

上述程式的輸出如下:

輸出

{ok,[{"FIRSTNAME",{sql_varchar,20}},
   {"LASTNAME",{sql_varchar,20}},
   {"AGE",sql_integer},
   {"SEX",{sql_char,1}},
   {"INCOME",sql_integer}]}

記錄計數

Erlang 也能夠獲取表中記錄的總數。

以下程式展示了一個示例。

示例

-module(helloworld). 
-export([start/0]). 

start() ->
   odbc:start(), 
   {ok, Ref} = odbc:connect("DSN = usersqlserver; UID = sa;PWD = demo123", []), 
   io:fwrite("~p",[odbc:select_count(Ref, "SELECT * FROM EMPLOYEE")]).

上述程式的輸出將是:

{ok,1}

Erlang - 埠

在 Erlang 中,埠用於不同程式之間的通訊。套接字是一個通訊端點,允許機器透過使用網際網路協議 (IP) 在網際網路上進行通訊。

埠中使用的協議型別

有兩種可用於通訊的協議。一種是 UDP,另一種是 TCP。UDP 允許應用程式相互發送短訊息(稱為資料報),但不能保證這些訊息的投遞。它們也可能亂序到達。另一方面,TCP 提供可靠的位元組流,只要連線建立,這些位元組流就會按順序投遞。

讓我們來看一個使用 UDP 開啟埠的簡單示例。

示例

-module(helloworld). 
-export([start/0]). 

start() -> 
   {ok, Socket} = gen_udp:open(8789), 
   io:fwrite("~p",[Socket]).

關於上述程式,需要注意以下幾點:

  • gen_udp 包含 Erlang 中用於 UDP 通訊的模組。

  • 這裡 8789 是在 Erlang 中開啟的埠號。您需要確保此埠號可用且可以使用。

上述程式的輸出為:

#Port<0.376>

在埠上傳送訊息

開啟埠後,可以在埠上傳送訊息。這是透過 send 方法完成的。讓我們來看一下語法和下面的示例。

語法

send(Socket, Address, Port, Packet)

引數

  • Socket − 這是使用 gen_udp:open 命令建立的套接字。

  • Address − 這是要將訊息傳送到的機器地址。

  • port − 這是需要傳送訊息的埠號。

  • Packet − 這是需要傳送的資料包或訊息詳細資訊。

返回值

如果訊息傳送成功,則返回 ok 訊息。

例如

-module(helloworld). 
-export([start/0]). 

start() ->
   {ok, Socket} = gen_udp:open(8789), 
   io:fwrite("~p",[Socket]), 
   io:fwrite("~p",[gen_udp:send 
   (Socket,"localhost",8789,"Hello")]).

輸出

上述程式的輸出如下。

#Port<0.376>ok

在埠上接收訊息

開啟埠後,也可以在埠上接收訊息。這是透過recv 方法完成的。讓我們來看一下語法和下面的示例。

語法

recv(Socket, length)

引數

  • Socket − 這是使用 gen_udp:open 命令建立的套接字。

  • Length − 這是需要接收的訊息的長度。

返回值

如果訊息傳送成功,則返回 ok 訊息。

例如

-module(helloworld). 
-export([start/0]). 

start() ->
   {ok, Socket} = gen_udp:open(8789), 
   io:fwrite("~p",[Socket]), 
   io:fwrite("~p",[gen_udp:send(Socket,"localhost",8789,"Hello")]),
   io:fwrite("~p",[gen_udp:recv(Socket, 20)]).

完整的程式

顯然,我們不能在同一個程式中同時使用相同的傳送和接收訊息。您需要在不同的程式中定義它們。因此,讓我們建立以下程式碼,該程式碼建立一個偵聽訊息的伺服器元件和一個傳送訊息的客戶端元件。

示例

-module(helloworld). 
-export([start/0,client/1]). 

start() -> 
   spawn(fun() -> server(4000) end).

server(Port) ->
   {ok, Socket} = gen_udp:open(Port, [binary, {active, false}]), 
   io:format("server opened socket:~p~n",[Socket]), 
   loop(Socket). 

loop(Socket) ->
   inet:setopts(Socket, [{active, once}]), 
   receive 
      {udp, Socket, Host, Port, Bin} -> 
      io:format("server received:~p~n",[Bin]), 
      gen_udp:send(Socket, Host, Port, Bin), 
      loop(Socket) 
   end. 

client(N) -> 
   {ok, Socket} = gen_udp:open(0, [binary]), 
   io:format("client opened socket=~p~n",[Socket]), 
   ok = gen_udp:send(Socket, "localhost", 4000, N), Value = receive 
      {udp, Socket, _, _, Bin} ->
         io:format("client received:~p~n",[Bin]) after 2000 ->
      0 
   end, 
   
gen_udp:close(Socket), 
Value.

關於上述程式,需要注意以下幾點:

  • 我們定義了兩個函式,第一個是 server。它將用於偵聽 4000 埠。第二個是 client,它將用於向伺服器元件傳送“Hello”訊息。

  • 接收迴圈用於讀取在定義迴圈內傳送的訊息。

輸出

現在,您需要從兩個視窗執行程式。第一個視窗將用於透過在erl 命令列視窗中執行以下程式碼來執行伺服器元件。

helloworld:start().

這將在命令列視窗中顯示以下輸出。

server opened socket:#Port<0.2314>

現在在第二個 erl 命令列視窗中,執行以下命令。

Helloworld:client(“<<Hello>>”).

發出此命令後,將在第一個命令列視窗中顯示以下輸出。

server received:<<"Hello">>

Erlang - 分散式程式設計

分散式程式是指那些設計為在計算機網路上執行並且只能透過訊息傳遞來協調其活動的程式。

我們可能需要編寫分散式應用程式的原因有很多。以下是一些原因。

  • 效能 − 透過安排程式的不同部分在不同的機器上並行執行,我們可以使程式執行得更快。

  • 可靠性 − 透過將系統構建為在多臺機器上執行,我們可以構建容錯系統。如果一臺機器發生故障,我們可以在另一臺機器上繼續執行。

  • 可擴充套件性 − 當我們擴充套件應用程式時,遲早我們會耗盡即使是最強大的機器的功能。在這個階段,我們必須新增更多機器來增加容量。新增新機器應該是一個簡單的操作,不需要對應用程式架構進行大的更改。

分散式 Erlang 中的核心概念是節點。節點是自包含的。

Erlang 系統包含一個完整的虛擬機器,它具有自己的地址空間和自己的程序集。

讓我們看一下用於分散式程式設計的各種方法

序號 方法和描述
1

spawn

這用於建立一個新程序並對其進行初始化。

2

node

這用於確定程序需要在其上執行的節點的值。

3

在節點上 spawn

這用於在一個節點上建立一個新程序。

4

is_alive

如果本地節點處於活動狀態並且可以成為分散式系統的一部分,則返回 true。

5

spawnlink

這用於在一個節點上建立一個新的程序連結。

Erlang - OTP (開放電信平臺)

OTP 代表開放電信平臺。它是一個應用程式作業系統,以及一組用於構建大型、容錯、分散式應用程式的庫和程式。如果您想使用 OTP 編寫自己的應用程式,那麼您會發現非常有用的核心概念是 OTP 行為。行為封裝了常見的行為模式——可以將其視為一個由回撥模組引數化的應用程式框架。

OTP 的強大功能來自於諸如容錯性、可擴充套件性、動態程式碼升級等特性,這些特性可以由行為本身提供。因此,第一個基本概念是建立一個模擬 OTP 環境基礎的伺服器元件,讓我們來看一下以下示例。

示例

-module(server). 
-export([start/2, rpc/2]). 

start(Name, Mod) -> 
   register(Name, spawn(fun() -> loop(Name, Mod, Mod:init()) end)). 
rpc(Name, Request) -> 
   Name ! {self(), Request}, 
   receive 
      {Name, Response} -> Response 
   end. 
   
loop(Name, Mod, State) ->
   receive 
      {From, Request} ->
         {Response, State1} = Mod:handle(Request, State), 
         From ! {Name, Response}, 
         loop(Name, Mod, State1) 
   end.

關於上述程式,需要注意以下幾點:

  • 該程序使用 register 函式向系統註冊。

  • 該程序生成一個處理處理的迴圈函式。

現在讓我們編寫一個將利用伺服器程式的客戶端程式。

示例

-module(name_server). 
-export([init/0, add/2, whereis/1, handle/2]). 
-import(server1, [rpc/2]). 

add(Name, Place) -> rpc(name_server, {add, Name, Place}). 
whereis(Name) -> rpc(name_server, {whereis, Name}). 

init() -> dict:new().
handle({add, Name, Place}, Dict) -> {ok, dict:store(Name, Place, Dict)}; 
handle({whereis, Name}, Dict) -> {dict:find(Name, Dict), Dict}.

這段程式碼實際上執行兩項任務。它充當從伺服器框架程式碼呼叫的回撥模組,同時包含客戶端將呼叫的介面例程。通常的 OTP 約定是將這兩個函式組合在同一個模組中。

以下是執行上述程式的方法:

erl中,首先透過執行以下命令執行伺服器程式。

server(name_server,name_server)

您將得到以下輸出:

輸出

true

然後,執行以下命令

name_server.add(erlang,”Tutorialspoint”).

您將得到以下輸出:

輸出

Ok

然後,執行以下命令:

name_server.whereis(erlang).

您將得到以下輸出:

輸出

{ok,"Tutorialspoint"}

Erlang - 併發

Erlang 中的併發程式設計需要遵循以下基本原則或流程。

該列表包括以下原則:

piD = spawn(Fun)

建立一個新的併發程序來評估 Fun。新程序與呼叫者並行執行。示例如下:

示例

-module(helloworld). 
-export([start/0]). 

start() ->
   spawn(fun() -> server("Hello") end). 

server(Message) ->
   io:fwrite("~p",[Message]).

上述程式的輸出為:

輸出

“Hello”

Pid ! Message

向識別符號為 Pid 的程序傳送訊息。訊息傳送是非同步的。傳送者不會等待,而是繼續執行其正在執行的操作。‘!’ 稱為傳送運算子。

示例如下:

示例

-module(helloworld). 
-export([start/0]). 
start() -> 
   Pid = spawn(fun() -> server("Hello") end), 
   Pid ! {hello}. 

server(Message) ->
   io:fwrite("~p",[Message]).

Receive…end

接收已傳送到程序的訊息。它具有以下語法:

語法

receive

Pattern1 [when Guard1] ->

Expressions1;

Pattern2 [when Guard2] ->

Expressions2;
...
End

當訊息到達程序時,系統嘗試將其與 Pattern1(可能帶有 Guard1 保護)進行匹配;如果成功,則評估 Expressions1。如果第一個模式不匹配,則嘗試 Pattern2,依此類推。如果沒有任何模式匹配,則將訊息儲存以供以後處理,並且程序等待下一條訊息。

以下程式顯示了包含所有三個命令的整個流程的示例。

示例

-module(helloworld). 
-export([loop/0,start/0]). 

loop() ->
   receive 
      {rectangle, Width, Ht} -> 
         io:fwrite("Area of rectangle is ~p~n" ,[Width * Ht]), 
         loop(); 
      {circle, R} ->
      io:fwrite("Area of circle is ~p~n" , [3.14159 * R * R]), 
      loop(); 
   Other ->
      io:fwrite("Unknown"), 
      loop() 
   end. 

start() ->
   Pid = spawn(fun() -> loop() end), 
   Pid ! {rectangle, 6, 10}.

關於上述程式,需要注意以下幾點:

  • loop 函式具有 receive end 迴圈。因此,當傳送訊息時,它將由 receive end 迴圈處理。

  • 生成一個新的程序,該程序進入 loop 函式。

  • 透過 Pid ! message 命令將訊息傳送到生成的程序。

上述程式的輸出為:

輸出

Area of the Rectangle is 60

最大程序數

在併發中,確定系統上允許的最大程序數非常重要。然後,您應該能夠理解系統上可以併發執行多少個程序。

讓我們來看一個如何確定系統上可以執行的最大程序數的示例。

-module(helloworld). 
-export([max/1,start/0]). 

max(N) -> 
   Max = erlang:system_info(process_limit), 
   io:format("Maximum allowed processes:~p~n" ,[Max]), 
   
   statistics(runtime), 
   statistics(wall_clock), 
   
   L = for(1, N, fun() -> spawn(fun() -> wait() end) end), 
   {_, Time1} = statistics(runtime), 
   {_, Time2} = statistics(wall_clock), lists:foreach(fun(Pid) -> Pid ! die end, L), 
   
   U1 = Time1 * 1000 / N, 
   U2 = Time2 * 1000 / N, 
   io:format("Process spawn time=~p (~p) microseconds~n" , [U1, U2]).
   wait() -> 
   
   receive 
      die -> void 
   end. 
 
for(N, N, F) -> [F()]; 
for(I, N, F) -> [F()|for(I+1, N, F)]. 

start()->
   max(1000), 
   max(100000).

在任何具有良好處理能力的機器上,上述兩個 max 函式都將透過。以下是上述程式的示例輸出。

Maximum allowed processes:262144

Process spawn time=47.0 (16.0) microseconds

Maximum allowed processes:262144

Process spawn time=12.81 (10.15) microseconds

帶超時的接收

有時,receive 語句可能會永遠等待永遠不會出現的郵件。這可能有很多原因。例如,我們的程式中可能存在邏輯錯誤,或者要向我們傳送訊息的程序可能在傳送訊息之前已經崩潰。為了避免這個問題,我們可以向 receive 語句新增超時。這設定了程序等待接收訊息的最大時間。

以下是帶有指定超時的 receive 訊息的語法

語法

receive 
Pattern1 [when Guard1] -> 
Expressions1; 

Pattern2 [when Guard2] ->
Expressions2; 
... 
after Time -> 
Expressions 
end

最簡單的例子是建立一個休眠函式,如下面的程式所示。

示例

-module(helloworld). 
-export([sleep/1,start/0]). 

sleep(T) ->
   receive 
   after T -> 
      true 
   end. 
   
start()->
   sleep(1000).

上述程式碼將在實際退出之前休眠 1000 毫秒。

選擇性接收

Erlang 中的每個程序都有一個關聯的郵箱。當您向程序傳送訊息時,該訊息將放入郵箱中。僅當程式評估 receive 語句時,才會檢查此郵箱。

以下是選擇性 receive 語句的一般語法。

語法

receive 
Pattern1 [when Guard1] ->
Expressions1; 

Pattern2 [when Guard1] ->
Expressions1; 
... 
after 
Time ->
ExpressionTimeout 
end

這就是上述 receive 語句的工作方式:

  • 當我們輸入 receive 語句時,我們啟動一個計時器(但僅當表示式中存在 after 部分時)。

  • 獲取郵箱中的第一條訊息,並嘗試將其與 Pattern1、Pattern2 等進行匹配。如果匹配成功,則將訊息從郵箱中刪除,並評估模式後面的表示式。

  • 如果 receive 語句中沒有任何模式與郵箱中的第一條訊息匹配,則將第一條訊息從郵箱中刪除並放入“儲存佇列”中。然後嘗試郵箱中的第二條訊息。重複此過程,直到找到匹配的訊息或檢查郵箱中的所有訊息。

  • 如果郵箱中的任何訊息都不匹配,則程序將被掛起,並在下次將新訊息放入郵箱時重新安排執行。請注意,當收到新訊息時,不會重新匹配儲存佇列中的訊息;只會匹配新訊息。

  • 只要訊息已匹配,則將所有已放入儲存佇列的訊息按到達程序的順序重新輸入郵箱中。如果設定了計時器,則將其清除。

  • 如果我們在等待訊息時計時器到期,則評估表示式 ExpressionsTimeout 並將任何儲存的訊息按到達程序的順序放回郵箱中。

Erlang - 效能

在談論效能時,需要注意以下關於 Erlang 的幾點。

  • Fun 非常快 − Fun 在 R6B 中獲得了自己的資料型別,並在 R7B 中得到了進一步最佳化。

  • 使用 ++ 運算子 − 此運算子需要以正確的方式使用。以下示例是不正確使用 ++ 操作的方法。

示例

-module(helloworld). 
-export([start/0]). 

start()->
   fun_reverse([H|T]) ->
   fun_reverse(T)++[H]; 
   fun_reverse([]) ->
   [].

由於 ++ 運算子複製其左運算元,因此結果被重複複製,導致二次複雜度。

  • 字串的使用 − 如果處理不當,字串處理可能會很慢。在 Erlang 中,您需要更多地考慮字串的使用方式並選擇合適的表示方式。如果使用正則表示式,請使用 STDLIB 中的 re 模組,而不是已廢棄的 regexp 模組

  • BEAM 是一個基於棧的位元組碼虛擬機器 − BEAM 是一個基於暫存器的虛擬機器。它有 1024 個虛擬暫存器,用於儲存臨時值以及在呼叫函式時傳遞引數。需要在函式呼叫後仍然存在的變數將儲存到棧中。BEAM 是一個執行緒程式碼直譯器。每個指令都是指向可執行 C 程式碼的字,使指令分派非常快。

Erlang - 驅動程式

有時我們希望在 Erlang 執行時系統內執行外語程式。在這種情況下,程式被編寫為共享庫,並動態連結到 Erlang 執行時系統中。連結的驅動程式對程式設計師來說就像一個埠程式,並遵循與埠程式完全相同的協議。

建立驅動程式

建立連結的驅動程式是將外語程式碼與 Erlang 介面最有效的方式,但它也是最危險的方式。連結的驅動程式中的任何致命錯誤都將導致 Erlang 系統崩潰。

以下是 Erlang 中驅動程式實現的示例 −

示例

-module(helloworld). 
-export([start/0, stop/0]). 
-export([twice/1, sum/2]). 

start() ->
   start("example1_drv" ). 
start(SharedLib) ->
   case erl_ddll:load_driver("." , SharedLib) of 
   ok -> ok; 
      {error, already_loaded} -> ok; 
      _ -> exit({error, could_not_load_driver}) 
   end, 
   
   spawn(fun() -> init(SharedLib) end). 

init(SharedLib) -> 
   register(example1_lid, self()), 
   Port = open_port({spawn, SharedLib}, []), 
   loop(Port). 

stop() -> 
   example1_lid ! stop. 

twice(X) -> call_port({twice, X}). 
sum(X,Y) -> call_port({sum, X, Y}). call_port(Msg) -> 
   example1_lid ! {call, self(), Msg}, receive 
      {example1_lid, Result} -> 
      Result 
   end. 

LINKED-IN DRIVERS 223 
loop(Port) -> 
receive 
   {call, Caller, Msg} -> 
   Port ! {self(), {command, encode(Msg)}}, receive 
   {Port, {data, Data}} ->
   Caller ! {example1_lid, decode(Data)} 
   end, 

loop(Port); 
stop -> Port ! 
   {self(), close}, 
   receive 
      {Port, closed} -> 
      exit(normal) 
   end; 
   
      {'EXIT', Port, Reason} -> 
      io:format("~p ~n" , [Reason]), 
      exit(port_terminated) 
   end. 

encode({twice, X}) -> [1, X]; 
encode({sum, X, Y}) -> [2, X, Y]. decode([Int]) -> Int.

請注意,使用驅動程式非常複雜,在使用驅動程式時應小心謹慎。

Erlang - Web程式設計

在 Erlang 中,可以使用inets 庫來構建 Erlang 中的 Web 伺服器。讓我們看看 Erlang 中可用於 Web 程式設計的一些函式。可以實現 HTTP 伺服器(也稱為 httpd)來處理 HTTP 請求。

該伺服器實現了許多功能,例如:

  • 安全套接字層 (SSL)
  • Erlang 指令碼介面 (ESI)
  • 公共閘道器介面 (CGI)
  • 使用者身份驗證(使用 Mnesia、Dets 或純文字資料庫)
  • 通用日誌檔案格式(帶或不帶 disk_log(3) 支援)
  • URL 別名
  • 動作對映
  • 目錄列表

第一步是透過命令啟動 Web 庫。

inets:start()

下一步是實現 inets 庫的啟動函式,以便可以實現 Web 伺服器。

以下是建立 Erlang 中 Web 伺服器程序的示例。

例如

-module(helloworld). 
-export([start/0]). 

start() ->
   inets:start(), 
   Pid = inets:start(httpd, [{port, 8081}, {server_name,"httpd_test"}, 
   {server_root,"D://tmp"},{document_root,"D://tmp/htdocs"},
   {bind_address, "localhost"}]), io:fwrite("~p",[Pid]).

關於上述程式,需要注意以下幾點。

  • 埠號必須唯一,並且不能被任何其他程式使用。httpd 服務將在此埠號上啟動。

  • server_rootdocument_root 是必需的引數。

輸出

以下是上述程式的輸出。

{ok,<0.42.0>}

要在 Erlang 中實現一個Hello world Web 伺服器,請執行以下步驟:

步驟 1 − 實現以下程式碼 −

-module(helloworld). 
-export([start/0,service/3]). 

start() ->
   inets:start(httpd, [ 
      {modules, [ 
         mod_alias, 
         mod_auth, 
         mod_esi, 
         mod_actions, 
         mod_cgi, 
         mod_dir,
         mod_get, 
         mod_head, 
         mod_log, 
         mod_disk_log 
      ]}, 
      
      {port,8081}, 
      {server_name,"helloworld"}, 
      {server_root,"D://tmp"}, 
      {document_root,"D://tmp/htdocs"}, 
      {erl_script_alias, {"/erl", [helloworld]}}, 
      {error_log, "error.log"}, 
      {security_log, "security.log"}, 
      {transfer_log, "transfer.log"}, 
      
      {mime_types,[ 
         {"html","text/html"}, {"css","text/css"}, {"js","application/x-javascript"} ]} 
   ]). 
         
service(SessionID, _Env, _Input) -> mod_esi:deliver(SessionID, [ 
   "Content-Type: text/html\r\n\r\n", "<html><body>Hello, World!</body></html>" ]).

步驟 2 − 按如下方式執行程式碼。編譯上述檔案,然後在erl中執行以下命令。

c(helloworld).

您將獲得以下輸出。

{ok,helloworld}

下一個命令是:

inets:start().

您將獲得以下輸出。

ok

下一個命令是:

helloworld:start().

您將獲得以下輸出。

{ok,<0.50.0>}

步驟 3 − 您現在可以訪問 url - https://:8081/erl/hello_world:service

廣告