我正在遍历一个字符串列表,如果字符串的开头与提供的字符串匹配,我想返回字符串的内容。
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
哪个有道理,但后来我不太确定如何去做。如何将子字符串与作为参数提供的其他变量匹配?
答案 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;我得到一个新的List。 Enum.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