我无法理解这段代码:
{<<"block">>, Els} ->
JIDs = parse_blocklist_items(Els, []),
process_blocklist_block(LUser, LServer, JIDs);
#1 parse_blocklist_items([], JIDs) -> JIDs;
#2 parse_blocklist_items([#xmlel{name = <<"item">>,
attrs = Attrs}
| Els],
JIDs) ->
case xml:get_attr(<<"jid">>, Attrs) of
{value, JID1} ->
JID = jlib:jid_tolower(jlib:binary_to_jid(JID1)),
parse_blocklist_items(Els, [JID | JIDs]);
false -> parse_blocklist_items(Els, JIDs)
end;
#3 parse_blocklist_items([_ | Els], JIDs) ->
parse_blocklist_items(Els, JIDs).
我不确定首先调用哪个函数。
Els是空的,这意味着首先调用#3,然后调用#2,然后调用#3。对?为什么我们需要功能#3?如果#2已经返回JID,#3会有什么不同?我输了。
答案 0 :(得分:5)
首先,一些术语:#1
,#2
和#3
被认为是同一功能的不同子句。
这是编写递归函数的常用方法。该函数将输入列表中的一些元素(但不是全部)转换为其他元素。
#1
是基本情况:如果没有更多输入元素,我们只返回所有累积的输出元素(JIDs
)。
在#2
中,输入列表的第一个元素是xmlel
字段,name
字段为<<"item">>
。我们检查jid
属性,如果有,我们创建一个JID并将其添加到列表中。请注意,我们使用递归调用来执行此操作:我们调用相同的函数,第一个参数是输入列表的剩余元素,第二个参数是现有输出列表< em> plus 新添加的元素。
如果输入列表的第一个元素与#2
中的模式不匹配,我们最终会进入#3
,我们只是跳过它并继续处理其余部分列表。
如果Els
为空,正如您在问题中提到的那样,那么我们将在#1
条款中结束,并且实际上未能在{{1}中找到代码}和#2
。
条款#3
和#2
类似,因为&#34;消费&#34;输入列表中的元素。不同之处在于,子句#3
有时为输出列表生成一个新元素,而子句#2
从不这样做。它们可以写成单个条款;这是一个风格和偏好的问题。
答案 1 :(得分:2)
你有三个条款,每个条款提供一个可能的匹配。
您的#1
与空列表匹配。
您的#2
与头部为#xmlel{}
且name
属性为<<"item">>
的列表匹配。
您的#3
匹配非空列表(即,头部不是xml元素,其名称为 item 。
匹配顺序始终从上到下。因此,匹配将在1然后2然后3。
[] will match 1
[foo, bar] will match 3
[#xmlel { name = <<"item">>, attrs = something}, baz] will match 2
答案 2 :(得分:1)
Erlang调度程序在调度函数时会查看该函数的参数和所有可用子句。它按照定义它们的顺序迭代函数子句,直到找到所有参数与其各自模式匹配的第一个函数子句。
因此,如果Els
是一个空列表,它将使用第1条。
如果Els
是第一个元素是xmlel
记录的列表,它将使用第2条。
如果Els
是第一个元素为其他内容的列表,则使用第3个子句。
第3条是一个catch-all子句,当列表中有意外值时,它很可能用于避免错误。这是一个相当常见的做法,它是有道理的。
这也是一个递归函数,因此在第2和第3条中,它再次调用parse_blocklist_items
,并且将一直这样做,直到它用完列表中的项目(并因此执行第1条)。