天天看點

轉載:【原譯】Erlang清單處理(Efficiency Guide)List handling1  Creating a list建立一個清單2  List comprehensions清單解析3  Deep and flat lists深度扁平清單4  Why you should not worry about recursive lists functions你不需要擔心遞歸清單函數的原因

轉自:http://www.cnblogs.com/futuredo/archive/2012/10/22/2734186.html

Lists can only be built starting from the end and attaching list elements at the beginning. If you use the ++ operator like this

清單隻能從尾端開始建立,從頭部加入元素。如果你像這樣使用++操作符

you will create a new list which is copy of the elements in List1, followed by List2. Looking at how lists:append/1 or ++would be implemented in plain Erlang, it can be seen clearly that the first list is copied:

你将建立一個新的清單,這個清單是List1的副本加上List2。看看lists:append/1函數或者++操作符在Erlang裡面是怎樣實作的,可以明了地看到第一個清單被複制了:(%% 取一個輸入清單的元素,加到新的清單中 %%)

So the important thing when recursing and building a list is to make sure that you attach the new elements to the beginning of the list, so that you build a list, and not hundreds or thousands of copies of the growing result list.

是以在遞歸周遊和構造一個清單的時候,要確定把新的元素附加到清單的頭部,這樣就能避免産生成百上千的清單的副本。

Let us first look at how it should not be done:

讓我們首先來看看不應該使用的方法:

DO NOT

Here we are not a building a list; in each iteration step we create a new list that is one element longer than the new previous list.

這裡我們并不是在建立一個清單;每一次疊代中,我們都産生了一個新的清單,這個新的清單比上次産生的清單還多一個元素。

To avoid copying the result in each iteration, we must build the list in reverse order and reverse the list when we are done:

為了避免在每次疊代中複制最後的結果,我們必須以相反順序來構造清單,然後在結束的時候反轉清單:

DO

Lists comprehensions still have a reputation for being slow. They used to be implemented using funs, which used to be slow.

清單解析仍然被認為是效率慢的。它們以前是使用函數來實作的,而函數過去也是效率慢的。

In recent Erlang/OTP releases (including R12B), a list comprehension

在最近的Erlang/OTP版本中(包括R12B),清單解析

is basically translated to a local function

被轉換成一個本地函數

In R12B, if the result of the list comprehension will obviously not be used, a list will not be constructed. For instance, in this code

在R12B版本中,如果清單解析的結果明确表示出将不會被使用的,那麼清單将不會被構造出來。例如,在下面這段代碼中

or in this code

或者下面這段代碼

the value is neither assigned to a variable, nor passed to another function, nor returned, so there is no need to construct a list and the compiler will simplify the code for the list comprehension to

清單裡面的元素既不會指派給一個變量,也不會傳給另外一個函數,或者直接傳回,是以這種情況下是沒有必要構造一個清單的,編譯器會簡單地把清單解析的代碼簡化成下面的形式(%% 一是沒有了清單結構,二是用了尾遞歸 %%)

lists:flatten/1 builds an entirely new list. Therefore, it is expensive, and even more expensive than the ++ (which copies its left argument, but not its right argument).

lists:flatten/1函數會構造一個完全嶄新的清單。是以,它的開銷很大,甚至比++操作符的都要來的大(++操作符隻複制它的左值,不複制右值)。

(%% flatten的效果就是把有深度的清單,如 [[1], [2], [3]] ,轉換成[1, 2, 3],會複制原有清單中所有的元素,如果沒了解錯誤的話 %%)

In the following situations, you can easily avoid calling lists:flatten/1:

When sending data to a port. Ports understand deep lists so there is no reason to flatten the list before sending it to the port.

When calling BIFs that accept deep lists, such as list_to_binary/1 or iolist_to_binary/1.

When you know that your list is only one level deep, you can can use lists:append/1.

下面幾種情況中,你可以容易地避免去調用lists:flatten/1函數:

發送資料到一個端口的時候。端口知道(%% 或者說能夠解析 %%)deep list(深清單),是以在把清單發送到端口之前不需要把它扁平化。

調用像list_to_binary/1或者iolist_to_binary/1等這樣的接收deep lists(深清單)的内置函數的時候。

你知道清單隻有一級深度的時候,可以使用list:append/1函數。(%% one level deep 應該是類似于[[1], [2], [3]]這種,隻需使用append函數來扁平化 %%)

Port example

端口樣例

A common way to send a zero-terminated string to a port is the following:

下面是一種常見的(但不推薦的)把0結尾字元串發送給一個端口的方式:

(%% 兩種附加元素的方式的對比,最後結果的深度不一樣 %%)

Instead do like this:

應該像這樣來做:

Append example

附加樣例

In the performance myth chapter, the following myth was exposed: Tail-recursive functions are MUCH faster than recursive functions.

在性能誤區那一章,講到了這個誤區:尾遞歸函數比普通遞歸函數更高效。

To summarize, in R12B there is usually not much difference between a body-recursive list function and tail-recursive function that reverses the list at the end. Therefore, concentrate on writing beautiful code and forget about the performance of your list functions. In the time-critical parts of your code (and only there), measure before rewriting your code.

總結一下,在R12B版本中,體遞歸函數和尾遞歸函數,同樣在結束時反轉清單,通常是沒有多大差別的。是以,你隻需要集中精力書寫優美的代碼,不用去擔心清單函數的性能。隻有在時間性能極其重要的那部分代碼裡,你需要在重寫前掂量一下。

Important note: This section talks about lists functions that construct lists. A tail-recursive function that does not construct a list runs in constant space, while the corresponding body-recursive function uses stack space proportional to the length of the list. For instance, a function that sums a list of integers, should not be written like this

重點:這一節講到了構造清單的函數。不構造清單的尾遞歸函數隻需常量空間,相反,體遞歸函數占用的棧空間跟清單的長度成正比。例如,一個計算整數清單元素總和的函數,不應該寫成這樣:

but like this

應該這樣

繼續閱讀