Erlang函数中的递归

时间:2014-06-24 14:23:42

标签: erlang

我无法理解这段代码:

{<<"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会有什么不同?我输了。

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条)。