2016-04-03 123 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