首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在Elixir中按关键字拆分列表

如何在Elixir中按关键字拆分列表
EN

Stack Overflow用户
提问于 2016-08-22 20:13:01
回答 4查看 1.4K关注 0票数 8

假设我有一个单词列表,其中一个关键字,在这个例子中,“停止”,划出完整的句子:

代码语言:javascript
复制
["Hello", "from", "Paris", "stop", "Weather", "is", "sunny", "stop", "Missing", "you", "stop"]

我想把它变成:

代码语言:javascript
复制
[["Hello", "from", "Paris"], ["Weather", "is", "sunny"], ["Missing", "you"]]

我知道我可以用String.split来处理字符串,但理想情况下,我想学习如何使用基本的函数构造来解决上述问题,比如头尾递归等,但我不知道如何从哪里开始积累中间列表。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2016-08-22 20:44:25

下面是一个使用模式匹配的简单尾递归实现:

代码语言:javascript
复制
defmodule Main do
  def split_on(list, on) do
    list
    |> Enum.reverse
    |> do_split_on(on, [[]])
    |> Enum.reject(fn list -> list == [] end)
  end

  def do_split_on([], _, acc), do: acc
  def do_split_on([h | t], h, acc), do: do_split_on(t, h, [[] | acc])
  def do_split_on([h | t], on, [h2 | t2]), do: do_split_on(t, on, [[h | h2] | t2])

  def main do
    ["Hello", "from", "Paris", "stop", "Weather", "is", "sunny", "stop", "Missing", "you", "stop"]
    |> split_on("stop")
    |> IO.inspect
  end
end

Main.main

输出:

代码语言:javascript
复制
[["Hello", "from", "Paris"], ["Weather", "is", "sunny"], ["Missing", "you"]]
票数 3
EN

Stack Overflow用户

发布于 2016-08-22 20:53:38

您可以使用chunk_by/2

代码语言:javascript
复制
["Hello", "from", "Paris", "stop", "Weather", "is", "sunny", "stop", "Missing", "you", "stop"]    
|> Enum.chunk_by(fn(x) -> x != "stop" end) 
|> Enum.reject(fn(x) -> x == ["stop"] end)

性能

出于好奇,我想对这个问题的实现性能进行基准测试。基准是每个实现的10万个调用,我运行了3次。如果有人感兴趣,以下是结果:

0.292903s = 0.316024s \x{e76f}\x{e76f}\x{e76f} chunk_by 0.168113s \x{e76f} 0.152456s \x{e76f} 0.151854s = Main.main (@Dogbert's answer) 0.167387s \x{e76f} 0.148059s \x{e76f} 0.143763s = chunk 0.177080s =0.180632 s= 0.185636s = splitter (@stephen

票数 7
EN

Stack Overflow用户

发布于 2016-08-22 21:48:05

这几乎是Enum.chunk_by/2所做的。

def chunk_by(可枚举,有趣) 每个元素上可被拆分,每个元素都返回一个新的值。

但是chunk_by不会丢弃任何元素,所以我们可以将它与Enum.filter/2结合起来。

代码语言:javascript
复制
list = [1, 2, 3, :stop, 4, 5, 6, :stop, 7, 8, :stop] # analogous to your list

list
|> Enum.chunk_by(&(&1 == :stop))
   # at this point, you have [[1,2,3], [:stop], [4,5,6], [:stop], [7,8], [:stop]]
|> Enum.reject(&(&1 == [:stop]))
   # here you are: [[1,2,3], [4,5,6], [7,8]]

第二种方法是使用Enum.reduce/3。由于我们在前面建立了累加器,将我们找到的第一个元素推到后面,所以在缩小列表之前倒转列表是有意义的。否则,我们将得到一个反向列表的反向列表。

我们可能会得到空列表,比如示例列表中的最后一个:stop。因此,我们再一次过滤列表的末尾。

代码语言:javascript
复制
list
|> Enum.reverse
|> Enum.reduce([[]], fn         # note: the accumulator is a nested empty list
  :stop, acc -> [[] | acc]      # element is the stop word, start a new list
  el, [h | t] -> [[el | h] | t] # remember, h is a list, t is list of lists
end)
|> Enum.reject(&Enum.empty?/1)

最后,让我们自己介绍一下列表,并构建一个累加器。如果这让你想起了reduce版本,那不是巧合。

代码语言:javascript
复制
defmodule Stopword do
  def chunk_on(list, stop \\ :stop) do
    list
    |> Enum.reverse
    |> chunk_on(stop, [[]])
  end

  defp chunk_on([], _, acc) do
    Enum.reject(acc, &Enum.empty?/1)
  end
  defp chunk_on([stop | t], stop, acc) do
    chunk_on(t, stop, [[] | acc])
  end
  defp chunk_on([el | t], stop, [head_list | tail_lists]) do
    chunk_on(t, stop, [[el | head_list] | tail_lists])
  end
end

我们使用不需要用户担心累加器的公共函数的公共模式,并将输入传递给带有累加器的私有arity+1函数。因为我们正在建立一个列表列表,所以在累加器中开始一个空列表是很有用的。这样,当累加器为空时,我们不需要特殊情况。

我们在遍历列表之前将其反转,就像我们对reduce所做的那样,就像在完成之后拒绝空列表一样。同样的理由也适用。

我们使用模式匹配来识别停止词。停止词标志着一个新列表的开始,所以我们添加一个新的空列表并丢弃停止词。

一个固定的单词简单地放在第一个列表的前面,在我们的列表中。这些条形和括号的语法有点不灵活。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39087950

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档