Erlang学习笔记(四)模块与函数

买的《Erlang程序设计》第二版终于到了,可以按照书中的章节记录学习内容了。

1.模块

模块保存在扩展名为.erl的文件里,必须先编译再运行,编译后的模块以.beam作为扩展名。
-export([area/1])的意思是带有一个参数的函数area可以在此模块之外调用。
-import(lists, [map/2])的意思是map/2函数是从lists模块里导入的,这就意味着我们可以用map(Fun, ….)来代替lists:map的写法了。

a. 逗号","分隔函数调用、数据结构和模式中的参数。
b. 分号";"分隔子句,例如在Case、If的使用中。
c. 句号“.”(后接空白)分隔函数整体,以及shell里的表达式。

2.fun:基本的抽象单元

Erlang是一种函数式编程语言。此外,函数式编程语言还表示函数可以被用作其他函数的参数,也可以作为返回函数。操作其他函数的函数被称为高阶函数而在Erlang中用于代表函数的数据类型被称为fun。
fun既可以作为函数也可以作为函数的参数被传递。
作为函数样例:

 Double = fun(X) -> 2*X end.

fun作为函数的参数样例:

L = [1,2,3,4].
lists:map(fun(x) -> 2*X end, L).

定义控制抽象样例

-module(test_for).
-export([for/3]).

for(Max, Max, F) -> [F(Max)];%这里表示当Max相同时将F(Max)的值加入到for中,这里类似于C语言里面的递归出口
for(I, Max, F)  -> [F(I)|for(I+1, Max, F)].%当I与Max不同时,将I与I以后分离
%简单的来说就是先把1拿出来,通过第一行代码加入到F中,接着第二行代码把2拿出来,再通过第一行代码加入F中

这里需要注意的是Erlang语言是函数式编程,它的变量由于一旦绑定就无法更改所以没有中间变量,所以像C++中的for(int i = 0; i < 10; i++)这种循环他是不存在的。它是通过递归调用的方式来实现循环的。在这个地方我曾纠结在不停的递归时I的值是否被重新赋予,是不是违反Erlang语言的语法规则?其实并不是,因为在递归调用的过程中,数据是存储在一个递归栈之中的。所以每层递归数所存储的位置是不同的,而在Erlang中,因为地址被绑定的原因导致了无法赋予新的值,在这里看来是不矛盾的,故可以实现递归调用。
可以举个C的例子来帮助理解:
Erlang学习笔记(四)模块与函数_第1张图片

在这里这个样例就是对上述过程的一个类比分析,在这个直观的C程序中,并没有出现i++,这种情况。这是单纯的通过递归调用的方式来实现1-10的求和这个过程。

3.简单列表处理

在列表处理的过程中,我认为将列表分隔开为列表头部与其他是非常重要的,因为Erlang是函数式的编程所以会导致的结果循环就要是要使用递归调用,这时候的拆分就可以很好地将列表“切开”,实现他的分别用,一般就是先处理头部,然后在进行拼接,或者一直递归到最后一层再实现与前面的拼接。
注意的是,递归调用的出口。有时会用一个空的列表来表示递归的出口,就是类似于C++中的递归的最后一层下界。
列表处理简答例子如下:

-module(test_sum).
-export([sum/1]).

sum([L|M]) -> L + sum(M);
sum([]) -> 0.

4.列表推导

列表推导是无需使用fun、map、filter就能实现创建列表的表达式。他可以是程序变的更短,更容易理解。
我感觉对于一个初学者来说,还是很不友好的啊,乱七八糟的指看着都别扭。不过列表推导是真的好用,可以节省好多代码量,而且也很形象,感觉开发这门语言的大佬是真的随意啊。
先举个栗子:

L = [1,2,3,4,5].
lists:map(fun(X)) -> 2*X end, L).
[2*X || X <- L].%作用效果与第一行相同。

再举个栗子:

-module(test_qsort).
-export([qsort/1]).%实现排序,过程是先把第一个拿出来然后比它小的在左边,大于等于的在右边,写起来比C语言简洁的太多了啊。

qsort([]) ->[];
qsort([Pivot | T]) -> 
                    qsort([X || X <- T,  X < Pivot])
                    ++[Pivot]++
                    qsort([X || X <- T, X >= Pivot]).

5.关卡

关卡(guard)是一种结构,可以用它来增加模式匹配的威力。使用关卡可以对某个模式里的变量执行简单的测试和比较。
关卡序列(guard sequence)是指单一或一系列的关卡,用”;”分隔,这是的”;”可以理解为逻辑“或”,所有的条件只要满足一个既可以通过关卡。”,”在关卡里面的意思是表示为逻辑“与”只有所有的条件都满足的时候才可以通过关卡。
来个栗子:

f(X, Y) when is_integer(X),  X > Y, Y < 6 -> X+Y. %","的样例
X =:= dog; X =:= cat
is_integer(X), X > Y; abs(Y) < 23 %混合情况的样例
....

6.归集器

程序遍历列表一次,把不同的参数放在合适的列表里,这些列表就被称为归集器。
举个栗子:

odd_and_evens1(L) ->
    Odds  = [X || X <- L, (X rem 2) =:= 1],
    Evens = [X || X <- L, (X rem 2) =:= 0],
    {Odd, Evens}.

在这个代码中,遍历了列表两次,当列表很长的时候就可能会出现问题,因此需要减少它的遍历次数。

-module(test_odds_and_evens).
-export([odds_and_evens/1]).

odds_and_evens(L) -> odds_and_evens_acc(L, [], []).

odds_and_evens_acc([H|T], Odds, Evens) ->
    case (H rem 2) of
        1 -> odds_and_evens_acc(T, [H|Odds], Evens);
        0 -> odds_and_evens_acc(T, Odds, [H|Evens])
    end;
odds_and_evens_acc([], Odds, Evens) -> {Odds, Evens}.

这个程序就实现了一次遍历所有的列表,但是也有个小问题,这里利用的时候类似头插法的方法,所以会导致数据的顺序跟之前相反了,需要利用lists中的reverse方法来实现逆转,这是内部函数。

7.部分习题解析

2.内置函数tuple_to_list(T)能将元组T里的元素转换成一个列表。轻编写一个名为:my_tuple_to_list(T)的函数来做同样的事情,但不要使用相同功能的内部的内置函数。

-module(test_my_tuple_to_list).
-export([my_tuplr_to_list/1]).

my_tuplr_to_list(T) -> 
    my_tuplr_to_list(T, tuple_size(T), 0, []).
my_tuplr_to_list(T, Index, F, L) when Index > F ->
    my_tuplr_to_list(T, Index-1, F, [element(Index, T) | L]);
my_tuplr_to_list(T, Index, F, L) when Index =:= F -> L.

7.向math_functions.erl添加一个返回{Even,Odd}的split(L)函数,其中Even是一个包含L里所有偶数的列表,Odd是一个包含L里所有奇数的列表。请使用两种不同的方式编写这个函数,一种使用归集器,另一种使用在练习6中编写的filter函数。

split(L) -> odds_and_evens_acc(L, [], []).

split([H|T], Odds, Evens) ->
    case (H rem 2) of
        1 -> split(T, [H|Odds], Evens);
        0 -> split(T, Odds, [H|Evens])
    end;
split([], Odds, Evens) -> {Odds, Evens}.
split(L) ->
    Odd  =  [X || X <- L, (X rem 2) =/= 0],
    Even =  [X || X <- L, (X rem 2) =:= 0],
    {Even, Odd}.

8.小结

刚开始接触函数式的编程,所以有一些懵,还好找到了我大腿鹏哥,鹏哥很耐心的给我讲解了一下如何理解函数式的编程。毕业多年还蒙受我鹏哥的照顾不胜感激啊。
现在说说我的理解,函数式编程是不使用中间变量的,他是通过函数的调用返回值来实现的,数据的入口是通过参数来解决的,循环是通过不停的调用来实现的,鹏哥给我举了个很形象的例子来理解,以求两个数的绝对值的差为样例来进行举例,如下:
C语言的编程方式:
Erlang学习笔记(四)模块与函数_第2张图片

在C语言中,我们求绝对值的差通过定义变量、比较变量、做运算,直接就可以得到结果。
函数式语言的实现方式:
Erlang学习笔记(四)模块与函数_第3张图片

没有中间变量的出现,而是各种函数的调用来实现这个功能,然后返回结果。不要纠结最后的int c那个定义只是为了好理解,在Eralng中就不会出现了。

你可能感兴趣的:(Erlang,学习笔记,erlang)