erlang ets select bad arg - erlang

erlang version 18.3
Got an strange error with Erlang ets:select/1
the following code will do select element from table and take them .
if I do
save(10), %% insert 10 data
remove(3) %% remove 3 data per time
it works
if I do
save(6007), %% insert more datas
remove(400) %% remove 400 data per time
it was bad arg in ets:select(Cont) also, it was not the in the first or second loop, but was always there.
any suggestion?
-record(item, {name, age}).
%% start the table
start() ->
ets:new(example_table, [public, {keypos, 2},
named_table,
{read_concurrency, true},
{write_concurrency, true}]).
%% insert n demo data
save(Limit) ->
All = lists:seq(1 ,Limit),
All_rec = [#item{name = {<<"demo">>, integer_to_binary(V)} , age = V} || V <- All],
ets:insert(example_table, All_rec).
%% remove all data, n data per select
remove(Limit) ->
M_head = #item{name = '$1', _ = '_'},
M_guards = [],
M_result = ['$1'],
M_spec = [{M_head, M_guards, M_result}],
case ets:select(example_table, M_spec, Limit) of
'$end_of_table' ->
0;
{Keys, Cont} ->
remove(example_table, Keys, Cont, 0, [])
end.
remove(Table, [], Cont, Count, _Acc) ->
case ets:select(Cont) of
'$end_of_table' ->
Count;
{Keys, Cont_1} ->
remove(Table, Keys, Cont_1, Count, [])
end;
remove(Table,[Key | T], Cont, Count, Acc) ->
case ets:take(example_table, Key) of
[] ->
remove(Table, T, Cont, Count, Acc);
[Rec] ->
io:format("Rec [~p] ~n", [Rec]),
remove(Table, T, Cont, Count + 1, [Rec | Acc])
end.
stack trace
4> example_remove:save(6007).
true
5> example_remove:remove(500).
** exception error: bad argument
in function ets:select/1
called as ets:select({example_table,304,500,<<>>,
[{<<"demo">>,<<"2826">>},
{<<"demo">>,<<"3837">>},
{<<"demo">>,<<"5120">>},
{<<"demo">>,<<"878">>},
{<<"demo">>,<<"1195">>},
{<<"demo">>,<<"1256">>},
{<<"demo">>,<<"1449">>},
{<<"demo">>,<<"5621">>},
{<<"demo">>,<<"5768">>}],
9})
in call from example_remove:remove/5 (d:/workspace/simple-cache/src/example_remove.erl, line 47)

I believe this happens because you simultaneously iterate over the table and modify it.
I suggest wrapping main remove cycle with guards of safe_fixtable
remove(Limit) ->
ets:safe_fixtable(example_table, true),
M_head = #item{name = '$1', _ = '_'},
M_guards = [],
M_result = ['$1'],
M_spec = [{M_head, M_guards, M_result}],
R = case ets:select(example_table, M_spec, Limit) of
'$end_of_table' ->
0;
{Keys, Cont} ->
remove(example_table, Keys, Cont, 0, [])
end,
ets:safe_fixtable(example_table, false),
R.

Related

Warning: use of operator '<' has no effect?

I am new to erlang and am trying to implement a simple function as follows:
% * ChatServers is a dictionary of usernames with tuples of the form:
% {server, Pid, Reference,LoggedInUsers}
get_chat_server([], _) ->
undefined;
get_chat_server([Key|_], ChatServers) ->
{server, Pid, Reference,LoggedInUsers} = dict:fetch(Key,ChatServers),
LoggedInUsers < 100,
{server, Pid, Reference,LoggedInUsers};
get_chat_server([_|T], ChatServers) ->
get_chat_server(T, ChatServers).
Basically what I am trying to do is find the first tuple of my dictionary whose LoggedInUsers number is less than 100.
However, once I compile my code, I get the following 2 warnings:
main_server_distributed.erl:63: Warning: use of operator '<' has no
effect main_server_distributed.erl:66: Warning: this clause cannot
match because a previous clause at line 61 always matches
I have some experience with prolog and as far as I recall this is a valid use of pattern matching and recursion. Could you please point out what am I doing wrong here? Thanks in advance.
The body of a clause (everything to the right of the ->) is not a list of conditions to fulfil, but simply a comma-separated list of expressions to evaluate. All resulting values except from that of the last expression will be discarded. Hence, the boolean value of your < comparison is not used anywhere.
You can do something like this...
get_chat_server([], _) ->
undefined;
get_chat_server([Key|T], ChatServers) ->
{server, Pid, Reference,LoggedInUsers} = dict:fetch(Key,ChatServers),
if
LoggedInUsers < 100 ->
{server, Pid, Reference,LoggedInUsers};
true ->
get_chat_server(T, ChatServers)
end.
Or this
get_chat_server([], _) ->
undefined;
get_chat_server([Key|T], ChatServers) ->
Result = dict:fetch(Key,ChatServers),
case Result of
{_, _, _, LoggedInUsers} when LoggedInUsers < 100 ->
Result;
_ ->
get_chat_server(T, ChatServers)
end.
main_server_distributed.erl:66: Warning: this clause cannot match
because a previous clause at line 61 always matches
You've essentially written:
get_chat_server(NonEmptyList, ChatServers) ->
{server, Pid, Reference,LoggedInUsers} = dict:fetch(Key,ChatServers),
LoggedInUsers < 100,
{server, Pid, Reference,LoggedInUsers};
get_chat_server(NonEmptyList, ChatServers) ->
get_chat_server(T, ChatServers).
Therefore, the first clause will always match anything that the second clause would have matched. More specifically, in the pattern:
[Key|_]
Key will match anything and _ will match anything. Likewise, in the pattern:
[_|T]
_ will match anything, and T will match anything.
Riffing off #dsmith's answer:
-module(my).
-export([get_chat_server/3, get_chat_server_test/0]).
get_chat_server(_MaxLoggedIn, []=_Keys, _ChatServers) ->
none;
get_chat_server(MaxLoggedIn, [Key|Keys], ChatServers) ->
get_chat_server(MaxLoggedIn, Keys, ChatServers, dict:fetch(Key, ChatServers) ).
get_chat_server(MaxLoggedIn, _, _, {_,_,_,LoggedInUsers}=ChatServer) when LoggedInUsers < MaxLoggedIn ->
ChatServer;
get_chat_server(MaxLoggedIn, [Key|Keys], ChatServers, _ChatServer) ->
get_chat_server(MaxLoggedIn, Keys, ChatServers, dict:fetch(Key, ChatServers) ).
%---------
get_chat_server_test() ->
Keys = [a, c],
ChatServers = [
{a, {server, a, a_, 200}},
{b, {server, b, b_, 100}},
{c, {server, c, c_, 30}}
],
ChatServerDict = dict:from_list(ChatServers),
none = get_chat_server(10, [], ChatServerDict),
{server, c, c_, 30} = get_chat_server(50, Keys, ChatServerDict),
{server, c, c_, 30} = get_chat_server(150, Keys, ChatServerDict),
PossibleResults = sets:from_list([{server,a,a_, 200},{server,c,c_,30}]),
true = sets:is_element(
get_chat_server(250, Keys, ChatServerDict),
PossibleResults
),
all_tests_passed.
You can also use higher order functions, i.e. dict:fold(), to get a list of all the ChatServers that meet your requirements:
max_fun(Max, Keys) ->
fun(Key, {_,_,_,LoggedInUsers}=Server, Acc) ->
case lists:member(Key, Keys) andalso LoggedInUsers<Max of
true -> [Server | Acc];
false -> Acc
end
end.
In the shell:
44> ChatServers = [
44> {a, {server, a, a_, 200}},
44> {b, {server, b, b_, 100}},
44> {c, {server, c, c_, 30}}
44> ].
[{a,{server,a,a_,200}},
{b,{server,b,b_,100}},
{c,{server,c,c_,30}}]
45> ChatServerDict = dict:from_list(ChatServers).
{dict,3,16,16,8,80,48,
{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},
{{[],
[[a|{server,a,a_,200}]],
[[b|{server,b,b_,100}]],
[[c|{server,c,c_,30}]],
[],[],[],[],[],[],[],[],[],[],[],[]}}}
46> Keys = [a,c].
[a,c]
47> MaxLoggedIn = 150.
150
50> F = my:max_fun(MaxLoggedIn, Keys).
#Fun<fl.0.128553666>
51> dict:fold(F, [], ChatServerDict).
[{server,c,c_,30}]

How to divide a string into substrings?

I would like to divide a string to sub-strings based on a given number , for example:
divide("string",1) = ["s","t","r","i","n","g"].
I have tried this, but no success .
lists:split(1,"string") = {"s", "tring"}
Any idea?
I would calculate the length once (since it's a slow operation) and then recursively use lists:split/2 until the list left is smaller than N:
divide(List, N) ->
divide(List, N, length(List)).
divide(List, N, Length) when Length > N ->
{A, B} = lists:split(N, List),
[A | divide(B, N, Length - N)];
divide(List, _, _) ->
[List].
1> c(a).
{ok,a}
2> a:divide("string", 1).
["s","t","r","i","n","g"]
3> a:divide("string", 2).
["st","ri","ng"]
4> a:divide("string", 3).
["str","ing"]
5> a:divide("string", 4).
["stri","ng"]
6> a:divide("string", 5).
["strin","g"]
7> a:divide("string", 6).
["string"]
8> a:divide("string", 7).
["string"]
I think #Dogbert solution is currently the best... But here an other implementation example with recursive loop.
divide_test() ->
[?assertEqual(divide("string",1), ["s","t","r","i","n","g"]),
?assertEqual(divide("string",2), ["st","ri","ng"]),
?assertEqual(divide("string",3), ["str","ing"]),
?assertEqual(divide("string",4), ["stri","ng"])
].
-spec divide(list(), integer()) -> list(list()).
divide(String, Size)
when is_list(String), is_integer(Size) ->
divide(String, Size, 0, [], []).
-spec divide(list(), integer(), integer(), list(), list()) -> list(list()).
divide([], _, _, Buf, Result) ->
Return = [lists:reverse(Buf)] ++ Result,
lists:reverse(Return);
divide([H|T], Size, 0, Buf, Result) ->
divide(T, Size, 1, [H] ++ Buf, Result);
divide([H|T], Size, Counter, Buf, Result) ->
case Counter rem Size =:= 0 of
true ->
divide(T, Size, Counter+1, [H] ++ [], [lists:reverse(Buf)] ++ Result);
false ->
divide(T, Size, Counter+1, [H] ++ Buf, Result)
end.
You can try this function. provided the number is > 0 less than or equal to string length divided by two.
first_substring(List, Separator) ->
first_substring_loop(List, Separator, []).
first_substring_loop([], _, Reversed_First) ->
lists:reverse(Reversed_First);
first_substring_loop(List, Separator, Reversed_First) ->
[H|T]= my_tuple_to_list(lists:split(Separator,List)),
first_substring_loop(lists:flatten(T), Separator, [H|Reversed_First]).
my_tuple_to_list(Tuple) -> [element(T, Tuple) || T <- lists:seq(1, tuple_size(Tuple))].
the result is
1> fact:first_substring("string", 1).
["s","t","r","i","n","g"]
2> fact:first_substring("string", 2).
["st","ri","ng"]
3> fact:first_substring("string", 3).
["str","ing"]
A short simple solution can be:
divide(String, Length) -> divide(String, Length, []).
divide([], _, Acc) -> Acc;
divide(String, Length, Acc) ->
{Res, Rest} = lists:split(min(Length, length(String)), String),
divide(Rest, Length, Acc ++ [Res]).
Also for a specific case of splitting with length 1, a list comprehension can be used:
ListOfLetters = [[Letter] || Letter <- String].

Remove list element occur only once

I have a list in erlang containing interger values.
I want to remove values that occur only one time.(Not Duplicates).
Input = [1,3,2,1,2,2]
Output = [1,2,1,2,2]
I am newbie to erlang. I have tried an approach to sorting them first using list:sort() and then removing a member if the member next to it is the same.
I am having trouble trying to iterate the list. It would be great help if you can show me how I can do it.
multiple(L) ->
M = L -- lists:usort(L),
[X || X <- L , lists:member(X,M)].
Use map to count values and then filter values which was not present just once.
-module(test).
-export([remove_unique/1]).
remove_unique(L) ->
Count = lists:foldl(fun count/2, #{}, L),
lists:filter(fun(X) -> maps:get(X, Count) =/= 1 end, L).
count(X, M) ->
maps:put(X, maps:get(X, M, 0) + 1, M).
And test:
1> c(test).
{ok,test}
2> test:remove_unique([1,2,3,3,3,5,5,6,7,7]).
[3,3,3,5,5,7,7]
3> test:remove_unique([1,2,3,3,3,5,5,6,7,8]).
[3,3,3,5,5]
4> test:remove_unique([1,3,2,1,2,2]).
[1,2,1,2,2]
Here's a solution I'd written when first seeing the question when posted, that uses the same logic as #A.Sarid's recursion/pattern matching answer, except that I use a "Last" parameter instead of the count.
-module(only_dupes).
-export([process/1]).
process([]) -> [];
process(L) when is_list(L) ->
[H|T] = lists:sort(L),
lists:sort(process(undefined, H, T, [])).
process(Last, Curr, [], Acc)
when Curr =/= Last ->
Acc;
process(_Last, Curr, [], Acc) ->
[Curr | Acc];
process(Last, Curr, [Next | Rest], Acc)
when Curr =/= Last, Curr =/= Next ->
process(Curr, Next, Rest, Acc);
process(_Last, Curr, [Next | Rest], Acc) ->
process(Curr, Next, Rest, [Curr | Acc]).
One way for iterating a list (that as a result will return a new list) is using recursion and pattern matching.
After you sort your list you want to iterate the list and to check not only that it is different from the next element, but that there was no other equal elements before it. Consider the list [3,3,3,5,5] if you will only check the next element, the last 3 will also be unique and that is incorrect.
Here is a working program, I used a counter to cover the above case as well. See the syntax for using [H|T] for iterating over the list. You may see more cases and read more about it here.
-module(test).
-export([remove_unique/1]).
remove_unique(Input) ->
Sorted = lists:sort(Input),
remove_unique(Sorted, [], 0).
% Base case - checks if element is unique
remove_unique([H|[]],Output,Count) ->
case Count of
0 -> Output;
_Other -> [H|Output]
end;
% Count is 0 - might be unique - check with next element
remove_unique([H1|[H2|T]],Output, 0)->
case (H1 =:= H2) of
true -> remove_unique([H2|T],[H1|Output],1);
false -> remove_unique([H2|T],Output,0)
end;
% Count is > 0 - not unique - proceed adding to list until next value
remove_unique([H1|[H2|T]],Output,Count) ->
case (H1 =:= H2) of
true -> remove_unique([H2|T],[H1|Output],Count+1);
false -> remove_unique([H2|T],[H1|Output],0)
end.
Test
7> test:remove_unique([1,2,3,3,3,5,5,6,7,7]).
[7,7,5,5,3,3,3]
8> test:remove_unique([1,2,3,3,3,5,5,6,7,8]).
[5,5,3,3,3]

Erlang sumif function

I'm trying to make a sumif function in Erlang that would return a sum of all elements in a list if the predicate function evaluates to true. Here is what I have:
sumif(_, []) -> undefined;
sumif(Fun, [H|T]) -> case Fun(H) of
true -> H + sumif(Fun, T);
false -> sumif(Fun, T)
end.
I also implemented my own pos function which returns true if a number is greater than 0 and false otherwise:
pos(A) -> A > 0.
I tried using pos with sumif but I'm getting this error:
exception error: bad function pos
Why is this happening? Is it because of my sumif function or pos? I have tested pos on its own and it seems to work just fine.
Edit: It might be because how I'm calling the function. This is how I'm currently calling it: hi:sumif(pos,[-1,1,2,-3]). Where hi is my module name.
Is it because of my sumif function or pos?
It's because of sumif. You should return 0 when an empty list is passed, as it'll be called from the 2nd clause when T is []:
-module(a).
-compile(export_all).
sumif(_, []) -> 0;
sumif(Fun, [H|T]) -> case Fun(H) of
true -> H + sumif(Fun, T);
false -> sumif(Fun, T)
end.
pos(A) -> A > 0.
Test:
1> c(a).
{ok,a}
2> a:sumif(fun a:pos/1, [-4, -2, 0, 2, 4]).
6
List comprehensions make things far simpler:
sumif(F, L) ->
lists:sum([X || X <- L, F(X)]).
Dobert's answer is of cousrse right, problem is your sum for empty list.
If your concern is performance a little bit you should stick to tail recursive solution (in this case it matter because there is not lists:reverse/1 involved).
sumif(F, L) ->
sumif(F, L, 0).
sumif(F, [], Acc) when is_function(F, 1) -> Acc;
sumif(F, [H|T], Acc) ->
New = case F(H) of
true -> H+Acc;
false -> Acc
end,
sumif(F, T, New).
Ways how to make correct function for first parameter:
F1 = fun pos/1, % inside module where pos/1 defined
F2 = fun xyz:pos/1, % exported function from module xyz (hot code swap works)
N = 0,
F3 = fun(X) -> X > N end, % closure
% test it
true = lists:all(fun(F) -> is_function(F, 1) end, [F1, F2, F3]).
There has tow error in your code:
1. sumif(_, []) -> undefined; should return 0, not undefined.
2. when you pass pos(A) -> A > 0. to sumif/2,you should use fun pos/1, please read http://erlang.org/doc/programming_examples/funs.html#id59138
sumif(F, L) ->
lists:foldl(fun(X, Sum) when F(X) -> Sum+X; (_) -> Sum end, 0, L).
You can use lists:foldl.

Counting Number of Items in a tuple List in Erlang

I have the following List structure:
[{A, [{B <--, [A, C]}, {C <--, [B, A]}]}, {B, [{C <--, [A, C]}]}]
For example, B = 1, C = 2.
What would be the correct way to do so?
UPDATE
I'd like to count the number of <-- (a symbol I added just to show what I'm referring to) pointed item in it.
It can be implemented in many ways. Here is one more.
count(List) ->
count(List, dict:new()).
count([{_, InList} | Rest], Dict) ->
count(Rest, count2(InList, Dict));
count([], Dict) ->
dict:to_list(Dict).
count2([{Symbol, _} | Rest], Dict) ->
count2(Rest, dict:update_counter(Symbol, 1, Dict));
count2([], Dict) ->
Dict.
Example Output:
1> test:count([{one, [{b, [123]}, {c,[123]}, {b,[123]}]}, {two, [{b, [123]}, {c,[123]}, {b,[123]}]}]).
[{b,4},{c,2}]
You can write a simple code using an accumulator and some list functions. Supposing all list elements are of the same format:
count(L) ->
count (L, []).
count ([], Cases) ->
Cases;
count ([{_E1, [{X, [_E2, _E3]}]} | Rest], Cases) ->
NewCases =
case lists:keysearch(X, 1, Cases) of
false ->
[ {X, 1} | Cases ];
{value, {X, Val}} ->
lists:keyreplace(X, 1, Cases, {X, 1+Val})
end,
count(Rest, NewCases).

Resources