(参见本文底部的编辑)
我正在Elixir中创建一个程序,它从我已经获得的标签列表中计算HTML标签的类型.这意味着密钥应该是标记,值应该是计数.
例如,在以下示例文件中
<html><head><body><sometag><sometag><sometag2><sometag>
Run Code Online (Sandbox Code Playgroud)
我的输出应该类似于以下内容:
html: 1
head: 1
body: 1
sometag: 3
sometag2: 1
Run Code Online (Sandbox Code Playgroud)
这是我的代码:
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
Run Code Online (Sandbox Code Playgroud)
我知道我应该使用它,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}]
Run Code Online (Sandbox Code Playgroud)
如您所见,有重复的条目,它变成了一个字典列表.现在我甚至都没有尝试让计数工作,只要字典不重复条目(这就是为什么值总是只有"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
Run Code Online (Sandbox Code Playgroud)
输出:
%{"<a": 1, "<body": 1, "<html": 1, "<p": 1}
Run Code Online (Sandbox Code Playgroud)
现在我必须完成实际计算标签的挑战...如果有人能提供任何见解,我将不胜感激!
首先,用正则表达式解析html并不是最好的主意.有关详细信息(尤其是已接受的答案),请参阅此问题.
其次,您正在尝试用函数式语言编写命令式代码(这是关于代码的第一个版本).Elixir中的变量是不可变的.dict永远是一张空地图.Enum.map获取一个列表并始终返回相同长度的新列表,并且所有元素都已转换.您的转换函数采用空映射并将一个键值对放入其中.
因此,您将获得包含一个元素映射的列表.这条线:
Map.put_new(dict, tag, 1)
Run Code Online (Sandbox Code Playgroud)
不会dict在适当的位置更新,而是使用旧的创建新的,这是空的.在您的示例中,它与以下内容完全相同:
%{tag => 1}
Run Code Online (Sandbox Code Playgroud)
您有几种选择可以采用不同的方式.最近的方法是使用Enum.reduce.它需要一个列表,一个初始累加器和一个函数elem, acc -> new_acc.
taglist
|> Enum.reduce(%{}, fn(tag, acc) -> Map.update(acc, tag, 1, &(&1 + 1)) end)
Run Code Online (Sandbox Code Playgroud)
它看起来有点复杂,因为有几个很好的语法糖.taglist |> Enum.reduce(%{}, fun)是一样的Enum.reduce(taglist, %{}, fun).&(&1 + 1)是简写fn(counter) -> counter + 1 end.
Map.update 需要四个参数:要更新的映射,要更新的键,如果键不存在则为初始值,以及如果存在则对键执行某些操作的函数.
所以,这两行代码执行此操作:
Enum.reduce%{}fn(tag, acc),并且:
&(&1 + 1)