【问题标题】:Elixir: Pattern match a list of specific type?Elixir:模式匹配特定类型的列表?
【发布时间】:2018-09-18 05:55:31
【问题描述】:

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

我们称之为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

一些背景

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

非常感谢任何帮助。谢谢:)

【问题讨论】:

    标签: elixir


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

      虽然@Dogbert 的回答完全有效,但我个人认为明确的条款更简洁:

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

      【讨论】:

        【解决方案3】:

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

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

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

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

        不同的是,如果不匹配,第一个失败,而第二个返回false。如果您想坚持使用 elixir 的“let it crash”,您可能对第一个感兴趣,而在大多数情况下,您会更喜欢使用第二个,例如:

        k == Enum.reject(l, &match?(%X{}=&1))
        k == [] || IO.inspect(k)
        

        Enum.reject?/2match?/2 一起使用不会崩溃并返回所有非 X 结构元素的列表。

        【讨论】:

        • @Dogbert 的答案更好,因为如果列表包含非 X 类型的结构,它将返回 false。仅当所有元素都属于struct X 时,它才会枚举整个列表,从而使其更快。
        • 谢谢,您的回答是回答“我希望模式匹配失败是列表包含其他内容。”的问题。但是,为了其他人搜索此问题的答案以及出于性能考虑,我将接受另一个。但是,非常感谢内森。
        • @dsignr 我完全理解,并且我承认在大多数情况下人们会寻找 Dogbert 的解决方案
        • 另外,如果您想在匹配失败时崩溃,您实际上不需要检查返回值是否为[],因为如果是这种情况,则已经抛出异常。
        • 这是准确的,最初,我没想到模式匹配会崩溃。现在,我要么使用Enum.map 与模式匹配崩溃,要么使用Enum.rejectmatch?,以便知道哪些元素没有模式匹配。我在这个方向上编辑了答案。
        【解决方案4】:

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

        正如我的问题中所述,我试图匹配上传文件的每个实例以确保它是 %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
        

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

        希望这对将来的某人有所帮助。

        【讨论】:

          【解决方案5】:

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

            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 的崩溃版本。有一些更通用的东西作为保护条款会很好。

          【讨论】:

            猜你喜欢
            • 2016-11-07
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-06-11
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多