Erlang 学习之第三天 . 函数,模块,递归,数字,字符串

Erlang 函数

Erlang是一种众所周知的函数式编程语言,因此您将看到许多关于函数如何在Erlang中工作的重点。本章介绍如何使用Erlang中的函数完成所有操作。

直接上实例: 定义函数

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

直接上实例: 匿名函数  匿名函数是一个函数,但它没有与任何名称相关联。在 Erlang 中有工具可以用来定义匿名函数。以下的程序是一个匿名函数的一个实例。

start() -> 
   Fn = fun() -> 
      io:fwrite("Anonymous Function") end, 
   Fn().
结果打印为 Anonymous Function


函数使用序列 在 Erlang 中函数也都有保护序列的能力。这些都不算什么它只不过是一个表达式,只有当评估(计算)为 true 时函数才运行。
实例:
add(X) when X>3 ->     io:fwrite("~w~n",[X]).  start() ->     add(4).

上面的代码的输出结果是  4
如果 add 函数被调用为 add(3),该程序将会出现错误。
 

Erlang 模块

模块是在单个文件中以单个名称重新组合的一组函数。此外,Erlang中的所有函数都必须在模块中定义。模块中定义的每个其他函数都需要使用 Module: Function (Arguments) 的形式来调用。

Erlang 递归

递归是 Erlang 的重要组成部分。首先,让我们看看如何通过实现 factorial 程序来实现简单的递归。

普通递归语法实例1:

-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]).    输出结果是  24   4*3*2*1  

在这个示例中,fac/1函数计算了输入参数为4的阶乘,即4!。阶乘的计算规则是将给定的整数与比它小的所有正整数相乘,直到1为止。

  1. N等于4时,fac/1函数首先判断N不等于0,然后根据第二个定义分支,将4乘以fac(N-1)。这里N-1是3。
  2. 然后,函数继续递归调用fac/1函数来计算3的阶乘。在计算3的阶乘时,函数会将3乘以fac(N-1),即2。
  3. 接着,函数继续递归调用fac/1函数来计算2的阶乘。在计算2的阶乘时,函数会将2乘以fac(N-1),即1。
  4. 然后,函数继续递归调用fac/1函数来计算1的阶乘。在计算1的阶乘时,函数会将1乘以fac(N-1),即0。
  5. 最后,当N等于0时,递归调用结束,函数返回1。

根据阶乘的计算规则,4! = 4 × 3 × 2 × 1 = 24。因此,最终的结果是24。

普通递归语法实例2:

-module(recursion_demo).
-export([sum/1, test_normal/0]).

 

% 普通递归求列表的和
sum([]) -> 0;
  sum([H|T]) -> H + sum(T).

% 测试普通递归
test_normal() ->
  List = [1, 2, 3, 4, 5],
  Result = sum(List),
  io:format("Sum of the list: ~p~n", [Result]).

根据相加的计算规则,5+4+3+2+1  结果是15

 

尾递归语法实例:

-module(tail_recursion_demo).
-export([sum/1, test_tail/0]).

 

% 尾递归求列表的和
sum(List) -> sum(List, 0).

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

% 测试尾递归
test_tail() ->
  List = [1, 2, 3, 4, 5],
  Result = sum(List),
  io:format("Sum of the list: ~p~n", [Result]).

根据相加的计算规则,5+4+3+2+1  结果是15

 

优缺点说明:

普通递归和尾递归在Erlang中的实现方式略有不同,以下是它们的语法和优缺点:

  1. 普通递归:

    • 语法:普通递归是通过函数调用自身来实现的。在函数体内部,需要有一个递归终止条件,以防止无限递归。
    • 优点:
      • 实现简单,易于理解。
      • 适用于大多数递归场景。
    • 缺点:
      • 对于大规模数据或者递归层次较深的情况,可能会导致栈溢出。
      • 每次递归调用都需要保留函数调用栈,消耗内存较大。
  2. 尾递归:

    • 语法:尾递归是一种特殊的递归形式,其递归调用是函数体的最后一个操作,并且这个调用的返回值直接返回给函数的调用者。尾递归调用不会在函数返回之前执行其他操作。
    • 优点:
      • 在Erlang中,尾递归可以被优化为迭代,不会导致栈溢出,因此更适合处理大规模数据或递归层次深的情况。
      • 函数调用的开销较小,节省内存空间。
    • 缺点:
      • 有时候不太直观,对于一些复杂的逻辑可能需要重新设计以使用尾递归。

         尾递归在Erlang中是一种重要的优化手段,可以有效避免普通递归中可能出现的栈溢出问题,提高程序的稳定性和性能。

尾递归函数定义:

  • 尾递归函数的定义需要满足两个条件:函数体内部的递归调用是函数体的最后一个操作,并且递归调用的返回值直接返回给函数的调用者。
  • 定义格式:
            • func_name(Parameters, Accumulator) ->
                % 递归终止条件
                if
                  Termination_condition -> Accumulator;
                  true -> func_name(NewParameters, NewAccumulator) % 尾递归调用
              end.

 尾递归的调用: 1,在尾递归函数中, 递归调用通常是 在 true 分支的 if 表达式中进行的 

         2,尾递归调用的返回值直接返回给函数的调用者,而不是要进行其他的操作,或者对返回的值进行进一步处理

参数传递: 1,尾递归函数通常使用一个累加器 (Acc) 参数来保存中间的结果,每次递归调用时,都会跟新累加器的值.

       2,参数的传递,通常需要包含原始参数 以及累加器的参数, 这样在每次的递归调用时 都可以传递跟新后的参数.

-module(tail_recursion).
-export([length/1, start/0]).

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

start() ->
  List = [1, 2, 3, 4, 5],
  Size = length(List),
  io:format("Length of the list: ~w~n", [Size]).

 

Erlang 数字

eralng中  数字有两种类型,  整数,浮点数

Erlang 字符串

通过将字符串文本括在引号中,可以在Erlang中构造一个字符串文字。需要使用双引号(例如“ Hello World”)构造Erlang中的字符串。





























posted @ 2024-02-05 11:23  d-w  阅读(72)  评论(0编辑  收藏  举报