学习笔记--Erlang程序设计

最近闲暇时间看了下Erlang程序设计,以下都来自这本书,主要是为了了解其思想(即为什么说天生支持并发)而不是看语法(当然对于FP因为折腾emacs也有一定了解),因为现阶段基本看不到实战的可能,之后大概可能就忘完了

1.尾递归求和

递归因为所有值都是保存在栈中,导致很容易栈溢出,Erlang在使用尾递归的时候会自动优化

那什么是尾递归呢?简单来说就是在调用自身时不进行任何其它操作,如下:

1
2
3
4
5
6
7
8
9
%% 书上的写法,典型的递归调用
sum([]) -> 0;
sum([H|T]) -> H + sum(T).

%% 使用尾递归
sum(L) -> sum(0, L).

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

怎么看这都是用循环去写递归、而且任何递归都可以改写成循环,只不过是优不优雅的问题,当然递归在大多数时候都是更简单明了的

PS.所以归集器是个啥(

2.回文构词

说真的看回文构词这个例子我一直在怀疑自己的智商,看了快一个小时才看懂这个列子。。

可能是前边看的太快了,完全没理解**<-**这个符号到底是什么

1
2
perms([]) -> [[]];
perms(L) -> [[H|T] || H <- L, T <- perms(L--[H])].

简单说明下防止以后再犯蠢 = =

首先 <- 是从表中取值,取的是值(所以这里是[H]),而不是把表本身给了前边的变量

先看下这个简单的示例:

1
2
3
4
5
6
7
8
71> f().
ok
72> L = "abc".
"abc"
73> [[H|T] || H <- L, T <- L--[H]].
[[97|98],[97|99],[98|97],[98|99],[99|97],[99|98]]
74> [[H,T] || H <- L, T <- L--[H]].
["ab","ac","ba","bc","ca","cb"]

很清楚了, <- 要从一个表中取到全部的值,也就是说在遍历完全部值之前不会停止

接着再说一下空列表时候必须给定值[[]]:

1
2
3
4
5
6
80> [[H|T] || H <- "abc", T <- []].
[]
81> ["a"|].
* 1: syntax error before: ']'
82> [[H|T] || H <- "abc", T <- [[]]].
["a","b","c"]

还是因为 <- 是从列表中取值,如果取空白会出错,如果不给定空的情况模式匹配也会出错

而只有递归调用时才会出现空列表的情况,shell中那个例子不会出现空列表

PS.看完这一章觉得和出错没关系,guard里使用逗号只有全为true才会执行,然虽然列表推导的qualifier和guard真的没啥关系,这里也不能使用分号,但是应该相当于过滤器返回了false

1
2
3
4
100> [{"a",A} || A <- ["b"], false].
[]
101> [{"a",A} || A <- ["b"], true].
[{"a","b"}]

当然上边换成任意一个非true原子都相当于false,真是除了分号其它地方都和guard一样

3.统计字符串里字符出现的次数

第五章坑真是不少,可能真像网上说的 “估计是老爷子太乐观了,有些功能可能永远也不会加进去”

书中示例:

1
2
3
4
5
6
7
8
9
count_characters(Str) ->
count_characters(Str, #{}).

count_characters([H|T], #{ H => N } = X) ->
count_characters(T, X#{ H := N+1 });
count_characters([H|T], X) ->
count_characters(T, X#{ H => 1});
count_characters([], X) ->
X.

网上找的:而且觉得这就目前来说应该最优了

1
2
3
4
5
6
7
8
9
10
count_characters(Str) ->
count_characters(Str, #{}).

count_characters([H|T], X) ->
case maps:is_key(H, X) of
true -> #{H := N} = X, count_characters(T, X#{H := N+1});
false -> count_characters(T, X#{H => 1})
end;
count_characters([], X) ->
X.

书中试图使用一个模式匹配 #{ H => N } = X 来作为条件(我怎么记得之前说过 =>还能用来做模式匹配、key不能是变量

编译时会提示

1
2
count_charactor.erl:19: illegal pattern
count_charactor.erl:20: variable 'N' is unbound

使用 := 进行模式匹配

1
count_charactor.erl:19: variable 'H' is unbound

所以要么是翻译问题,要么是18改了,现在应该只能使用 := 进行模式匹配并且key依然不能用变量

这里的思路很棒奈何不支持:

  1. #{ H => N } = X 的结果作为条件,如果匹配失败则执行下一个语句(你可以把H改成一个字面量试试
  2. #{ H => N } = X 匹配成功则把匹配到的值+1,失败则添加这个key
  3. 改写也是参照这个思路来的,只是使用了 maps:is_key 来先判断是否存在这个key

改写的同样使用了变量进行模式匹配但是没有出错,因为变量之前已经绑定过了(这里不得不吐槽一下:

binary里边Size明明可以使用之前模式匹配绑定的变量啊,估计是类型推导的锅

4.异常的练习和吐槽

改写的那一个,先吐槽一下:

在这里提到Ruby我就想到了Elixir(真爱情结晶(笑,虽然表示一看到defmodule就兴趣全无了

还有to_json和from_json这两个函数也是没有的,去用第三方库吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
-module(try_test).
-export([generate_exception/1, demo/1, catcher/2]).

generate_exception(1) -> a;
generate_exception(2) -> throw(a);
generate_exception(3) -> exit(a);
generate_exception(4) -> {'EXIT', a};
generate_exception(5) -> error(a).

demo(User) ->
[catcher(I, User) || I <- [1,2,3,4,5]].

catcher(N, User) ->
try generate_exception(N) of
Val -> {N, normal, Val}
catch
Throw:X ->
case User =:= developer of
false -> {N, Throw, X};
true -> {X, erlang:get_stacktrace()}
end
end.

学习笔记--Erlang程序设计
https://back.pub/post/learn-erlang-notes/
作者
Dash
发布于
2015年2月7日
许可协议