0

I have started learning Erlang recently and came across the following error while trying to pattren match

The following expression is working fine:

{A,_,[B|_],{B}}={abc,23,[22,x],{22}}.
Resulting in
A = abc
B = 22

The following expression is not working:

{A,_,[_|B],{B}}={abc,23,[22,x],{x}}.
Is resulting in 
** exception error: no match of right hand side value {abc,23,[22,x],{x}}

However if I replace the ',' in [22 , x with a | like the following its working find and bounding x to B

{A,_,[_|B],{B}}={abc,23,[22|x],{x}}.
{abc,23,[22|x],{x}}
B.
x

Any explanation about this would highly appreciated.

Many thanks in advance

  • 1
    [_|Whatever] can mean literally *anything*, so it can't be matched, because `_` can consume/match anything, including the entire element of the match. That means "Anything at all, followed by `Whatever` -- which we don't define here". `[22|Whatever]` or any `[FiniteThing|_]` means "something concrete and limited, followed by `Whatever`", which can be matched because it can match and consume the first part of the list exactly. IOW, "anything" matches/consumes *everything*, whereas a specific and limited thing can only represent itself. – zxq9 Nov 14 '14 at 16:36

2 Answers2

1

You need to look closer on how does the | operator works. It basically takes head of list, which is one element, and returns tail of list, which is all the rest. And like "all" suggest tail is also a list. It could be one element list, it could be even empty list, but still it's gonna be a list.

> [Head| Tail] = [23,x].
[23,x]
> Head.
23
> Tail
[x].

So in your pattern matching, you assign to be tail [x], and than try to pattern match on simply x. And that's what's failing.

On side note: you can create new list with | operator, but you should do this with caution. since you could create improper list (and you do with [23 | x]). That's why your "fix" is working.

If you would like to match on two element list, you could do it explicitly with

[A, B] =  [23, x].

but this will fail if list have more or less elements.

If you would like to match on only on two first elements, you can still use | operator.

> [A, B | Rest] = [23, x].
[23, x]
> A.
23
> B.
x
> Rest.
[].

And this will fail only with one-element or empty list.

Community
  • 1
  • 1
mpm
  • 3,534
  • 23
  • 33
1

The operator | is used for a recursive definition of a list: [A|B] means that you add the element A to an existing list B. A is the first element of the resulting list, called the head, B is the rest of the list called tail. B can be also split into a head and a tail, and the process can continue until the tail is equal to the empty list [].

The operator , is a separator between list elements, so [A,B] is a list of 2 elements A and B.

The 2 operators can be combined: [A,B,C|D] is a list of at least 3 elements, which are A, B and C, and a tail D which can be empty.

In your test you used another syntax: [23|x]; 23 can be an element of a list (in fact any erlang term can be an element of a list) but x is an atom and cannot be a list tail. Doing this you broke the recursive definition of the list, this structure is not often used and is called an improper list.

  • when you match [_|B] and [_,x], you assign [x] to B which do not match to x later in the expression

  • when you match [_|B] and [_|x], you assign x to B which indeed match to x later in the expression, but the right way should be

  • {A,_,[_|B],{B}}={abc,23,[22,x],{[x]}}.

Pascal
  • 13,977
  • 2
  • 24
  • 32