【问题标题】:How do you pattern match for nil or a blank string in Elixir?如何在 Elixir 中对 nil 或空白字符串进行模式匹配?
【发布时间】:2019-02-28 04:06:55
【问题描述】:

这是一个带有测试的代码示例,其中我有一个函数检查传入的值是否在允许的值集中,如果不是,它会引发异常,但如果传入的值是 nil 或一个空白字符串,它只返回false。这里的想法是任何类型的空白值都意味着没有价值。如果存在与有效值之一不匹配的非空白值,那么我们确实希望引发异常,因为该值无效。还想象这个值来自命令行参数或 Phoenix 请求参数,我们无法控制输入值只是 nil,我们必须检查空值、nil、空字符串或仅包含的字符串的所有排列空格。

ExUnit.start()

defmodule ValidColor do
  @valid_colors MapSet.new(~w[red green blue])

  def valid?(color) do
    cond do
      blank?(color) -> false
      MapSet.member?(@valid_colors, color) -> true
      true -> raise "invalid color"
    end
  end

  defp blank?(value) do
    case value do
      nil -> true
      v when is_binary(v) -> v =~ ~r/\A\s*\z/
      _ -> false
    end
  end
end

defmodule ValidColorTest do
  use ExUnit.Case, async: true

  test "nil" do
    refute ValidColor.valid?(nil)
  end

  test "empty string" do
    refute ValidColor.valid?("")
  end

  test "string with only blank characters" do
    refute ValidColor.valid?("     ")
  end

  test "invalid color" do
    assert_raise RuntimeError, "invalid color", fn ->
      ValidColor.valid?("yellow")
    end
  end

  test "valid color" do
    assert ValidColor.valid?("red")
  end
end

这似乎是 blank? 辅助函数有用的情况,就像我在这个例子中所做的那样。好像有a sentiment among Elixir programmers that this type of helper method is unnecessary and can be done with pattern matching

如何仅使用模式匹配而不使用 blank? 辅助函数来实现这样的函数?

【问题讨论】:

    标签: elixir


    【解决方案1】:

    虽然这里给出的所有答案在某种程度上都是正确的,但它们在语义上并不完美。你想要的实际上是将nil 视为一个空字符串并拒绝它。直接做:

    defp blank?(str_or_nil),
      do: "" == str_or_nil |> to_string() |> String.trim()
    

    请注意,此解决方案与目前在此给出的其他解决方案不同,它适用于 所有 空格,在 UTF-8 规范中定义为空格。

    【讨论】:

      【解决方案2】:

      您也可以使用case/2

      def blank?(str) do
        case str do
          nil -> true
          "" -> true
          " " <> r -> blank?(r)
          _ -> false 
        end
      end
      

      注意:在这种情况下,我们只考虑空格,“\n”、“\r”和“\t”上的模式匹配也必须像@dogbert 在他的回答中所做的那样考虑。

      【讨论】:

        【解决方案3】:

        nil 的情况很简单:

        defp blank?(nil), do: true
        

        要检查字符串是否仅包含空白字符,需要更多涉及的模式匹配:

        # Empty string is always blank.
        defp blank?(""), do: true
        
        # If the string's first byte is a space, tab, newline, or carriage return,
        # we recursively check the remaining part of the string.
        defp blank?(<<h, t::binary>>) when h in ' \t\n\r', do: blank?(t)
        
        # Otherwise, it's not blank.
        defp blank?(_), do: false
        

        【讨论】:

        • 不幸的是,这不适用于 NBSP 和 UTF-8 中定义的其他 12 个空格。 String.trim/1 确实如此。
        【解决方案4】:

        Elixir 函数可以有多个函数头。有了这个,创建一个blank?/1 辅助函数就很简单了。

        defp blank?(nil), do: false
        defp blank?(""), do: false
        defp blank?(_), do: true
        

        【讨论】:

        • 这不会通过OP提供的“只有空白字符的字符串”测试。
        • 你是对的。我没有通过所有的测试,我只是看到“但是如果传入的值是 nil 或空字符串”,并将空字符串表示为空。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-11-13
        • 1970-01-01
        • 2017-12-03
        • 2021-04-17
        • 1970-01-01
        相关资源
        最近更新 更多