Elixir:模式匹配函数的两个相同参数

时间:2018-01-25 08:01:06

标签: erlang elixir

我正在遍历一个字符串列表,如果字符串的开头与提供的字符串匹配,我想返回字符串的内容。

e.g。

strings = [ "GITHUB:github.com", "STACKOVERFLOW:stackoverflow.com" ]
IO.puts fn(strings, "GITHUB") // => "github.com"

这是我到目前为止的想法:

def get_tag_value([ << tag_name, ": ", tag_value::binary >> | rest ], tag_name), do: tag_value

def get_tag_value([ _ | rest], tag_name), do: get_tag_value(rest, tag_name)

def get_tag_value([], tag_name), do: ""

但我明白了:

** (CompileError) lib/file.ex:31: a binary field without size is only allowed at the end of a binary pattern and never allowed in binary generators

哪个有道理,但后来我不太确定如何去做。如何将子字符串与作为参数提供的其他变量匹配?

5 个答案:

答案 0 :(得分:2)

有很多方法可以给这只猫上皮。

例如:

def get_tag_value(tag, strings) do
  strings
  |> Enum.find("", &String.starts_with?(&1, tag <> ":"))
  |> String.split(":", parts: 2)
  |> Enum.at(1, "")
end

或者如果您仍想明确使用递归:

def get_tag_value(_tag, []), do: ""

def get_tag_value(tag, [str | rest]) do
  if String.starts_with?(str, tag <> ":") do
    String.split(str, ":", parts: 2) |> Enum.at(1, "")
  else
    get_tag_value(tag, rest)
  end
end

只是众多可能方式中的两种。

但是,如果事先知道它(或至少是长度),你将无法在函数头中匹配字符串。

答案 1 :(得分:2)

以下是我如何使用模式匹配并且不调用String.starts_with?String.split

defmodule A do
  def find(strings, string) do
    size = byte_size(string)
    Enum.find_value strings, fn
      <<^string::binary-size(size), ":", rest::binary>> -> rest
      _ -> nil
    end
  end
end

strings = ["GITHUB:github.com", "STACKOVERFLOW:stackoverflow.com"]

IO.inspect A.find(strings, "GITHUB")
IO.inspect A.find(strings, "STACKOVERFLOW")
IO.inspect A.find(strings, "GIT")
IO.inspect A.find(strings, "FOO")

输出:

"github.com"
"stackoverflow.com"
nil
nil

答案 2 :(得分:0)

iex(1)> strings = [ "GITHUB:github.com", "STACKOVERFLOW:stackoverflow.com" ]

iex(2)> Enum.filter(strings, fn(s) -> String.starts_with?(s, "GITHUB") end)
iex(3)> |> Enum.map(fn(s) -> [_, part_2] = String.split(s, ":"); part_2 end)

# => ["github.com"]

Enum.filter/2中,我选择他们开头的所有字符串&#34; GITHUB&#34;我得到一个新的ListEnum.map/2遍历新的List并在冒号处拆分每个字符串以仅返回第二部分。结果是List,其中所有部分都在冒号后面,原始字符串以&#34; GITHUB&#34;开头。

请注意,如果有像&#34; GITHUBgithub.com&#34;没有冒号,你得到一个MatchError。要避免这种情况,请使用String.starts_with?(s, "GITHUB:")过滤正确的字符串或避免模式匹配,就像我在Enum.map/2中所做的那样,或者使用模式匹配来获取像@ryanwinchester这样的空列表。

答案 3 :(得分:0)

您可以使用Enum.map和Enum.filter的组合来获取您正在寻找的匹配对:

def get_tag_value(tag_name, tags) do
  tags
  |> Enum.map(&String.split(&1, ":")) # Creates a list of [tag_name, tag_value] elements
  |> Enum.filter(fn([tn, tv]) -> tn == tag_name end) # Filters for the tag name you're after
  |> List.last # Potentially gets you the pair [tag_name, tag_value] OR empty list
end

最后,您可以再次拨打List.last/1以获取空列表(找不到匹配项)或标记值。

或者,您可以使用case语句返回不同类型的结果,例如:nomatch原子:

def get_tag_value(tag_name, tags) do
  matches = tags
    |> Enum.map(&String.split(&1, ":")) # Creates a list of [tag_name, tag_value] elements
    |> Enum.filter(fn([tn, tv]) -> tn == tag_name end) # Filters for the tag name you're after
    |> List.last # Potentially gets you the pair [tag_name, tag_value] OR empty list
  case matches do
    [] -> :nomatch
    [_, tag_value] -> tag_value
  end
end

答案 4 :(得分:0)

这是我对Erlang的看法:

get_tag_value(Tag, Strings) ->
    L = size(Tag),
    [First | _] = [Val || <<Tag:L/binary, $:, Val/binary>> <- Strings]
    First.

在Elixir中也是如此(可能有更多惯用的写法,tho):

def gtv(tag, strings) do
  l = :erlang.size(tag)
  [first | _ ] =
    for << t :: binary - size(l), ":", value :: binary >> <- strings,
        t == tag,
        do: value
  first
end