Erlang Programming Exercise: 3-4
In this exercise you are to make a database using this interface:
db:new() ⇒ Db.
db:destroy(Db) ⇒ ok.
db:write(Key, Element, Db) ⇒ NewDb.
db:delete(Key, Db) ⇒ NewDb.
db:read(Key, Db) ⇒{ok, Element} | {error, instance}.
db:match(Element, Db) ⇒ [Key1, ..., KeyN].
This one took a while to get right. In other languages I can look at some code and quickly figure out what it does. When I look at erlang recursive list code, it takes a while to parse it and figure out what it does.
In the read/2 and match/2 functions below, I end up discarding the head item from the list if it doesn't match what I'm looking for. It took a while for me to get used to throwing away data like this. In other languages I would iterated over the list until I found what I was looking for, saving allocation and deallocation cycles. Getting access to the head item in Erlang is very fast so this is a common pattern. This makes me wonder what sort of optimizations the Erlang VM (beam) guys have done to minimize the data copy costs. Anyhow, here is my solution:
-module(threefour_db).
-export([new/0, destroy/1, write/3, delete/2, read/2, match/2]).
new() ->
[].
destroy(_Db) ->
ok.
delete(_Key, NewDb, []) ->
NewDb;
delete(Key, NewDb, [{Key, _Element}|T]) ->
delete(Key, NewDb, T);
delete(Key, NewDb, [H|T]) ->
delete(Key, [H|NewDb], T).
delete(Key, Db) ->
delete(Key, [], Db).
write(Key, Element, Db) ->
NewDb = delete(Key, Db),
[{Key, Element}|NewDb].
read(_Key, []) ->
{error, instance};
read(Key, [{Key,Element}|_T]) ->
{ok, Element};
read(Key, [_H|T]) ->
read(Key, T).
match(_Element, [], Keys) ->
Keys;
match(Element, [{Key,Element}|Db], Keys) ->
match(Element, Db, [Key|Keys]);
match(Element, [_H|Db], Keys) ->
match(Element, Db, Keys).
match(Element, Db) ->
match(Element, Db, []).
Cheers,
-Halzy