[erlang-questions] How to match on a nested record against a dict

1 view
Skip to first unread message

Jarrod Roberson

unread,
Nov 22, 2009, 12:52:21 AM11/22/09
to Erlang
I have the following data structure: it is a record as specified in
inet_dns.hrl:

{ok,{dns_rec,{dns_header,0,true,'query',true,false,false,false,false,0},
[],
[{dns_rr,"_see._tcp.local",ptr,in,0,0,
"jhr@Blackintosh._see._tcp.local",undefined,[],
false}],
[],[]}}

I have the following code where Sub is a dict:

receiver(Sub) ->
receive
{udp, _Socket, IP, InPortNo, Packet} ->
DnsRec = inet_dns:decode(Packet),
io:format("~n~nFrom: ~p~nPort: ~p~nData:
~p~n",[IP,InPortNo,DnsRec]),
receiver(Sub);
stop ->
true;
AnythingElse ->
%io:format("RECEIVED: ~p~n",[AnythingElse]),
receiver(Sub)
end.

I want to match on the "_see._tcp.local" and see if it is in the dns_rr
record and only do the io:format() if that is in my subscriptions dict.
Can anyone point me in the right direction of what idiom to use? This
pattern matching stuff is difficult for us olde school if () ers

Igor Ribeiro Sucupira

unread,
Nov 22, 2009, 2:17:57 AM11/22/09
to Jarrod Roberson, Erlang
Hi, Jarrod.

I can't understand exactly what you want, so I'll give you an example.

Suppose DomainsSet is a sets of domains and DnsRec is a dns_rec. This
would be a list of the domains that are present in some resource
record and in DomainsSet:
[Domain || #dns_rr{domain = Domain} <- DnsRec#dns_rec.anlist,
sets:is_element(Domain, DomainsSet)]

Is that helpful?

Igor.


--
"The secret of joy in work is contained in one word - excellence. To
know how to do something well is to enjoy it." - Pearl S. Buck.

________________________________________________________________
erlang-questions mailing list. See http://www.erlang.org/faq.html
erlang-questions (at) erlang.org

Jarrod Roberson

unread,
Nov 22, 2009, 10:55:35 AM11/22/09
to Igor Ribeiro Sucupira, Erlang
On Sun, Nov 22, 2009 at 2:17 AM, Igor Ribeiro Sucupira <igo...@gmail.com>wrote:

> Hi, Jarrod.
>
> I can't understand exactly what you want, so I'll give you an example.
>
> Suppose DomainsSet is a sets of domains and DnsRec is a dns_rec. This
> would be a list of the domains that are present in some resource
> record and in DomainsSet:
> [Domain || #dns_rr{domain = Domain} <- DnsRec#dns_rec.anlist,
> sets:is_element(Domain, DomainsSet)]
>
> Is that helpful?
>
> Igor.
>

> thanks it is a place for me to start

Jarrod Roberson

unread,
Nov 23, 2009, 12:25:56 AM11/23/09
to Erlang
On Sun, Nov 22, 2009 at 2:17 AM, Igor Ribeiro Sucupira <igo...@gmail.com>wrote:

> Hi, Jarrod.
>
> I can't understand exactly what you want, so I'll give you an example.
>
> Suppose DomainsSet is a sets of domains and DnsRec is a dns_rec. This
> would be a list of the domains that are present in some resource
> record and in DomainsSet:
> [Domain || #dns_rr{domain = Domain} <- DnsRec#dns_rec.anlist,
> sets:is_element(Domain, DomainsSet)]
>
> Is that helpful?
>
> Igor.
>

> Ok have tried and tried, and your snippet won't compile.
I have both Erlang books and can't figure out how to do this logic.

here is how I would do it in pseudo-code in languages that I am familar
with.

for (dns_rr in dns_rec.getAnList())
{
if (dns_rr.domain == "_test._tcp.local")
{
// do some logic here
}
}


receiver(Subs) ->


receive
{udp, _Socket, IP, InPortNo, Packet} ->

{ok,DnsRec} = inet_dns:decode(Packet),
% this is where I want the "domain" detecting logic from the
dns_rr records


io:format("~n~nFrom: ~p~nPort: ~p~nData:
~p~n",[IP,InPortNo,DnsRec]),

receiver(Subs);


stop ->
true;
AnythingElse ->
%io:format("RECEIVED: ~p~n",[AnythingElse]),

receiver(Subs)
end.

Rob Charlton

unread,
Nov 23, 2009, 3:37:32 AM11/23/09
to Jarrod Roberson, Erlang
Hi Jarrod,

First off, to get the record definitions for #dns_rec and #dns_rr, you
need to include this line in your module (normally near the top):

-include_lib("kernel/src/inet_dns.hrl").

Now I would code it something like:

receive
{udp, _Socket, IP, InPortNo, Packet} ->

process_dnsrec(Sub,inet_dns:decode(Packet)),
receiver(Sub);

...

process_dnsrec(Sub,{error,E}) ->

%% do some error handling here (if required)

;

process_dnsrec(Sub,{ok,#dnsrec{anlist=Responses}) ->

%% Responses is a list of #dns_rr records, so we want to handle them one at a time

process_dnsrec1(Sub,Responses).

process_dnsrec1(_,[]) ->

%% no more responses

;

process_dnsrec1(Sub,[#dns_rr{domain=Dom}|Rest]) ->
%% lookup the domain field from the #dns_rr record

case dict:find(Dom,Sub) of

[Result] ->

io:format("Interesting domain ~p~n",[Result]);

error ->

%% do nothing for non-interesting domains

ok

end,

process_dnsrec1(Sub,Rest).


Cheers

Rob

Jarrod Roberson

unread,
Nov 24, 2009, 3:18:06 PM11/24/09
to Rob Charlton, Erlang
Thanks for the example code, it is getting me past the "how" and onto the
"why" of logic in Erlang, I have it all working except this one part. I keep
getting this exception. I have used your example and come up with this.

start() ->
S=open({224,0,0,251},5353),

Pid=spawn(?MODULE,receiver,[dict:store("_see._tcp.local",[],dict:new())]),
gen_udp:controlling_process(S,Pid),
{S,Pid}.

receiver(Sub) ->
receive
{udp, _Socket, _IP, _InPortNo, Packet} ->
process_dnsrec(Sub,inet_dns:decode(Packet)),
receiver(Sub);


stop ->
true;
AnythingElse ->

io:format("RECEIVED: ~p~n",[AnythingElse]),
receiver(Sub)
end.

process_dnsrec(_Sub,{error,E}) -> io:format("Error: ~p~n", [E]);
process_dnsrec(Sub,{ok,#dns_rec{anlist=Responses}}) ->
process_dnsrec1(Sub,Responses).

process_dnsrec1(_,[]) -> ok;


process_dnsrec1(Sub,[#dns_rr{domain=Dom}|Rest]) ->

case dict:find(Dom,Sub) of
[Result] ->

io:format("Interesting domain ~p=~p~n",[Dom,Result]);


error ->
%% do nothing for non-interesting domains
ok
end,
process_dnsrec1(Sub,Rest).

and here is the Error I am getting

Erlang R13B02 (erts-5.7.3) [source] [smp:2:2] [rq:2] [async-threads:0]
[kernel-poll:false]

Eshell V5.7.3 (abort with ^G)
1> zeroconf:start().
{#Port<0.442>,<0.34.0>}
2>
=ERROR REPORT==== 24-Nov-2009::15:16:40 ===
Error in process <0.34.0> with exit value:
{{case_clause,{ok,[]}},[{zeroconf,process_dnsrec1,2},{zeroconf,receiver,1}]}

Jarrod Roberson

unread,
Nov 24, 2009, 4:25:57 PM11/24/09
to Rob Charlton, Erlang
On Tue, Nov 24, 2009 at 3:18 PM, Jarrod Roberson <jar...@vertigrated.com>wrote:

>
> process_dnsrec1(Sub,[#dns_rr{domain=Dom}|Rest]) ->
> case dict:find(Dom,Sub) of
> [Result] ->
> io:format("Interesting domain ~p=~p~n",[Dom,Result]);
>

thanks for reading I figured it out by trial and error.
I replaced the [Result] with {ok,Result} and it started working as expected

Igor Ribeiro Sucupira

unread,
Nov 25, 2009, 7:09:28 PM11/25/09
to Jarrod Roberson, Rob Charlton, Erlang
If you don't need the values that you store in the dict (in your
example, you store [] and never use it), you can use a sets instead of
a dict. Usage is a little bit simpler:

-------------------------
1> D = dict:store("_see._tcp.local", [], dict:new()).
{dict,1,16,16,8,80,48,
{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},
{{[],[],[],[],
[["_see._tcp.local"]],
[],[],[],[],[],[],[],[],[],[],[]}}}
2> dict:find("_see._tcp.local", D).
{ok,[]}

3> S = sets:add_element("_see._tcp.local", sets:new()).
{set,1,16,16,8,80,48,
{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},
{{[],[],[],[],
["_see._tcp.local"],
[],[],[],[],[],[],[],[],[],[],[]}}}
4> sets:is_element("_see._tcp.local", S).
true
-------------------------


See also gb_sets and ordsets.

Igor.

--

"The secret of joy in work is contained in one word - excellence. To
know how to do something well is to enjoy it." - Pearl S. Buck.

________________________________________________________________

Reply all
Reply to author
Forward
0 new messages