Is it possible to programmatically pattern match?
Pattern = {error, '_'},
IsError =
case {error, "foo"} of
Pattern -> true;
_ -> false
end.
I know I can do this with macros, but I have a dynamic list of patterns I would like to match that aren't known ahead of time.
Perhaps the closest you can get is using a compiled match specification, by calling the functions ets:match_spec_compile and ets:match_spec_run:
MS = ets:match_spec_compile([{{error, '_'}, [], ['$_']}]).
Items = [ok, {error, foo}, {error, bar}].
ets:match_spec_run(Items, MS).
This returns the two items in the Items list that match:
[{error,foo},{error,bar}]
Related
I have the following functions:
search(DirName, Word) ->
NumberedFiles = list_numbered_files(DirName),
Words = make_filter_mapper(Word),
Index = mapreduce(NumberedFiles, Words, fun remove_duplicates/3),
dict:find(Word, Index).
list_numbered_files(DirName) ->
{ok, Files} = file:list_dir(DirName),
FullFiles = [ filename:join(DirName, File) || File <- Files ],
Indices = lists:seq(1, length(Files)),
lists:zip(Indices, FullFiles). % {Index, FileName} tuples
make_filter_mapper(MatchWord) ->
fun (_Index, FileName, Emit) ->
{ok, [Words]} = file:consult(FileName), %% <---- Line 20
lists:foreach(fun (Word) ->
case MatchWord == Word of
true -> Emit(Word, FileName);
false -> false
end
end, Words)
end.
remove_duplicates(Word, FileNames, Emit) ->
UniqueFiles = sets:to_list(sets:from_list(FileNames)),
lists:foreach(fun (FileName) -> Emit(Word, FileName) end, UniqueFiles).
However, when i call search(Path_to_Dir, Word) I get:
Error in process <0.185.0> with exit value:
{{badmatch,{error,{1,erl_parse,["syntax error before: ","wordinfile"]}}},
[{test,'-make_filter_mapper/1-fun-1-',4,[{file,"test.erl"},{line,20}]}]}
And I do not understand why. Any ideas?
The Words variable will match to content of the list, which might not be only one tuple, but many of them. Try to match {ok, Words} instead of {ok, [Words]}.
Beside the fact that the function file:consult/1 may return a list of several elements so you should replace {ok,[Words]} (expecting a list of one element = Words) by {ok,Words}, it actually returns a syntax error meaning that in the file you are reading, there is a syntax error.
Remember that the file should contain only valid erlang terms, each of them terminated by a dot. The most common error is to forget a dot or replace it by a comma.
Using Erlang, I have the following expression:
{add,{var,a},{mul,{num,2},{var,b}}}
and I am using lists:keymember to see whether the letter b is within the expression as such:
lists:keymember(b,2,[expr])
However, it doesn't look within the third tuple '{mul,{num,2},{var,b}' as that is a separate tuple. Is there a function that will search through the whole tuple and tuples within?
Thanks
As far I as I know there are no such functions. Probably you will have to implement some custom solution using recursion. Here is my example:
-module(test).
-compile(export_all).
find(_, []) -> false;
find(E, T) when is_tuple(T) ->
find(E, tuple_to_list(T));
find(E, [H|T]) ->
case find(E, H) of
false -> find(E, T);
true -> true
end;
find(V, E) -> V == E.
And usage:
1> test:find(b, {add,{var,a},{mul,{num,2},{var,b}}}).
true
2> test:find(b, {add,{var,a},{mul,{num,2},{var,c}}}).
false
Please review your code.
Line1: this is a tree, not a list.
Line2: expr is not a variable.
What you want to do is a visitor function, and you'll have to write it yourself.
A very good start would be to read this.
I am currently playing around with minimal web servers, like Cowboy. I want to pass a number in the URL, load lines of a file, sort these lines and print the element in the middle to test IO and sorting.
So the code loads the path like /123, makes a padded "00123" out of the number, loads the file "input00123.txt" and sorts its content and then returns something like "input00123.txt 0.50000".
At the sime time I have a test tool which makes 50 simultaneous requests, where only 2 get answered, the rest times out.
My handler looks like the following:
-module(toppage_handler).
-export([init/3]).
-export([handle/2]).
-export([terminate/3]).
init(_Transport, Req, []) ->
{ok, Req, undefined}.
readlines(FileName) ->
{ok, Device} = file:open(FileName, [read]),
get_all_lines(Device, []).
get_all_lines(Device, Accum) ->
case io:get_line(Device, "") of
eof -> file:close(Device), Accum;
Line -> get_all_lines(Device, Accum ++ [Line])
end.
handle(Req, State) ->
{PathBin, _} = cowboy_req:path(Req),
case PathBin of
<<"/">> -> Output = <<"Hello, world!">>;
_ -> PathNum = string:substr(binary_to_list(PathBin),2),
Num = string:right(PathNum, 5, $0),
Filename = string:concat("input",string:concat(Num, ".txt")),
Filepath = string:concat("../data/",Filename),
SortedLines = lists:sort(readlines(Filepath)),
MiddleIndex = erlang:trunc(length(SortedLines)/2),
MiddleElement = lists:nth(MiddleIndex, SortedLines),
Output = iolist_to_binary(io_lib:format("~s\t~s",[Filename,MiddleElement]))
end,
{ok, ReqRes} = cowboy_req:reply(200, [], Output, Req),
{ok, ReqRes, State}.
terminate(_Reason, _Req, _State) ->
ok.
I am running this on Windows to compare it with .NET. Is there anything to make this more performant, like running the sorting/IO in threads or how can I improve it? Running with cygwin didn't change the result a lot, I got about 5-6 requests answered.
Thanks in advance!
The most glaring issue: get_all_lines is O(N^2) because list concatenation (++) is O(N). Erlang list type is a singly linked list. The typical approach here is to use "cons" operator, appending to the head of the list, and reverse accumulator at the end:
get_all_lines(Device, Accum) ->
case io:get_line(Device, "") of
eof -> file:close(Device), lists:reverse(Accum);
Line -> get_all_lines(Device, [Line | Accum])
end.
Pass binary flag to file:open to use binaries instead of strings (which are just lists of characters in Erlang), they are much more memory and CPU-friendly.
Two tables are related and I want to write function to delete all records in these two tables, but the output indicates that I can't do that. Is the low efficient choice to delete record one by one is the only available choice?
clear_gyne()->
R = execute_mnesia_transaction(
fun()->
mnesia:clear_table(bas_gyne),
mnesia:clear_table(bas_gyne_property)
end),
R.
execute_mnesia_transaction(TxFun) ->
%% Making this a sync_transaction allows us to use dirty_read
%% elsewhere and get a consistent result even when that read
%% executes on a different node.
%% case worker_pool:submit(
%% fun () ->
Result_a = case mnesia:is_transaction() of
false -> DiskLogBefore = mnesia_dumper:get_log_writes(),
Res = mnesia:sync_transaction(TxFun),
DiskLogAfter = mnesia_dumper:get_log_writes(),
case DiskLogAfter == DiskLogBefore of
true -> Res;
false -> {sync, Res}
end;
true -> mnesia:sync_transaction(TxFun)
end,
case Result_a of
{sync, {atomic, Result}} -> mnesia_sync:sync(), Result;
{sync, {aborted, Reason}} -> throw({error, Reason});
{atomic, Result} -> Result;
{aborted, Reason} -> throw({error, Reason})
end.
execute_mnesia_transaction is copied from rabbitmq project's source code.
The output is
bas_store:clear_gyne().
** exception throw: {error,{aborted,nested_transaction}}
in function bas_store:execute_mnesia_transaction/1 (src/bas_store.erl, line 29)
mnesia:clear_table/1 is categorized into schema transactions, so can not be nested in another transaction.
cf. mnesia:clear_table
http://erlang.org/pipermail/erlang-questions/2005-August/016582.html
I am looking for a way to find tuples in a list in Erlang using a partial tuple, similarly to functors matching in Prolog. For example, I would like to following code to return true:
member({pos, _, _}, [..., {pos, 1, 2}, ...])
This code does not work right away because of the following error:
variable '_' is unbound
Is there a brief way to achieve the same effect?
For simple cases it's better to use already mentioned lists:keymember/3. But if you really need member function you can implement it yourself like this:
member(_, []) ->
false;
member(Pred, [E | List]) ->
case Pred(E) of
true ->
true;
false ->
member(Pred, List)
end.
Example:
>>> member(fun ({pos, _, 2}) -> true; (_) -> false end, [..., {pos, 1, 2}, ...]).
Use lists:keymember/3 instead.
You can do it with a macro using a list comprehension:
-define(member(A,B), length([0 || A <- B])>0).
?member({pos, _, _}, [{width, 17, 42}, {pos, 1, 2}, totally_irrelevant]).
It is not very efficient (it runs through the whole list) but it is the closest I can think to the original syntax.
If you want to actually extract the elements that match you just remove 'length' and add a variable:
-define(filter(A,B), [_E || A =_E <- B]).
You could do it using list comprehension:
Matches = [ Match || {Prefix, _, _} = Match <- ZeList, Prefix == pos].
Another possibility would be to do what match specs do and use the atom '_' instead of a raw _. Then, you could write a function similar to the following:
member(X, List) when is_tuple(X), is_list(List) ->
member2(X, List).
% non-exported helper functions:
member2(_, []) ->
false;
member2(X, [H|T]) when not is_tuple(H); size(X) =/= size(H) ->
member2(X, T);
member2(X, [H|T]) ->
case is_match(tuple_to_list(X), tuple_to_list(H)) of
true -> true;
false -> member2(X, T)
end.
is_match([], []) ->
true;
is_match(['_'|T1], [_|T2]) ->
is_match(T1, T2);
is_match([H|T1], [H|T2]) ->
is_match(T1, T2);
is_match(_, _) ->
false.
Then, your call would now be:
member({pos, '_', '_'}, [..., {pos, 1, 2}, ...])
This wouldn't let you match patterns like {A, A, '_'} (checking where the first two elements are identical), but if you don't need variables this should work.
You could also extend it to use variables using a similar syntax to match specs ('$1', '$2', etc) with a bit more work -- add a third parameter to is_match with the variable bindings you've seen so far, then write function clauses for them similar to the clause for '_'.
Granted, this won't be the fastest method. With the caveat that I haven't actually measured, I expect using the pattern matching in the language using a fun will give much better performance, although it does make the call site a bit more verbose. It's a trade-off you'll have to consider.
May use ets:match:
6> ets:match(T, '$1'). % Matches every object in the table
[[{rufsen,dog,7}],[{brunte,horse,5}],[{ludde,dog,5}]]
7> ets:match(T, {'_',dog,'$1'}).
[[7],[5]]
8> ets:match(T, {'_',cow,'$1'}).
[]