Elixir - 修改匿名函数外部的值

Mid*_*ire 0 elixir

首先,我绝对相信我会以错误的方式解决这个问题,因为我还在学习来自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)
Run Code Online (Sandbox Code Playgroud)

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

这不起作用,因为传递给的匿名函数Enum.each有自己的作用域,并且没有为index和设置值high_views.

有没有办法将值从外部范围传递到匿名函数?或者更好的问题是,我该怎么做呢?

我打算让它工作,然后重构代码,但我卡住了.谢谢你的帮助.

Mar*_*lin 6

Elixir是不变的.该函数是一个闭包,因此外部变量在那里可见,但你不能改变它们.您只能重新绑定它们,但重新绑定将保留在内部的匿名函数范围内.

但是你正在尝试做的工具都在Enum模块中.

您实际上是在寻找具有最大视图的索引.让我们看看Enum函数.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)
Run Code Online (Sandbox Code Playgroud)

与您的实现的主要区别在于内部函数根据其参数返回一个值,而不是尝试改变外部状态.

我把"无所事事"的情况折叠成一个简单的全能_,并假设youtube视频没有负视图计数返回-1.您的示例的直接转换将在此处返回零(您的初始值high_views).这也许是安全的.