首先,我绝对相信我会以错误的方式解决这个问题,因为我还在学习来自Ruby的Elixir ......
我从youtube获取搜索结果列表,并尝试提取视图最多的视频。
# html is the contents of the search results page
metas = html |> Floki.find(".yt-lockup-meta-info > li")
counter = -1
index = -1
high_views = 0
Enum.each(metas, fn(li) ->
counter = counter + 1
text = Floki.text(li)
case String.split(text, " ") do
[count, "views"] ->
views = String.to_integer(String.replace(count, ",", ""))
IO.puts(">>> #{counter} - #{to_string(views)} views")
if views > high_views do
high_views = views
index = counter
end
[age, time_measurement, "ago"] ->
nil
end
end)
metas
是li
元组的列表,如下所示:
[{"li", [], ["2 years ago"]}, {"li", [], ["5,669,783 views"]},
{"li", [], ["9 years ago"]}, {"li", [], ["17,136,804 views"]},
...
{"li", [], ["1 year ago"]}, {"li", [], ["15,217 views"]},
{"li", [], ["8 years ago"]}, {"li", [], ["909,053 views"]}]
这不起作用,因为传递给Enum.each
的匿名函数有自己的范围,并且没有设置index
和high_views
的值。
有没有办法将值从外部作用域传递到匿名函数?或者更好的问题是,我该怎么做呢?
我打算让它工作,然后重构代码,但我卡住了。谢谢你的帮助。
答案 0 :(得分:6)
Elixir是不可改变的。该函数是一个闭包,因此外部变量在那里可见,但你不能改变它们。您只能重新绑定它们,但重新绑定将保留在内部的匿名函数范围内。
但是,您尝试做的工具都在Enum
模块中。
您实际上是在寻找具有最大视图的索引。让我们看看Enum functions。 Enum.max_by/2
看起来很有希望。它需要一个枚举和一个返回我们想要最大值的函数。我将它与Enum.with_index/1
配对,它接受一个列表,并用元素的索引将每个元素包装在一个元组中。
metas
|> Enum.with_index
|> Enum.max_by(fn {li, index} ->
text = Floki.text(li)
case String.split(text) do # (splits on whitespace by default)
[count, "views"] ->
views = count |> String.replace(",", "") |> String.to_integer
IO.puts ">>> #{index} - #{views} views"
views
_ -> -1
end
end)
你的实现的主要区别在于内部函数根据其参数返回一个值,而不是试图改变外部状态。
我将“无所事事”的情况折叠成一个简单的全能_
,并假设youtube视频没有负视图计数,则返回-1。您的示例的直接转换将在此处返回零(您的high_views
的初始值)。那也许是安全的。
答案 1 :(得分:0)
正如我猜测的那样,我确实错了。以下是我最终完成这项工作的方法:
defp extract_song_url_from_youtube_response(html = _) do
sorted = html
|> Floki.find(".yt-lockup-content")
|> Enum.sort(fn(item1, item2) -> view_count(item1) > view_count(item2) end)
[_, id] = Enum.at(sorted, 0)
|> Floki.find("h3 > a")
|> Floki.attribute("href")
|> Enum.find(fn(x) -> x =~ "/watch" end)
|> String.split("=")
"https://www.youtube.com/embed/" <> id
end
defp view_count(item) do
meta = item |> Floki.find(".yt-lockup-meta-info > li")
views = case Enum.at(meta, 1) do
{"li", _, viewlist} ->
parts = String.split(Enum.at(viewlist, 0), " ")
String.to_integer(String.replace(Enum.at(parts, 0), ",", ""))
nil ->
# most likely a playlist
0
end
end
因此,我没有尝试从匿名函数范围之外修改变量,而是重新进入HTML层次结构,并根据视频收到的观看次数对每个<div>
结果进行排序。 / p>
Elixir真是令人惊叹,一旦我能够绕过它并停止试图强迫事物像Ruby一样。