Is it a list ?

Luke Gorrie luke@REDACTED
Thu Jul 10 23:21:22 CEST 2003


Marc Ernst Eddy van Woerkom <Marc.Vanwoerkom@REDACTED> writes:

> >   and experience: things that have proved to have unfortunate consequences 
> >   tend to be called illegitimate by later generations.
> 
> It's old cruft, if I understood you and Luke right.

Before drawing conclusions, there are some interesting uses of
improper* lists that are fun to consider.

We all know Erlang's pattern matching, like [A,B,C] = [1,2,3]. But
suppose you want to match patterns that are created at runtime, by
writing an interpreter for pattern matching instead. The pattern
interpreter takes a data structure representing a pattern and a term
to match it with, and returns a list of variable bindings (if it
matches successfully) or the atom 'nomatch'.

Consider a simple form of pattern -- any atom is a variable, lists are
matched recursively, and anything else has to match by
value. Examples:

  match(['A','B','C'], [1,2,3]) => [{A,1},{B,2},{C,3}]
  match(12345,         12345)   => []
  match(['A','B','C'], foo)     => nomatch

Here comes a simple implementation. It will recurse down lists, match
variables with values (if used multiple times, the values must agree),
and match anything else by value.

  match(Pattern, Object) -> match(Pattern, Object, []).

  %% match(Pattern, Object, Bindings) => Bindings' | nomatch
  match([P|Ps], [X|Xs], Bs) ->            % matching a list..
      match(Ps, Xs, match(P, X, Bs));
  match(_, _, nomatch) ->
      %% This tricky clause is to handle the previous one's recursion --
      %% it will pass 'nomatch' instead of a bindings list if the first
      %% element of a list doesn't match!
      nomatch;
  match(P, X, Bs) when atom(P) ->         % matching a variable..
      case lists:keysearch(P, 1, Bs) of   % is it bound?
          false           -> [{P,X}|Bs];  % no -- create binding
          {value, {P, X}} -> Bs;          % yes -- existing binding matches
          {value, {P, _}} -> nomatch      % yes -- incompatible binding!
      end;
  match(X, X, Bs) ->                      % successful match of a constant..
      Bs;
  match(_, _, _) ->                       % failure!
      nomatch.

Cute function I think! I knocked it off from the Lisp literature.

Now, suppose you wanted to extend the pattern matcher to handle
patterns like [A,B|C]. How would you do it? (Drum roll..) it already
works!

  match(['A','B'|'C'], [1,2,3,4,5]) -> [{'A',1},{'B',2},{'C',[3,4,5]}]

But... how would you code this if you couldn't use improper lists to
represent patterns?

NB: At a glance, it looks like the shell's erlang interpreter
    (erl_eval) works basically like this.

NB2: I cheated in the examples above -- the function actually returns
     bindings in "reverse" order.

[*]: I think there's a more neutral name for "improper" lists. Anyone
     know it?

Cheers,
Luke




More information about the erlang-questions mailing list