Check if a value is present in a record or not? - erlang

Suppose I have a record named #parameters, and I am accessing it from outside and I do not know if all the keys inside it have a value stored with it.
Suppose there is a key in my record named Index. How can I check if the value for Index is present in my record or not? And how do I put it in a case statement like:
Index = find from the record
or else put it as 100.

I am accessing it from outside and I do not know if all the keys
inside it have a value stored with it.
All fields in a record have a value. If a default value for a field is not specified in the record definition, then erlang uses the atom undefined for the default value. For instance:
-module(a).
-compile(export_all).
-record(pattern, {a, b=0, c}).
go() ->
#pattern{a=1}.
In the shell,
1> c(a).
a.erl:2:2: Warning: export_all flag enabled - all functions will be exported
% 2| -compile(export_all).
% | ^
{ok,a}
2> rr(a).
[pattern]
3> a:go().
#pattern{a = 1,b = 0,c = undefined}
Suppose there is a key in my record named Index; how can I check if
the value for Index is present in my record or not?
Once again, there will always be a value for the index key. Here's an example that demonstrates how to do one thing if index has the value undefined and do something else if index has some other value.
-module(a).
-compile(export_all).
-record(pattern, {a, b=0, index}).
go() ->
Pattern1 = #pattern{a=1},
check_index(Pattern1),
Pattern2 = #pattern{a=1, index=10},
check_index(Pattern2).
check_index(#pattern{index=undefined}) ->
io:format("index key is undefined~n");
check_index(#pattern{index=Number}) ->
io:format("index key is ~w~n", [Number]).
In the shell:
1> c(a).
a.erl:2:2: Warning: export_all flag enabled - all functions will be exported
% 2| -compile(export_all).
% | ^
{ok,a}
2> rr(a).
[pattern]
3> a:go().
index key is undefined
index key is 10
ok
You could also turn the check_index() function into a boolean function that returns true or false.

Related

Generate a random 16-byte string in the 32-character hexadecimal format

I am new to Elixir / Erlang. I have been using Ruby for a while and I am wanting to convert this piece of code to Elixir / Erlang.
SecureRandom::random_bytes(16).each_byte.map { |b| sprintf("%02X",b) }.join
To have the same result, in Elixir you can use the Erlang module :crypto.strong_rand_bytes(16) to generate the random number, and convert it using Base.encode16
Take a look at https://hexdocs.pm/elixir/Base.html to understand better the Base module
.
Example:
:crypto.strong_rand_bytes(16) |> Base.encode16 # => "4B14868924ACEE98C9C9C404A1F87B08"
%% Generate random bytes
<<X:128/big-unsigned-integer>> = crypto:strong_rand_bytes(16).
%% conver to hex
lists:flatten(io_lib:format("~32.16.0b", [X])).
First, your ruby code can be simplified to:
SecureRandom.hex(16).upcase
Next, in erlang a "string" can be somewhat of a nebulous concept. In erlang, it is typically more efficient to construct a list which contains chunks of your string, then pass around the list, then when you need to output the string, erlang will automatically join the chunks for you:
-module(a).
-compile(export_all).
go() ->
MyIolist = [ io_lib:format("~2.16.0B", [X]) || <<X>> <= crypto:strong_rand_bytes(16) ],
%% Print a ruler -------
Ruler = "1234567890",
lists:foreach(fun(_X) -> io:format("~s", [Ruler]) end,
lists:seq(1, 4) ),
io:format("~n"),
%%-----------------------
%% Print the list containing the chunks of the string:
io:format("~s~n", [MyIolist]).
The format sequence:
"~2.16.0B"
is read as:
2 => field width,
16 => base (base 10 is the default)
0 => padding character (so that the integers 0-15 are represented by two characters)
~B => format code that allows you to specify the base of an integer and uses uppercase for letters
(the particulars go between ~ and B)
In the shell:
23> c(a).
a.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,a}
24> a:go().
1234567890123456789012345678901234567890
21755436149C16E0A9902F7B7E9F6929
ok
25> a:go().
1234567890123456789012345678901234567890
86D3850FA2590B810C1CAB0B965EE415
ok
26> a:go().
1234567890123456789012345678901234567890
A9C31CAFCC74C8311A5389E3BA1CD19F
ok
27> a:go().
1234567890123456789012345678901234567890
FB82878326F7B6FDB87069AF031345FF
ok
28> a:go().
1234567890123456789012345678901234567890
57515F06DFA699710E543D9885CB90ED
ok
29> a:go().
1234567890123456789012345678901234567890
F84B308723DB660A8A4371E3A80C23DC
ok
I ran that a few times so you can see that the strings are all the same length. 16 bytes * 2 hex_chars/byte = 32 chars.
As Venkatakumar Srinivasan demonstrated, for your purposes you can just treat all 16 bytes(128 bits) as a single integer:
-module(a).
-compile(export_all).
go() ->
<<X:128>> = crypto:strong_rand_bytes(16),
Result = io_lib:format("~32.16.0B", [X]),
%% Print a ruler:
Ruler = "1234567890",
lists:foreach(fun(_X) -> io:format("~s", [Ruler]) end,
lists:seq(1, 4) ),
io:format("~n"),
%%-----------------------
%% Print the list containing the chunks of the string:
io:format("~s~n", Result).
In the shell:
42> c(a).
a.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,a}
43> a:go().
1234567890123456789012345678901234567890
551589BE1CB7B0C048AEF67DD9343F7F
ok
44> a:go().
1234567890123456789012345678901234567890
AC3DC11F714D7F8FE8ABBED566E9AB5B
ok
45> a:go().
1234567890123456789012345678901234567890
176530F08AFEC3B5452A818E4A95C743
ok
46> a:go().
1234567890123456789012345678901234567890
8F2E651B3D7D53AF88147244BB8F6153
ok
There's no reason to call flatten(). Although, io_lib:format() doesn't concern itself with creating complete strings, when you tell erlang to output the list returned by io_lib:format() using the format sequence ~s, erlang will join everything together into one string.

Erlang Badarith Error

The below Erlang function results in badarith error sometimes even when the values of Lat and Distance are float values
What could be the reason and what can be done to avoid it ?
-define(EARTH_RADIUS, 6372800).
-define(PI, ct_expand:term(math:pi())).
-define(RAD_FACTOR, ct_expand:term(?PI / 180)).
add_distance_lat(Lat, Distance) ->
Lat + ((Distance / ?EARTH_RADIUS) / ?RAD_FACTOR).
The below Erlang function results in badarith error sometimes even
when the values of Lat and Distance are float values
I don't know what the ct_expand:term does, but you can easily test that add_distance_lat() does not cause a badarith error when given two floats as arguments. I removed ct_expand:term from the following code:
-module(my).
-compile(export_all).
-define(EARTH_RADIUS, 6372800).
-define(PI, math:pi()).
-define(RAD_FACTOR, ?PI / 180).
test(0) -> ok;
test(N) ->
X = rand:uniform() * 1000,
Y = rand:uniform() * 10000,
io:format("X=~w, Y=~w~n", [X, Y]),
ok = add_distance_lat(X, Y),
test(N-1).
add_distance_lat(Lat, Distance) ->
try Lat + ((Distance / ?EARTH_RADIUS) / ?RAD_FACTOR) of
_GoodResult -> ok
catch
error:Error -> Error;
_:Other -> Other
end.
Here's what running the test 10 times looks like:
5> c(my).
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}
6> my:test(10).
X=169.43178167665917, Y=994.0890019584891
X=106.80009354948483, Y=5318.014760525637
X=483.5152966069006, Y=849.1797017589287
X=413.42192963089917, Y=1813.3077771861872
X=695.5858531252752, Y=6659.873970151745
X=476.4974288029442, Y=3429.9745843747255
X=352.2598626576124, Y=5441.283558914134
X=189.92661305858994, Y=7267.19292963693
X=525.3094754648756, Y=6112.466577043024
X=629.8521203556536, Y=8758.910589712157
ok
The fact that ok was returned on the last line means that add_distance_lat() performed without error. If there was an error, then you would get a badmatch here:
ok = add_distance_lat(X, Y),
because add_distance_lat() would not return ok. Here's an example:
test() ->
ok = add_distance_lat(12.34567, '10.1111').
add_distance_lat(Lat, Distance) ->
try Lat + ((Distance / ?EARTH_RADIUS) / ?RAD_FACTOR) of
_GoodResult -> ok
catch
error:Error -> Error;
_:Other -> Other
end.
In the shell:
11> c(my).
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}
12> my:test().
** exception error: no match of right hand side value badarith
in function my:test/0 (my.erl, line 15)
The error says that the right hand side of this line:
ok = add_distance_lat(12.34567, '10.1111').
is the atom badarith, which does not match the atom ok.
You can comment out the io:format() statement in the first test() example, then run the test 100,000,000 times. If ok is returned, then there was no badarith error:
7> c(my).
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}
8> my:test(100000000).
ok
I'm pretty confident that add_distance_lat() handles floats with aplomb.
On the other hand:
3> my:add_distance_lat(30.45, "20.11").
badarith
4> my:add_distance_lat('1000.24', 12.123412341324).
badarith
As legoscia suggested, if you add the is_number() guards to your function, then you'll get a function_clause error (no matching function clause is found when evaluating a function call) if you don't provide floats or integers as arguments:
add_distance_lat(Lat, Distance) when is_number(Lat), is_number(Distance) ->
Lat + ((Distance / ?EARTH_RADIUS) / ?RAD_FACTOR).
In the shell:
5> c(my).
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}
6> my:add_distance_lat(30.45, "20.11").
** exception error: no function clause matching my:add_distance_lat(30.45,"20.11") (my.erl, line 27)
7> my:add_distance_lat('1000.24', 12.123412341324).
** exception error: no function clause matching my:add_distance_lat('1000.24',12.123412341324) (my.erl, line 27)
If you don't get the function_clause error with your data, then look to ct_expand:term for the problem.

mnesia - creating index on a field after the table is created

I created a table using
mafiapp_friends = [aaa, expertise, xxx, yyy]
Note expertise is at position 2 in record
mnesia:create_table(mafiapp_friends,
[{attributes, record_info(fields, mafiapp_friends)},
{disc_copies, Nodes}]).
I forgot to add an index statement in it
{index, [#mafiapp_friends.expertise]},
Now I need to create this index. However I do not want to delete the table and re-create the table as I have data in it.
I executed following statement:
mnesia:add_table_index(mafiapp_friends, expertise)
And then I did
mnesia:schema(mafiapp_friends).
In the output I see,
index -> [3]
{index,3} -> {mafiapp_friends,index,3}
Can you tell me what [3] means here?
So, 3 is position of indexed field in the table, i.e.
#mafiapp_friends.expertise
See an example:
2> rd(mafiapp_friends, {aaa, expertise, xxx, yyy}).
mafiapp_friends
3> #mafiapp_friends.expertise.
3
4> record_info(fields, mafiapp_friends).
[aaa,expertise,xxx,yyy]
5>
Note that records in erlang are just tuples with the first element being record name, that's why indexing looks a bit strange on the first sight
5> X = #mafiapp_friends{expertise = hello}.
#mafiapp_friends{aaa = undefined,expertise = hello,
xxx = undefined,yyy = undefined}
6> element(3, X).
hello
7>

How to get the name of a function?

Is it possible to know the name of a function in itself ?
a_function()->
io:format("I am ~p!", [????]). % What to use here?
Use the macro ?FUNCTION_NAME to get the name of the current function as an atom, and ?FUNCTION_ARITY to get the arity as an integer.
Example:
-module(test).
-export([a_function/2]).
a_function(_Foo, _Bar) ->
io:write("I am ~p/~p!",[?FUNCTION_NAME, ?FUNCTION_ARITY]).
1> c(test).
{ok,test}
2> test:a_function(a, b).
I am a_function/2!
This was implemented in EEP-0045.
For Erlang Versions 18 and Older
In older Erlang versions, there's no simple way to get the current function name at compile time. You can however retrieve it at runtime:
{current_function, {M, F, A}} = process_info(self(), current_function)
Where A is the arity (number of arguments), not the actual arguments. The first argument to process_info/2 is a process ID which can be either the current process (self()) or an other process. For example:
1> process_info(self(), current_function).
{current_function,{erl_eval,do_apply,5}}
Note however, that while this would be functionally equivalent to the ?FUNCTION_NAME macro, it's much slower because it is evaluated in runtime.
at runtime, you could throw an exception and check the top of the stacktrace.
foo() ->
catch throw(away),
[{Module, Fun, Arity} | _] = erlang:get_stacktrace(),
io:format("I am ~p:~p/~p!~n",[Module, Fun, Arity]).

Is it possible to use record name as a parameter?

Lets say I have a record:
-record(foo, {bar}).
What I would like to do is to be able to pass the record name to a function as a parameter, and get back a new record. The function should be generic so that it should be able to accept any record, something like this.
make_record(foo, [bar], ["xyz"])
When implementing such a function I've tried this:
make_record(RecordName, Fields, Values) ->
NewRecord = #RecordName{} %% this line gives me an error: syntax error before RecordName
Is it possible to use the record name as a parameter?
You can't use the record syntax if you don't have access to the record during compile time.
But because records are simply transformed into tuples during compilation it is really easy to construct them manually:
-record(some_rec, {a, b}).
make_record(Rec, Values) ->
list_to_tuple([Rec | Values]).
test() ->
R = make_record(some_rec, ["Hej", 5]), % Dynamically create record
#some_rec{a = A, b = B} = R, % Access it using record syntax
io:format("a = ~p, b = ~p~n", [A, B]).
Alternative solution
Or, if you at compile time make a list of all records that the function should be able to construct, you can use the field names also:
%% List of record info created with record_info macro during compile time
-define(recs,
[
{some_rec, record_info(fields, some_rec)},
{some_other_rec, record_info(fields, some_other_rec)}
]).
make_record_2(Rec, Fields, Values) ->
ValueDict = lists:zip(Fields, Values),
% Look up the record name and fields in record list
Body = lists:map(
fun(Field) -> proplists:get_value(Field, ValueDict, undefined) end,
proplists:get_value(Rec, ?recs)),
list_to_tuple([Rec | Body]).
test_2() ->
R = make_record_2(some_rec, [b, a], ["B value", "A value"]),
#some_rec{a = A, b = B} = R,
io:format("a = ~p, b = ~p~n", [A, B]).
With the second version you can also do some verification to make sure you are using the right fields etc.
Other tips
Other useful constructs to keep in mind when working with records dynamically is the #some_rec.a expression which evaluates to the index of the a field in some_recs, and the element(N, Tuple) function which given a tuple and an index returns the element in that index.
This is not possible, as records are compile-time only structures. At compilation they are converted into tuples. Thus the compiler needs to know the name of the record, so you cannot use a variable.
You could also use some parse-transform magic (see exprecs) to create record constructors and accessors, but this design seems to go in the wrong direction.
If you need to dynamically create record-like things, you can use some structures instead, like key-value lists, or dicts.
To cover all cases: If you have fields and values but don't necessarily have them in the correct order, you could make your function take in the result of record_info(fields, Record), with Record being the atom of the record you want to make. Then it'll have the ordered field names to work with. And a record is just a tuple with its atom name in the first slot, so you can build it that way. Here's how I build an arbitrary shallow record from a JSON string (not thoroughly tested and not optimized, but tested and working):
% Converts the given JSON string to a record
% WARNING: Only for shallow records. Won't work for nested ones!
%
% Record: The atom representing the type of record to be converted to
% RecordInfo: The result of calling record_info(fields, Record)
% JSON: The JSON string
jsonToRecord(Record, RecordInfo, JSON) ->
JiffyList = element(1, jiffy:decode(JSON)),
Struct = erlang:make_tuple(length(RecordInfo)+1, ""),
Struct2 = erlang:setelement(1, Struct, Record),
recordFromJsonList(RecordInfo, Struct2, JiffyList).
% private methods
recordFromJsonList(_RecordInfo, Struct, []) -> Struct;
recordFromJsonList(RecordInfo, Struct, [{Name, Val} | Rest]) ->
FieldNames = atomNames(RecordInfo),
Index = index_of(erlang:binary_to_list(Name), FieldNames),
recordFromJsonList(RecordInfo, erlang:setelement(Index+1, Struct, Val), Rest).
% Converts a list of atoms to a list of strings
%
% Atoms: The list of atoms
atomNames(Atoms) ->
F = fun(Field) ->
lists:flatten(io_lib:format("~p", [Field]))
end,
lists:map(F, Atoms).
% Gets the index of an item in a list (one-indexed)
%
% Item: The item to search for
% List: The list
index_of(Item, List) -> index_of(Item, List, 1).
% private helper
index_of(_, [], _) -> not_found;
index_of(Item, [Item|_], Index) -> Index;
index_of(Item, [_|Tl], Index) -> index_of(Item, Tl, Index+1).
Brief explanation: The JSON represents some key:value pairs corresponding to field:value pairs in the record we're trying to build. We might not get the key:value pairs in the correct order, so we need the list of record fields passed in so we can insert the values into their correct positions in the tuple.

Resources