Elixir:模式匹配特定类型的列表吗?

时间:2018-09-18 05:55:31

标签: elixir

我有一个任意大小的结构列表。

我们称之为l

l = [%X{a:1}, %X{a:3}, %X{a:9}, %X{a:11}]

l的大小不断变化。我想知道的是如何与l进行模式匹配以确保它始终由%X{}的结构组成。如果列表包含其他内容,我希望模式匹配失败。例如:

l = [%X{a:1}, %X{a:3}, %Y{a:9}, %Z{a:11}]

我尝试过的事情

i = %X{}
j = %Y{}

[%X{}|_] = [i,i,i]

但这仅匹配第一个元素。

[%X{}|_] = [i,j,j]

对于我的用例应该会失败,但是不会。 也许如果有一个运算符或类似的东西,它将与特定类型的列表匹配,那正是我在寻找的东西:

[%X{}+] = [i,i,i] # Doesn't exist, just an example

某些背景

我在凤凰城,并且我有一个模型posthas_manyimages关系。给定的用户可以上传多张图片,并且我想进行模式匹配以确保在这种情况下使用正确的结构(%Plug.Upload{})。

非常感谢您的帮助。谢谢:)

5 个答案:

答案 0 :(得分:5)

您不能在列表的每个元素上进行模式匹配(没有递归)。在这种情况下,我将使用Enum.all?/2match?/2宏:

if Enum.all?(list, &match?(%X{}, &1)) do
  ...
end

答案 1 :(得分:3)

虽然@Dogbert的答案是完全正确的,但我个人认为显式子句更加简洁:

all_are_x =
  Enum.all?(list, fn
    %X{} -> true
    _ -> false
  end)

答案 2 :(得分:1)

AFAIK,您要查找的内容不存在:您无法在列表的每个元素上进行模式匹配。

您可以使用Enum.map/2来使第一个非%X{}元素崩溃:

Enum.map(l, &(%X{}=&1))

要进行模式匹配,我使用了%X{} = something,而Dogbert使用了match?(%X{}, &1)

区别在于,第一个如果不匹配则失败,而第二个返回false。如果您想坚持使用长生不老药的“ 使其崩溃”,则可能对第一个很感兴趣,而在大多数情况下,您将更喜欢使用第二个,例如:

k == Enum.reject(l, &match?(%X{}=&1))
k == [] || IO.inspect(k)
Enum.reject?/2一起使用的

match?/2不会崩溃,并返回非X结构的所有元素的列表。

答案 3 :(得分:0)

我最终如何解决这个问题:

如我的问题所述,我试图匹配每个上载文件的实例,以确保它是一个%Plug.Upload{}结构。我应该提到我在凤凰城。 Phoenix的好处是您可以将这种验证直接添加到模型中-这就是变更集及其验证的目的!并且会针对要保存到数据库的模型的每个实例自动进行验证。

因此,我最终在自己的image.ex模型中添加了自定义验证。

所以,我的变更集最终看起来像:

  @doc false
  def changeset(image, attrs) do
    image
    |> cast(attrs, [:title, :description, :alt, :src, :sequence, :file])
    |> ensure_valid_upload()
    |> validate_required([:file])
  end

话虽如此,我最终还是在代码的其他与模型没有直接关系的其他区域中使用了可接受的答案。

希望这对以后的人有帮助。

答案 4 :(得分:0)

例如,当我只想要原子时,我会这样做。我相当公然,但我喜欢在使用时保持代码明确:

  def only_atoms(list) when is_list list do
    Enum.map(list, &only_atom/1)
  end

  def only_atom(x) when is_atom x do
    x
  end

我想这很像 @Aleksei Matiushkin 的崩溃版本。可以有一些更通用的保护条款来使用。