Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pattern matching on binary type cannot use the value of a variable

Tags:

erlang

elixir

E.g:

arg = "echo:hello"
prefix = "echo"

case arg do
  <<^prefix, ":", msg::binary>> -> IO.puts("Echo message: #{msg}")
  _ -> IO.puts("No match")
end

Result: No match

What if I want to use the value of prefix as a pattern match?

like image 395
Hentioe Avatar asked Aug 17 '18 18:08

Hentioe


People also ask

What is the use of pattern matching?

Pattern matching is used to determine whether source files of high-level languages are syntactically correct. It is also used to find and replace a matching pattern in a text or code with another text/code. Any application that supports search functionality uses pattern matching in one way or another.

What is binary matching?

The binary string matching problem consists in finding all the occurrences of a pattern in a text where both strings are built on a binary alphabet. This is an interesting problem in computer science, since binary data are omnipresent in telecom and computer network applications.

What is pattern matching syntax?

Patterns are written in JME syntax, but there are extra operators available to specify what does or doesn't match. The pattern-matching algorithm uses a variety of techniques to match different kinds of expression.

Does C# have pattern matching?

C# pattern matching provides more concise syntax for testing expressions and taking action when an expression matches. The " is expression" supports pattern matching to test an expression and conditionally declare a new variable to the result of that expression.


1 Answers

This won't work because you can only match fixed size binaries if you're not matching the "rest" of the string. There are 3 different solutions, depending on your use-case

1. Calculate the size beforehand

If you really want to use that binary pattern matching, you can manually calculate the size beforehand:

arg = "echo:hello"
prefix = "echo"
prefix_size = byte_size(prefix)

case arg do
  <<^prefix::binary-size(prefix_size), ":", msg::binary>> -> IO.puts("Echo message: #{msg}")
  _ -> IO.puts("No match")
end

2. Use compile-time module attributes

Depending on your use case, you could use module attributes, which size is known at compile time so they do work:

defmodule MyModule do

  @prefix "echo"

  def test(arg) do
    case arg do
      <<@prefix::binary, ":", msg::binary>> -> IO.puts("Echo message: #{msg}")
      _ -> IO.puts("No match")
    end
  end
end

3. Use String.replace_prefix/3

Or, if you want to keep prefix a runtime binary, you could use String.replace_prefix/3

arg = "echo:hello"
prefix = "echo"

case String.replace_prefix(arg, "#{prefix}:", "")do
  ^arg -> IO.puts("No match")
  msg -> IO.puts("Echo match: #{msg}") 
end

String.replace_prefix/3 returns the input string if there is no match found, so we match it via ^arg. If this is not the case, we got a match and since we replaced the prefix with "", we just get the part after the :.

like image 54
Jonas Dellinger Avatar answered Oct 09 '22 08:10

Jonas Dellinger