Matching strings in LFE patterns
Eric Bailey
Written on 28 December, 2015
Updated on 18 December, 2023
Tags: lfe, lisp, beam, pattern-matching, open-source
While writing an LFE solution for Day 6 of Advent of Code, I found myself
wanting to write parse_instruction/1
like this:
parse_instruction("toggle " ++ Rest) -> toggle(parse_coordinates(Rest)); parse_instruction("turn on " ++ Rest) -> turn_on(parse_coordinates(Rest)); parse_instruction("turn off " ++ Rest) -> turn_off(parse_coordinates(Rest)). parse_coordinates(String) -> {ok,[X0,Y0,X1,Y2],[]} = io_lib:fread("~d,~d through ~d,~d", String), {{X0,Y0},{X1,Y1}}. toggle({{X0,Y0},{X1,Y1}}) -> undefined. turn_on({{X0,Y0},{X1,Y1}}) -> undefined. turn_off({{X0,Y0},{X1,Y1}}) -> undefined.
Trying it out
But the literal LFE translation doesn't work as desired.
(defun parse-instruction ([(++ "turn off " rest)] ...))
N.B. In v0.10.1, exp_append/1
had the following clause, commented out.
%% Cases with lists of numbers (strings).
[[N|Ns]|Es] when is_number(N) -> [cons,N,['++',Ns|Es]];
Instead, invocation of a defun
of that form throws a function_clause
error.
> (defun f ([(++ "prefix" suffix)] suffix)) f > (f "prefixsuffix") exception error: function_clause
Adding ++*
patterns to LFE
After this discovery, I took to #erlang-lisp and tried to figure out why.
Discussing the issue with @rvirding
for a few minutes, we decided adding ++*
and having patterns like (++* "prefix" suffix)
expand to nested cons
-es was
a solid approach.
Rather than take the overly complicated approach of counting and limiting the
number of expanded cons
-es and bottoming out to a call to erlang:++
, we
decided to keep it simple and just let ++*
patterns do their own thing.
The solution we came up with is as follows:
%% exp_predef(...) -> ...; exp_predef(['++*'|Abody], _, St) -> Exp = exp_prefix(Abody), {yes,Exp,St}; %% exp_predef(...) -> .... exp_prefix([[N|Ns]|Es]) when is_number(N) -> [cons,N,['++*',Ns|Es]]; exp_prefix([[]|Es]) -> ['++*'|Es]; exp_prefix(Args) -> exp_append(Args).
Usage
Now in the develop branch, you can do the following:
> (defun f ([(++* "prefix" suffix)] suffix)) f > (f "prefixsuffix") "suffix"
or even:
> (defun f ([(++* "p" "r" "e" "f" "i" "x" suffix)] suffix)) f > (f "prefixsuffix") "suffix"