Erlang 程式設計/遞迴技巧
我們經常希望使用遞迴函式構建列表。也許我們想反轉一個列表。我們從單引數版本(公共入口點函式)開始,並使用它來呼叫雙引數版本(私有),其中額外的引數包含我們想要構建的輸出。我們可以從輸入中取出頭部,[H|T],在我們構建輸出的同時,構建。當輸入為空時,我們完成了。
觀察字串實際上是如何作為字元列表處理的,這很不錯。語法糖將字串轉換為列表並返回。
(這裡我們使用遞迴的組裝-拆卸技巧。Johnny-Five 不會贊成這種技巧。[參考 1986 年電影“短路”。])
記住,我們需要用括號括住 H,因為它需要在使用加號加號“++”進行列表連線時是一個列表。T 在 [H|T] 中始終是一個列表。H 在 [H|T] 中可能是也可能不是一個列表;無論哪種方式,我們都需要給它一個括號,使其正常工作。
%%% provides utility functions
%
-module(util).
-export([reverse/1]).
%% reverse(L) is for public use
%% RETURNS: the reverse of a list L
%% ARG: L <== any list
reverse( L ) -> %% The entry point function: reverse(L)
reverse( L, []). %% the private function: reverse(L1, L2)
%%
%% reverse() uses the assembly-disassembly method
%% of recursion.
%% reverse(L1,L2) is the private version of reverse
reverse([], Build) -> %% The base case: has an empty list for input so
Build; %% we're done building and return the final answer: Build
reverse([H|T], Build) -> %% The recursive case: has at least one element: H
reverse( T, [H] ++ Build ). %% so glue it onto Build and
%% recurse with the left-overs: T
% sample output
%
c(util).
{ok,util}
%
util:reverse("abc").
"cba"
%
util:reverse([1,2,3]).
[3,2,1]
%
util:reverse( [ [1,1], [2,2], [3,3]]).
[ [3,3], [2,2], [1,1]]
%
util:reverse([ {1,1}, {2,2}, {3,3} ]).
[ {3,3}, {2,2}, {1,1} ]
%
util:reverse( { [1,1], [2,2], [3,3] } ).
error
編譯並執行。
我們可以反轉字串,它實際上是一個列表。我們可以反轉一個整數列表。我們可以反轉一個列表列表。我們可以反轉一個元組列表。但我們不能反轉一個元組列表,因為頂層結構不是列表。
如果我們想變得聰明一些,我們可以在入口點使用一個守衛來檢查引數的型別,然後如果它是一個元組,將其更改為一個列表,執行反轉,然後在輸出時將其更改回一個元組。
請注意,這個例子純粹是教育性的,在實際應用中,您應該避免一次將一個元素追加到列表中。構建列表的正確方法是使用 [Head|Tail] 形式。這通常會導致列表以相反的順序出現。為了解決這個問題,我們隨後使用 lists:reverse 內建函式。參見示例
%slow build_append()->build_append(0,[]). build_append(N,L) when N < 100000 -> build_append(N+1,L++[N]); % function calls its self with N+1 and a new list with N as the last element. build_append(_,L) -> L.
%fast build_insert()->build_insert(0,[]). build_insert(N,L) when N < 100000 -> build_insert(N+1,[N|L]); % function calls its self with N+1 and a new list with N as the head. build_insert(_,L) -> lists:reverse(L). % function has to reverse the list before it retuns it.
編譯並執行這些函式進行比較。
速度差異背後的原因在於 Erlang 列表作為連結串列的實現。
1) 編寫一個函式 t_reverse(T),它反轉一個大小為 5 的元組 T。(即 {a,b,c,d,e})
2) 編寫一個函式 r_reverse(L),它遞迴地反轉列表列表 L 中的每個子列表。
3) 編寫一個函式 s_reverse(L),它遞迴地反轉子列表,但不反轉列表列表的頂層。
4) 編寫一個函式 n_reverse(L),它遞迴地反轉子列表,但前提是它們包含整數。