我正在使用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列表,但除此之外,枚举的想法正在弄乱我的脑袋。有人可以帮帮我吗?感谢。
答案 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"}