Elixir - 循环并添加到地图

Wil*_*nne 35 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)
Run Code Online (Sandbox Code Playgroud)

我想知道如何在Elixir中有效地进行Enumeration.而且,似乎我在"历史性"中添加东西的过程就是反功能编程.

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

Jos*_*lim 87

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

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

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

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

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

我们可以使用reduce在Elixir中实现这一点:

map = Enum.reduce [1, 2, 3], %{}, fn x, acc ->
  Map.put(acc, x, x)
end
Run Code Online (Sandbox Code Playgroud)

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

出于这些原因,我们很少使用each枚举.相反,我们使用的功能Enum模块,支持广泛的业务,最终回落到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
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

  • 我已经编辑了问题以获得更完整的答案! (2认同)