【问题标题】:Pattern match middle of list in ElixirElixir中列表的模式匹配中间
【发布时间】:2017-04-11 13:26:30
【问题描述】:

有没有办法进行模式匹配,例如 [_head |中间 | _last] 在长生不老药中?也就是说,我想匹配一个至少包含三件事(但通常更多)的列表,但只匹配除头和最后一个元素之外的所有内容。

例子:

    def match_middle([_head | middle | _last]) do
      IO.inspect middle
    end

    match_middle [1, 2, 3, 4, 5]
    >>> [2, 3, 4]

【问题讨论】:

  • 大概没有办法
  • 您确定要进行模式匹配,而不仅仅是提取没有第一个和最后一个元素的列表吗?
  • @GavinBrelstaff 我特别想知道模式匹配,但如果你有一个简洁的方法,我想看看。我目前正在使用想到的第一件事。我看不到在 cmets 中格式化代码的方法......这里用分号来指导。 [_ |休息] = 物品; [_最后| rest_backwards] = Enum.reverse 休息; middle = Enum.reverse(rest_backwards)

标签: pattern-matching elixir


【解决方案1】:

Elixir 列表实际上是可以追溯到 Lisp 早期的缺点列表。

iex(1)> foo = [1 ,2 , 3]
[1, 2, 3]
iex(2)> [ head | [ middle | tail ]] = foo
[1, 2, 3]
iex(3)> middle
2

所以当我们把它写成[1,2,3]时,它实际上是[1, [2,[3,[]]]]

但是,这并不能涵盖您的要求。可能是这样的

def match_middle([_head | [ second | tail]]) when tail != [] do
  flip_tail = Enum.reverse(tail)
  [end | rest] = flip_tail
  [second | Enum.reverse(rest) ]
end

【讨论】:

  • 老实说,这不是“模式匹配类似于[_head | middle | _last]”,例如。 G。它不能用于守卫。
【解决方案2】:

可能我会在没有模式匹配的情况下解决这个问题,因为您的情况非常具体 - 您假设 middle 是除第一个和最后一个元素之外的所有内容,但拆分运算符以不同的方式工作 - 它为第一个元素和其他所有内容拆分列表作为tail

在你的情况下,我会创建私有函数来匹配中间:

defp get_middle_from_list(list) do
  list 
  |> Stream.drop(1)
  |> Stream.drop(-1)
  |> Enum.to_list()
end

并使用它

get_middle_from_list [1, 2, 3, 4, 5]
> [2, 3, 4]

如果列表包含少于 3 个元素,它当然会返回 []

【讨论】:

    【解决方案3】:

    如您所见,List.pop_at/3 本身是使用递归、:lists.reverse 和头部访问实现的。也就是说,没有办法直接访问列表中的最后一个元素。

    另一方面,如果您担心列表中的元素不能超过N,则仍然可以实现直接模式匹配:

    defmodule MatchInTheMiddle do
      @n 10
      Enum.each(0..@n, fn i ->
        @match_clause Enum.map(0..i, fn j -> {:"p#{j}", [], Elixir} end)
        def match_middle(unquote(
          [{:_head, [], Elixir}] ++ @match_clause ++ [{:_last, [], Elixir}])
        ), do: IO.inspect unquote(@match_clause)
      end)
    end
    

    而且,是的,短列表和长列表的优雅后备:

    def match_middle([]), do: []
    def match_middle([_]), do: []
    def match_middle([_head | tail]) do
      with [_tail | middle] <- :lists.reverse(tail), do: middle
    end
    
    MatchInTheMiddle.match_middle([1,2,3,4])
    #⇒ [2,3]
    

    这并不像您可能期望的那样优雅,但在某些情况下它可能会有所帮助,因为这些子句被编译成模式匹配并且在大量数据上这更高效 Enum 操作。

    【讨论】:

    • 这是最接近我可以在守卫或模式匹配中使用的表达式。确实,虽然不是很优雅。
    猜你喜欢
    • 2016-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-24
    • 2017-06-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多