有没有办法模式匹配[_head |中间| Elixir中的_last]?也就是说,我希望在列表中匹配至少包含三个内容(但通常更多),但只匹配除头部和最后一个元素之外的所有内容。
示例:
def match_middle([_head | middle | _last]) do
IO.inspect middle
end
match_middle [1, 2, 3, 4, 5]
>>> [2, 3, 4]
答案 0 :(得分:4)
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
答案 1 :(得分: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个元素,它将返回[]
。
答案 2 :(得分:1)
正如您所看到的,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
操作。