2016-04-03 83 views
1

从列表中计算列表项的出现次数?

我正在Elixir中制作一个程序,用来计算我已经获得的标记列表中的HTML标记类型。这意味着密钥应该是标签,值应该是计数。

例如在下面的示例文件

<html><head><body><sometag><sometag><sometag2><sometag> 

我的输出应该是这样的:

html: 1 
head: 1 
body: 1 
sometag: 3 
sometag2: 1 

这里是我的代码:

def tags(page) do 
    taglist = Regex.scan(~r/<[a-zA-Z0-9]+/, page) 

    dict = Map.new() 

    Enum.map(taglist, fn(x) -> 
         tag = String.to_atom(hd(x)) 
         Map.put_new(dict, tag, 1) 
         end) 

end 

我知道我应该可能使用Enum.each代替,但当我这样做时,我的字典最终只是空的而不是不正确的。

随着Enum.map,这是我收到的输出:

iex(15)> A3.test 
[%{"<html" => 1}, %{"<body" => 1}, %{"<p" => 1}, %{"<a" => 1}, %{"<p" => 1}, 
%{"<a" => 1}, %{"<p" => 1}, %{"<a" => 1}, %{"<p" => 1}, %{"<a" => 1}] 

正如你所看到的,有重复的条目和它变成词典列表。现在,我甚至不试图让计数工作,只要字典不重复条目(这就是为什么值总是“1”)。

感谢您的任何帮助。

编辑:------------------

好了,所以我想通了,我需要使用Enum.reduce

下面的代码产生输出我在找(现在):

def tags(page) do 
    rawTagList = Regex.scan(~r/<[a-zA-Z0-9]+/, page) 
    tagList = Enum.map(rawTagList, fn(tag) -> String.to_atom(hd(tag)) end) 


    Enum.reduce(tagList, %{}, fn(tag, acc) -> 
            Map.put_new(acc, tag, 1) 
           end) 

end 

输出:

%{"<a": 1, "<body": 1, "<html": 1, "<p": 1} 

现在我已经完成交流的挑战在我去的时候对标签进行统计......如果任何人都能提供任何有关的信息,我将不胜感激!

+0

请将imgur中的代码片段粘贴到问题中。该片段很短,所以SO政策表示应在问题中包含短片段。这使得搜索更容易,即使在imgur被关闭的情况下也可以使用片段。 – tkowal

+0

完成,对问题的任何见解? – KingDan

回答

6

首先,用正则表达式解析html是不是最好的想法。 See this question for more details(特别是接受的答案)。其次,你试图用功能性语言编写命令式代码(这是关于代码的第一个版本)。 Elixir中的变量是不可变的。 dict将永远是一张空白的地图。 Enum.map需要一个列表,并且总是返回所有元素转换后长度相同的新列表。您的转换函数会使用空映射并将一个键值对放入其中。

因此,您将得到一个包含一个元素映射的列表。行:

Map.put_new(dict, tag, 1) 

不到位更新dict,但使用旧的,里面是空的创建新的。在你的例子中它是完全一样的:

%{tag => 1} 

你有几个选择来做它不同。最接近的方法是使用Enum.reduce。它需要一个列表,一个初始累加器和一个函数elem, acc -> new_acc

taglist 
|> Enum.reduce(%{}, fn(tag, acc) -> Map.update(acc, tag, 1, &(&1 + 1)) end) 

它看起来有点复杂,因为有几个很好的语法糖。 taglist |> Enum.reduce(%{}, fun)Enum.reduce(taglist, %{}, fun)相同。 &(&1 + 1)fn(counter) -> counter + 1 end的简写。

Map.update需要四个参数:更新的映射,更新的键,初始值(如果键不存在)以及如果键存在时用键执行操作的函数。

所以,这些代码两行做到这一点:

  • 叠代列表Enum.reduce
  • 开始与空地图%{}
  • 取当前元素和地图fn(tag, acc),并且:
    • 如果键不存在插入1
    • 如果它存在增加一个&(&1 + 1)
+1

这是一个绝对出色的答案。我刚开始学习Elixir是我今天的第一个函数式编程语言,所以你的文章提供了很多新的见解。非常感谢你的时间:)希望我可以不止一次地upvote。 – KingDan