henrique-marcomini-m
How to pattern match (or guards) if a string is alphanumeric
I have the following code:
defp has_char_in_string?(value), do: Regex.match?(~r/[^\d]/, value)
def somefun(arg) do
case has_char_in_string?(arg) do
true -> foo()
false -> bar()
end
end
And I really want to keep this regex within this module and do not externalize this logic. But I also want to use pattern matching or guards instead of a case. Is this even possible? And if it is, how?
Thanks in advance
Most Liked
peerreynders
Probably not what you are looking for:
def somefun(true),
do: foo()
def somefun(false),
do: bar()
def somefun(arg) when is_binary(arg),
do: Regex.match?(~r/[^\d]/, arg)
|> somefun()
or
defp p_somefun(true),
do: foo()
defp p_somefun(false),
do: bar()
def somefun(arg) when is_binary(arg) do
Regex.match?(~r/[^\d]/, arg)
|> p_somefun()
end
al2o3cr
It’s possible, especially if the regular expression is simple - you can translate the regex into its corresponding finite state machine and represent that with pattern matching:
defmodule RegexRecursion do
def somefun(arg) do
call_bar(arg)
end
defp call_bar("0" <> rest), do: call_bar(rest)
defp call_bar("1" <> rest), do: call_bar(rest)
defp call_bar("2" <> rest), do: call_bar(rest)
defp call_bar("3" <> rest), do: call_bar(rest)
defp call_bar("4" <> rest), do: call_bar(rest)
defp call_bar("5" <> rest), do: call_bar(rest)
defp call_bar("6" <> rest), do: call_bar(rest)
defp call_bar("7" <> rest), do: call_bar(rest)
defp call_bar("8" <> rest), do: call_bar(rest)
defp call_bar("9" <> rest), do: call_bar(rest)
defp call_bar(""), do: bar()
defp call_bar(x) when is_binary(x), do: foo()
defp foo(), do: IO.puts("foo")
defp bar(), do: IO.puts("bar")
end
Here, the regex [^\d] translates to a state machine that stays in call_bar as long as each character is 0-9, actually calls bar given an empty string, and calls foo otherwise.
If the regex doesn’t involve backreferences or lookaheads (so it’s a theory-of-languages regular expression), it’s always possible to do this.
HOWEVER
The example above is a good example of how this approach obfuscates what should have been code like this:
defmodule RegexRecursionSimple do
def somefun(arg) when is_binary(arg) do
if Regex.match?(~r/[^\d]/, arg) do
foo()
else
bar()
end
end
defp foo(), do: IO.puts("foo")
defp bar(), do: IO.puts("bar")
end
I’m very curious what’s motivating the preference for pattern matching here; it’s not the right tool for the job.
hauleth
TBH I would say that Regex is overkill there. And your pattern match can be improved:
def somefun(<<num>> <> rest) when num in ?0..?9, do: call_bar(rest)
def somefun(""), do: bar()
def somefun(bin) when is_binary(bin), do: foo()
The problem with regular expressions the use backtracking engine (and PCRE is such engine) is that it can explode to O(n^2) with some expressions (namely nested wildcard matches). And such cases have been spotted on the wild with pretty simple expressions.
NobbZ
No, there is no way to make this regex or even something semantically equivalent into a guardsave predicate.
kip
I have a package called ex_cldr_unicode that includes guards for
These all operate on the Unicode character classes so it covers what passes for a digit in a more complete sense. It might help or give you some ideas.
Note that it works on code points since there is a limited set of underlying functions that can be used in guards.
There is another bunch of functions that might be helpful, including Cldr.Unicode.alphanumeric?/1 which will return a boolean and also uses the full Unicode definitions (not just Latin1):
iex> Cldr.Unicode.alphanumeric? "1st"
true
iex> Cldr.Unicode.alphanumeric? "KeyserSöze1995"
true
iex> Cldr.Unicode.alphanumeric? "3段"
true







