查看源代码 列表推导式
简单示例
本节从一个简单的示例开始,展示了一个生成器和一个过滤器
> [X || X <- [1,2,a,3,4,b,5,6], X > 3].
[a,4,b,5,6]
可以这样解读: X 的列表,其中 X 取自列表 [1,2,a,...]
并且 X 大于 3。
符号 X <- [1,2,a,...]
是一个生成器,表达式 X > 3
是一个过滤器。
可以添加额外的过滤器 is_integer(X)
,以将结果限制为整数
> [X || X <- [1,2,a,3,4,b,5,6], is_integer(X), X > 3].
[4,5,6]
生成器可以组合使用。例如,两个列表的笛卡尔积可以写成如下形式
> [{X, Y} || X <- [1,2,3], Y <- [a,b]].
[{1,a},{1,b},{2,a},{2,b},{3,a},{3,b}]
快速排序
著名的快速排序例程可以写成如下形式
sort([]) -> [];
sort([_] = L) -> L;
sort([Pivot|T]) ->
sort([ X || X <- T, X < Pivot]) ++
[Pivot] ++
sort([ X || X <- T, X >= Pivot]).
表达式 [X || X <- T, X < Pivot]
是 T
中所有小于 Pivot
的元素的列表。
[X || X <- T, X >= Pivot]
是 T
中所有大于或等于 Pivot
的元素的列表。
使用上述算法,列表按以下方式排序
- 包含零个或一个元素的列表是微不足道地排序的。
- 对于包含多个元素的列表
- 列表中的第一个元素被隔离为枢轴元素。
- 剩余的列表被划分为两个子列表,使得
- 第一个子列表包含所有小于枢轴元素的元素。
- 第二个子列表包含所有大于或等于枢轴元素的元素。
- 子列表通过相同的算法递归排序,并将结果组合起来,形成一个由以下内容组成的列表
- 来自第一个子列表的所有元素,即所有小于枢轴元素的元素,按排序顺序排列。
- 枢轴元素。
- 来自第二个子列表的所有元素,即所有大于或等于枢轴元素的元素,按排序顺序排列。
注意
虽然如上所示的排序算法可以很好地说明带有过滤器的列表推导式,但在实际使用中,
lists
模块包含以更有效的方式实现的排序函数。
排列
以下示例生成列表中元素的所有排列
perms([]) -> [[]];
perms(L) -> [[H|T] || H <- L, T <- perms(L--[H])].
这以所有可能的方式从 L
中取出 H
。结果是所有列表 [H|T]
的集合,其中 T
是 L
的所有可能的排列的集合,删除了 H
> perms([b,u,g]).
[[b,u,g],[b,g,u],[u,b,g],[u,g,b],[g,b,u],[g,u,b]]
勾股数
勾股数是满足 A**2 + B**2 = C**2
的整数集 {A,B,C}
。
函数 pyth(N)
生成所有满足 A**2 + B**2 = C**2
且边之和等于或小于 N
的整数 {A,B,C}
的列表
pyth(N) ->
[ {A,B,C} ||
A <- lists:seq(1,N),
B <- lists:seq(1,N),
C <- lists:seq(1,N),
A+B+C =< N,
A*A+B*B == C*C
].
> pyth(3).
[].
> pyth(11).
[].
> pyth(12).
[{3,4,5},{4,3,5}]
> pyth(50).
[{3,4,5},
{4,3,5},
{5,12,13},
{6,8,10},
{8,6,10},
{8,15,17},
{9,12,15},
{12,5,13},
{12,9,15},
{12,16,20},
{15,8,17},
{16,12,20}]
以下代码减少了搜索空间,效率更高
pyth1(N) ->
[{A,B,C} ||
A <- lists:seq(1,N-2),
B <- lists:seq(A+1,N-1),
C <- lists:seq(B+1,N),
A+B+C =< N,
A*A+B*B == C*C ].
使用列表推导式进行简化
例如,列表推导式可以用于简化 lists.erl
中的某些函数
append(L) -> [X || L1 <- L, X <- L1].
map(Fun, L) -> [Fun(X) || X <- L].
filter(Pred, L) -> [X || X <- L, Pred(X)].
列表推导式中的变量绑定
列表中推导式中出现的变量的作用域规则如下
- 生成器模式中出现的所有变量都被假定为“新的”变量。
- 在列表推导式之前定义的,并且在过滤器中使用的任何变量,都具有它们在列表推导式之前的值。
- 变量不能从列表推导式中导出。
作为这些规则的一个示例,假设你想编写函数 select
,该函数从元组列表中选择某些元素。假设你编写 select(X, L) -> [Y || {X, Y} <- L].
,其目的是从 L
中提取所有第一个项目为 X
的元组。
编译这将给出以下诊断
./FileName.erl:Line: Warning: variable 'X' shadowed in generate
此诊断警告模式中的变量 X
与函数头中出现的变量 X
不同。
评估 select
给出以下结果
> select(b,[{a,1},{b,2},{c,3},{b,7}]).
[1,2,3,7]
这不是想要的结果。为了实现期望的效果,必须将 select
编写如下
select(X, L) -> [Y || {X1, Y} <- L, X == X1].
生成器现在包含未绑定的变量,并且测试已移至过滤器中。
现在按预期工作
> select(b,[{a,1},{b,2},{c,3},{b,7}]).
[2,7]
另请注意,生成器模式中的变量将遮蔽先前生成器模式中绑定的同名变量。例如
> [{X,Y} || X <- [1,2,3], X=Y <- [a,b,c]].
[{a,a},{b,b},{c,c},{a,a},{b,b},{c,c},{a,a},{b,b},{c,c}]
将变量导入列表推导式的规则的一个后果是,某些模式匹配操作必须移至过滤器中,而不能直接写在生成器中。
为了说明这一点,不要 如下编写
f(...) ->
Y = ...
[ Expression || PatternInvolving Y <- Expr, ...]
...
而是如下编写
f(...) ->
Y = ...
[ Expression || PatternInvolving Y1 <- Expr, Y == Y1, ...]
...