文章目录
- 递归
- 递归实现循环
- 列表和尾递归
- Enum模块
递归
由于在Elixir
中,变量是不可变的,从而类似i++
这种自增指令是没法实现的,也就没法进行循环。但作为一种编程语言来说,循环又是必不可少的一种流程控制手段,而在elixir
中,实现循环的第一方法,就是递归。
递归,就是调用自身的函数,下面举一个简单的计算阶乘的例子
# Test.ex
defmodule Test dodef fac(n) when n <= 1 do1enddef fac(n) don*fac(n-1)end
end
测试结果如下
> Test.fac(5)
120
递归实现循环
想用递归实现循环,则至少需要有一个用于终止循环的标记,例如,若想打印所有小玉N
的整数,用for
循环写一个伪代码就是
for(i=0; i<=N; i++) print(i)
而在elixir
中,若用递归来改写这个循环,则代码如下
# 在Test.ex的defmodule中添加下列内容
def printNum(i, n) doIO.puts iif i < n doprintNum(i+1, n)end
end
其运行结果为
iex(1)> Test.printNum(1,5)
1
2
3
4
5
nil
列表和尾递归
在介绍模式匹配时,也附带介绍了elixir
中的一种键值对数据结构,即关键字列表、表单,这是因为模式匹配是elixir
中最核心的一种编程理念。但对于普通程序员来说,列表显然要比关键字列表更为基础。
在elixir
中,列表的创建方式非常简单,只需用方括号框起来就可以,而且列表中不限定数据类型。
> lst = [1,2,"a","trump"]
[1, 2, "a", "trump"]
之所以在讲解循环的实现方式之后再着重介绍列表,是因为在某些语言中,是支持类似for ... in lst
这种循环的,换言之,列表是一种天然的适合迭代的数据结构。
针对列表,有一种特殊的迭代方法,即尾递归,下面通过这种思路,实现一个数组求和函数
# 在Test.ex中添加下列内容
def sumList(lst) dosumList(lst, 0)
end# 尾递归函数
def sumList([head|tail], sumVar) dosumList(tail, head + sumVar)
enddef sumList([], sumVar) dosumVar
end
调用结果为
> Test.sumList([1,2,3,4])
10
Enum模块
尽管尾递归用起来并不复杂,但和直接迭代列表相比,仍然是比较复杂的。故而elixir
提供了Enum模块,用于处理类似列表这种可枚举的数据结构,从而使得一行代码就可以进行列表求和
> Enum.sum([1,2,3,4])
10
> Enum.reduce([1,2,3,4], 0, &+/2)
10
Enum.sum
相当于是封装好的求和函数,自然没什么好说的,但reduce
却是另一种编程逻辑,相当于从0开始,逐次执行+
这一二元操作,直到列表被迭代完成。
和reduce
相比,map
可能更容易理解,相当于是把列表中的每个值抽取出来,然后逐个操作,并得到一个新的列表,下面是对列表中每个元素求平方
> Enum.map([1, 2, 3, 4], fn x -> x * 2 end)
[2, 4, 6, 8]
Enum
中提供了一系列用于可迭代变量的函数
函数 | 说明 |
---|---|
min /max /sum | 最小值;最大值;求和 |
all? /any? | 条件判断 |
each /map | 遍历 |
filter | 按条件过滤 |
reduce | |
sort /uniq | 排序;去除重复值 |
其中,each
和map
的区别,即前者并不产生新的值,后者产生新值,如果想打印列表中的所有值,就比较推荐Enum.each
函数。
filter, all?, any?
这三者所需要的函数,返回值应该都是布尔型,下面以erm(x,2)==0
作为条件,分别测试一下这三个函数
iex(7)> Enum.filter([1, 2, 3, 4], fn(x) -> rem(x, 2) == 0 end)
[2, 4]
iex(8)> Enum.any?([1, 2, 3, 4], fn(x) -> rem(x, 2) == 0 end)
true
iex(9)> Enum.all?([1, 2, 3, 4], fn(x) -> rem(x, 2) == 0 end)
false
其中,any?
的含义是,只要列表中有符合2的倍数的值,那么就返回true
;all?
则要求列表中所有值都是2的倍数,才返回true
。