如何在Elixir中按索引获取列表元素

48
{status, body} = File.read("/etc/hosts")
if status == :ok do
    hosts = String.split body, "\n"
    hosts = Enum.map(hosts, fn(host) -> line_to_host(host) end)
else
    IO.puts "error reading: /etc/hosts"
end

我有下面的Elixir函数,它读取/etc/hosts文件并尝试使用String.split逐行拆分。
然后,我通过主机行列表映射,并为每个调用line_to_host(host)。 line_to_host方法使用" "拆分行,然后我想设置from和to变量:
def line_to_host(line) do
    data = String.split line, " "
    from = elem(data, 0) // doesn't work
    to = elem(data, 1) // doesn't work either
    %Host{from: from, to: to}
end

我查看了stackoverflow、elixir文档和谷歌搜索,以了解如何获取特定索引处的列表元素。

我知道有head/tail,但肯定有更好的方法来获取列表元素。

elem(list, index)正是我需要的,但不幸的是它不能与String.split一起使用。

如何在Elixir中通过ID获取列表/元组元素


2
你不想在 if 后面执行 {status, body} = File.read("/etc/hosts")。最好使用模式匹配:case File.read("/etc/hosts") do {:ok, body} -> ... - José Valim
好的,知道了。谢谢伙计。 - posixpascal
2个回答

50

您可以使用模式匹配来实现:

[from, to] = String.split line, " "

也许你想添加parts: 2选项,以确保在该行中有多个空格的情况下只获得两个部分:

[from, to] = String.split line, " ", parts: 2

这里还有 Enum.at/3,虽然可以在这里正常工作,但并不习惯。使用 Enum.at 的问题是,由于 Elixir 中列表的实现方式,它需要遍历整个列表直到请求的索引位置,因此对于大型列表来说可能非常低效。


编辑:以下是使用 Enum.at 的示例,但在这种情况下我不会使用它

parts = String.split line, " "
from = Enum.at(parts, 0)
to = Enum.at(parts, 1)

我对这个解决方案有些不满意。难道不能只获取String.split返回的X部分吗?Enum.at与String.split不兼容。 - posixpascal
1
Enum.at 应该可以正常工作。我会在我的答案中添加一个示例。请注意,在 Elixir 中使用 Enum.at 是非常不符惯例的,如果有其他解决方法的话。 - Patrick Oscity
如何创建Enum.map从数组的第二个项目开始(即跳过第一个)? - W.M.
1
@W.M. 我不确定我正确理解了你的问题,它似乎与此无关。你应该发布一个新的问题,包括示例输入、期望输出以及你已经尝试过的内容。 - Patrick Oscity
@PatrickOscity 我的意思是如何循环遍历一个对象数组并跳过第一个项目(在位置0),即在数组中不包括第一个项目的 Enum.map - W.M.
2
@W.M. 你可以使用 [head | tail].= list(如果你需要第一个元素 head)或者 tail = tl(list)(如果你不需要 head)来获取列表的尾部,然后对其进行映射。 - Patrick Oscity

2
你可以考虑实现一个递归函数来替代Enum.at。这会提供更好的性能。当然,这取决于你是否需要高性能的代码。
基准测试:
Comparison: 
Recursive Enum At        9.41 M
Native Enum At           6.48 M - 1.45x slower +48.05 ns

示例代码:

defmodule Test do
  def enum_at([h | t] = x, i) when i > 0  do
    enum_at(t, i-1)
  end
  def enum_at([h | t], i), do: h
end

#[1,2,3,4,5,6,7,8] |> Test.enum_at(4)
# 5

在您的情况下:
data = String.split line, " "
%Host{from: enum_at(data, 0), to: enum_at(data, 1)}

模式匹配可能是最好的选择,但如果拆分返回一个由3个元素组成的列表,则可能会“失败”。您应该在两种情况下都添加验证。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接