Elixir: pattern matching works differently for tuples and maps

前端 未结 2 1405
孤城傲影
孤城傲影 2021-02-07 14:44

In Elixir, if I try to pattern match the following two tuples:

{a} = {1, 2}

I get a match error. But if I do the same for two maps:

<         


        
相关标签:
2条回答
  • 2021-02-07 15:05

    maps, the main key-value store in Elixir, have an interesting capability that sets them apart from other data structures with regards to pattern-matching.

    A map can actually pattern-match on just a subset of a value. The key(s) in the pattern have to exist in the match, but the two structures don’t have to mirror each other in the same way a list or a tuple has to. For example:

    iex(1)> [a, b] = [1, 2, 3]
    ** (MatchError) no match of right hand side value: [1, 2, 3]
    
    iex(1)> {a, b} = {1, 2, 3}
    ** (MatchError) no match of right hand side value: {1, 2, 3}
    
    iex(1)> %{:a => one} = %{:a => 1, :b => 2, :c =>3}
    %{a: 1, b: 2, c: 3}
    iex(2)> one
    1
    iex(3)> %{:a => one, :c => three} = %{:a => 1, :b => 2, :c =>3}
    %{a: 1, b: 2, c: 3}
    iex(4)> three
    3
    iex(5)> one
    1
    iex(6)> %{} = %{:a => 1, :b => 2, :c =>3}
    %{a: 1, b: 2, c: 3}
    iex(7)> %{:d => four} = %{:a => 1, :b => 2, :c =>3}
    ** (MatchError) no match of right hand side value: %{a: 1, b: 2, c: 3}
    
    iex(8)> %{:a => one, :d => four} = %{:a => 1, :b => 2, :c =>3}
    ** (MatchError) no match of right hand side value: %{a: 1, b: 2, c: 3}
    

    We can see that neither the list nor the tuple matches if the data structure of the pattern is different to the data structure of the match, namely the size. Whereas this is not the case for the map. Because the key is present in both the pattern and the match then the match is successful, regardless of the different sizes of the pattern and the match. An empty map will also match.

    However the match will not be successful if the key in the pattern is not in the match. This is also the case even if there are matching keys. So any key used in the pattern has to be present in the match.

    You will see this sort of matching extensively in Phoenix Framework to pick up only the necessary params from request.

    Source: - Notes on Elixir: Pattern-Matching Maps

    0 讨论(0)
  • 2021-02-07 15:23

    In the first example you are attempting to match a single element tuple against a two-element tuple. In the second example you are matching on the :x key in both the left and right maps.

    EDIT: I should clarify the rules around data structures and pattern matching in Elixir.

    When matching on tuples, you need to provide a pattern which matches the entire tuple structure. You can use _, which is the "catch-all" pattern, but you'll still need to provide it for all elements of the tuple. When matching on lists, you must match on either all elements when using the [a, b, c] = [1, 2, 3] syntax, or on the head and tail, using the [h|t] = [1, 2, 3] syntax. When matching maps though, you can match on one or more keys in the map, which gives you the %{a: b} = %{a: :foo, b: :bar} syntax.

    The semantics are a bit different between data structures, but are fairly common sense. The tuple rule exists because two tuples cannot be the same unless they have the same number of elements, a list has the same limitation, but because of the semantics of lists, accessing the head element of the list is the most common operation when working with them, hence the [h|t] syntax. Maps however can match based on specific keys, so the number of elements are irrelevant, as long as both sides of the match contain the same key, and optional pattern for the value, then it's a successful match.

    0 讨论(0)
提交回复
热议问题