Erlang语言基础学习笔记

6.3k words

Erlang语言基础学习笔记

常用的一些语法

API类(及其实现)

%%%-------------------------------------------------------------------
%%% lists:max()
max([H|T]) -> max(T, H).
max([H|T], Max) when H > Max -> max(T, H);
max([_|T], Max)              -> max(T, Max);
max([],    Max)              -> Max.
%%%-------------------------------------------------------------------
%%% lists:min()
min([H|T]) -> min(T, H).
min([H|T], Min) when H < Min -> min(T, H);
min([_|T], Min)              -> min(T, Min);
min([],    Min)              -> Min. 
%%%-------------------------------------------------------------------
%%% lists:sum()
sum(L)          -> sum(L, 0).
sum([H|T], Sum) -> sum(T, Sum + H);
sum([], Sum)    -> Sum.
%%%-------------------------------------------------------------------
%%% lists:reverse()
reverse(List) ->
    reverse(List, []).
reverse([], Acc) ->
    Acc;
reverse([Head | Tail], Acc) ->
    reverse(Tail, [Head | Acc]).
%%%-------------------------------------------------------------------
%%% lists:member(Elem, List) 存在返回true,不存在返回false
member(_, []) -> false;
member(Element, [Element | _]) -> true;
member(Element, [_ | Tail]) -> member(Element, Tail).
%%%-------------------------------------------------------------------
%%% ++
concatenate_lists(List1, List2) ->
    case List1 of
        [] ->
            List2;
        [Head | Tail] ->
            [Head | concatenate_lists(Tail, List2)]
    end.
%%%-------------------------------------------------------------------

算术表达式

操作符 描述 参数类型
+ X + X 数字
- X - X 数字
X * Y X * Y 数字
X / Y X / Y (浮点除法) 数字
X div Y X被Y整除 整数
X rem Y X除以Y的整数余数 整数

练习题总结

元组,列表相关练习题

遍历方式与累加器

对于列表的遍历,用尾递归,结合累加器,进行数据存储,示例:

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

其中,H是列表的头元素,T是H以后的列表,[H | Acc]为通过头插的形式把H插入到Acc头部。

终止遍历条件

要终止对列表的遍历非常简单:

test([], Acc) ->
    Acc;
%% 以下为遍历
test([H | T], Acc) ->
    test(T, [H | Acc]).

这里的意思是,当传入的第一个参数,列表为空时,输出Acc。

简化调用API

继续上面,我们的累加器是自己设置的,实际用户调用的时候不需要设置Acc,所有调用接口可以简化:

-export([test/1]).

test(List) ->
    test(List, []).

test([], Acc) ->
    lists:reverse(Acc);
%% 以下为遍历
test([H | T], Acc) ->
    test(T, [H | Acc]).

一个简单的遍历列表并把每个元素加到累加器中的功能就写好了,如果需要的话可以用lists:reverse()将Acc进行反转。

索引的控制

一般情况下,在调用函数的时候就进行索引的比较与控制:

$$$ 计算数字列表或元组索引N到M的和

%% API
-export([get_sum/3]).

get_sum(List, N, M) ->
    get_sum(List, N, M, 1, []).

get_sum([], _, _, _, Acc) ->
    lists:sum(Acc);
get_sum([H | T], N, M, Index, Acc) when (Index =< M) and (Index >= N)->
    get_sum(T, N, M, Index + 1, [H | Acc]);
get_sum([_ | T], N, M, Index, Acc) ->
    get_sum(T, N, M, Index + 1, Acc).

其中,我们在调用get_sum函数的时候,就对要传入其中的Index参数进行了控制,并用关卡机制对Index进行了检查,使其能够完成题目的要求。

关于索引控制的经典例题:

%%% 替换元组或列表中指定位置的元素,新元素作为参数和列表或元组一起传入函数内

%% API
-export([replaceIdx/3]).

replaceIdx(List, Index, Val) ->
    replaceIdx(List, Index, Val, 1, []).

%% 列表空后反转累加器。
replaceIdx([], _, _, _, Acc) ->
    lists:reverse(Acc);

%% 到达替换位置的处理:把Val放到累计器的头部
replaceIdx([_ | Rest], Index, Val, Index, Acc) ->
%%    io:format("[~p]~n", [[Val | Acc]]),
    replaceIdx(Rest, Index, Val, Index + 1, [Val | Acc]);

%% 未到达指定位置:把头元素放到累加器的头部。
replaceIdx([Element | Rest], Index, Val, CurrentIndex, Acc) ->
%%    io:format("[~p]~n", [[Element | Acc]]),
    replaceIdx(Rest, Index, Val, CurrentIndex + 1, [Element | Acc]).

case与if表达式

%%% 对数字列表中的奇数进行求和,后除以3的商得值为A,并将偶数求和后除以3的余数得值为B,然后求A+B结果,输出A、B、A+B

%% API
-export([get_res/1]).

%% 求和
get_sum([]) -> 0;
get_sum([H | T]) -> H + get_sum(T).

%% 求偶数和
get_even_sum([], Acc) -> Acc;
get_even_sum([H | T], Acc) ->
    %% 这里的case用来做一种类似if的判断,如果`H rem 2 = 0`说明是偶数,就把H加到累加器中。
    case H rem 2 of
        0 -> get_even_sum(T, Acc + H);
        _ -> get_even_sum(T, Acc)
    end.

%% 求值
get_res([H | T]) ->
    A = get_sum([H | T]) div 3,
    B = q5:get_even_sum([H | T], 0) rem 3,
    {A, B, A + B}.
%%% 查找元素在元组或列表中的位置

%% API
-export([get_index/3]).

get_index([], _, _) -> 0; %% 不会用到的参数用_传入即可。
get_index([H | T], N, Index) when (Index =< T) ->
    if
        (H == N) ->
            Index;
        true ->
            get_index(T, N, Index + 1)
    end.
%%% 移除元组或列表中指定位置的元素

%% API
-export([remove/2]).

remove(List, Position) ->
    case Position of
        1 ->
            [H | T] = List,
            T;
        N when N > 1 ->
            [H | T] = List,
            [H | remove(T, N - 1)]
    end.

列表推导

[ F(X) || X <- L]标记的意思是“由F(X)组成的列表(X从列表L中提取)”。

%%% 对数字列表或元组,输出所有偶数乘以它在此列表或元组中的偶数位数

%% API
-export([evens_mul/1]).

evens_mul(List) ->
    Even = evens(List, [], 1),
    [H * Idx || {H, Idx} <- Even].

evens([], Even, _) ->
    Even;
%% 先拿到偶数与坐标
evens([H | T], Even, Idx) ->
    if
        H rem 2 == 0 ->
            evens(T, [{H, Idx} | Even], Idx + 1);
        true ->
            evens(T, Even, Idx)
    end.

冒泡排序

%%% 使用冒泡排序对列表进行排序

%% API
-export([bubble_sort/1]).

%% 取列表头作为最大值和
bubble_sort(List) ->
    bubble_sort(List, length(List)).

bubble_sort(List, 0) -> List; % 当迭代次数为 0 时,排序完成
bubble_sort(List, N) ->
    SortedList = bubble_pass(List, N), % 对列表进行下一趟冒泡,一个元素到达最终位置
    bubble_sort(SortedList, N - 1). % 递归的进行下一趟冒泡

bubble_pass([X, Y | Rest], N) when X > Y ->
    [Y | bubble_pass([X | Rest], N - 1)]; % 如果 X > Y 就交换他们
bubble_pass([X | Rest], N) ->
    [X | bubble_pass(Rest, N - 1)]; % 否则位置保持不变
bubble_pass([], _) -> [].

列表更方便的匹配方式

%%% 对一个字符串按指定符字劈分

%% API
-export([split/2]).

split(String, [C | _]) ->
    split_head(String, C, []).
split_tail([], _, RAcc) ->
    lists:reverse(RAcc);
split_tail([H | T], Seps, RAcc) ->
    split_tail(T, Seps, [H | RAcc]).
split_head([Seps | T], Seps, FAcc) ->
    [lists:reverse(FAcc), split_tail(T, Seps, [])];
split_head([H | T], Seps, FAcc) ->
    split_head(T, Seps, [H | FAcc]).

其中split_head([Seps | T], Seps, FAcc)为在传参,条件判断的时候就进行了匹配,更方便好用。

列表的链式反应

%%% 对相同类型的数据进行拼接

%% API
-export([merge_lists/2, merge_tuples/2, merge_binary/2]).

%% 列表拼接
merge_lists([], List2) -> List2;
merge_lists([Head | Tail], List2) ->
    [Head | merge_lists(Tail, List2)].

%% 元组拼接
merge_tuples(Tuple1, Tuple2) ->
    TupleList1 = tuple_to_list(Tuple1),
    TupleList2 = tuple_to_list(Tuple2),
    Tuples = merge_lists(TupleList1, TupleList2),
    list_to_tuple(Tuples).

%% 二进制拼接
merge_binary(Binary1, Binary2) ->
    BinaryList1 = binary_to_list(Binary1),
    BinaryList2 = binary_to_list(Binary2),
    BinaryList = merge_lists(BinaryList1, BinaryList2),
    list_to_binary(BinaryList).

[Head | merge_lists(Tail, List2)]为链式反应,不断把每次递归的头结点依次塞到该列表内。

字符串相关练习题

回文题

%%% 检查一个字符串是否是回文

%% API
-export([check/1]).

check(String) ->
    Middle = (length(String) div 2) + (length(String) rem 2),
    split_string(String, Middle, 1, []).
split_string([_ | T], Index, Index, Acc) ->
    check_equal(lists:reverse(Acc), split_string_right(T, []));
split_string([H | T], Index, CIndex, Acc) ->
    split_string(T, Index, CIndex + 1, [H | Acc]).
split_string_right([], Acc) ->
    Acc;
split_string_right([H | T], Acc) ->
    split_string_right(T, [H | Acc]).
check_equal(List, List) ->
    true;
check_equal(_, _) ->
    false.

多层嵌套

%%% 查找一个字符串中的最长单词

%% API
-export([check_max_length/1]).

%% 经过测试,空格是32
check_max_length(String) ->
    check_max_length(String, 32, 0, 0).
check_max_length([], _, Length, Max) ->
    check_max(Length, Max);
check_max_length([Seps | T], Seps, Length, Max) ->
    check_max_length(T, Seps, 0, check_max(Length, Max));
check_max_length([_ | T], Seps, Length, Max) ->
    check_max_length(T, Seps, Length + 1, Max).
check_max(Length1, Length2) ->
    if
        Length1 > Length2 ->
            Length1;
        true ->
            Length2
    end.
Comments