Elixir - 循环并添加到地图

时间:2015-04-28 15:54:50

标签: enumeration elixir

我正在使用C#构建的一些代码在Elixir中重建一些东西。

它非常被黑客攻击,但效果很好(虽然不是在Linux上,因此重建)。

它所做的基本上是检查一些RSS提要并查看是否有任何新内容。这是代码:

Map historic (URL as key, post title as value).
List<string> blogfeeds
while true
for each blog in blogfeeds
   List<RssPost> posts = getposts(blog)
   for each post in posts
        if post.url is not in historic
           dothing(post)
           historic.add(post)

我想知道如何在Elixir中有效地进行Enumeration。而且,似乎我在“历史性”中添加内容的过程就是反函数式编程。

显然,第一步是宣布我的URL列表,但除此之外,枚举的想法正在弄乱我的脑袋。有人可以帮帮我吗?感谢。

3 个答案:

答案 0 :(得分:84)

这是一个很好的挑战,拥有和解决它肯定会让你对函数式编程有所了解。

函数式语言中此类问题的解决方案通常为reduce(通常称为fold)。我将从一个简短的答案(而不是直接翻译)开始,但随时可以要求跟进。

以下方法通常不适用于函数式编程语言:

map = %{}
Enum.each [1, 2, 3], fn x ->
  Map.put(map, x, x)
end
map

最后的地图仍然是空的,因为我们不能改变数据结构。每次拨打Map.put(map, x, x)时,都会返回一张新地图。因此,我们需要在每次枚举后显式检索新映射。

我们可以使用reduce:

在Elixir中实现这一点
map = Enum.reduce [1, 2, 3], %{}, fn x, acc ->
  Map.put(acc, x, x)
end

Reduce会将前一个函数的结果作为累加器发出,用于下一个项目。运行上面的代码后,变量map将为%{1 => 1, 2 => 2, 3 => 3}

出于这些原因,我们很少在枚举上使用each。相反,我们使用the Enum module中支持各种操作的函数,当没有其他选项时,最终会回退到reduce

编辑:回答问题并进行更直接的代码翻译,您可以按照以下方式检查和更新地图:

Enum.reduce blogs, %{}, fn blog, history ->
  posts = get_posts(blog)
  Enum.reduce posts, history, fn post, history ->
    if Map.has_key?(history, post.url) do
      # Return the history unchanged
      history
    else
      do_thing(post)
      Map.put(history, post.url, true)
    end
  end
end

事实上,这里的集合会更好,所以让我们重构一下并在过程中使用一个集合:

def traverse_blogs(blogs) do
  Enum.reduce blogs, HashSet.new, &traverse_blog/2
end

def traverse_blog(blog, history) do
  Enum.reduce get_posts(blog), history, &traverse_post/2
end

def traverse_post(post, history) do
  if post.url in history do
    # Return the history unchanged
    history
  else
    do_thing(post)
    HashSet.put(history, post.url)
  end
end

答案 1 :(得分:0)

这也可能有所帮助:

iex(31)> c "count_animals.exs"
JEA: begin
%{"cats" => 1, "ducks" => 10, "geese" => 18}
JEA: end
[]

输出:

{{1}}

我只是在学习Elixir,所以上述内容无疑是次优的,但希望能提供一些信息。

答案 2 :(得分:0)

我还是Elixir的新手,但这是一个可爱且简单的解决方案,它使用了模式匹配和递归。

defmodule YourModule do
   def reduce_list([], reduced) do reduced end

   def reduce_list([first | rest ], reduced) do
      # Do what you need to do here and call the function again 
      # with remaining list items and updated map.
      reduce_list(rest, Map.put(reduced, first, "Done"))
   end
end 

然后仅使用要映射的列表和一个空的映射调用该函数

> YourModule.reduce_list(["one", "two", "three"], %{})
  %{"one" => "Done", "three" => "Done", "two" => "Done"}