Ну вот я ее и доделал.
После изучения основ Erlang решил написать что-нибудь практичное, но так
как реальных задач не было, пришлось придумывать самому.
Итак задача — построить ассоциативный массив — частоту использования
символов в файле.
Но, так как в Erlang "все что угодно — это процесс" будем решать задачу с
помощью процессов.
Архитектура решения выглядит так:
1. Есть один процесс (ft), который читает файл и разбивает его на слова.
2. На каждое найденное слово процесс 1(ft) порождает новый
процесс(word_server), который строит ассоциативный массив — частота
использования символов в слове.
3. Каждый из процессов 2(word_server) после разбора слова посылает
сформированный ассоциативный массив третьему типу процессов (db_server),
который был сразу запущен процессом(ft). Процесс db_server принимает от
множества процессов word_server ассоциативные массивы (по словам) и
формирует общий ассоциативный массив (по всему тексту), который затем
выводится на экран.
То есть, у меня есть один процесс читающий файл(ft), один процесс,
агрегирующий данные и множество процессов разбирающих слова.
Каждый из модулей можно отдельно протестировать:
* ft:test_words() — разбор текста на слова;
* ft:test_dicts() — разбор текста на слова и построения ассоциативного
массива на основании одного слова
* db_server:test() — агрегатирование ассоциативных массивов.
Запускается все это дело так: ft:start().
Имя файла прописано в тексте в ft:process_file().
Итак тексты:
-module(ft).
-export([start/0, test_words/0, test_dicts/0]).
-import(db_server).
-import(word_server).
start()->
db_server:register_db_server(),
process_all_words(process_file()),
get_result_from_db_server().
test_words()->
process_file().
test_dicts()->
process_all_words_test_mode(process_file()).
process_file()->
{ok, Device} = file:open("data.txt", [read]),
get_words_from_file(Device).
get_words_from_file(Device)->
process_all_lines(get_all_lines_in_file(Device, []), []).
get_result_from_db_server()->
db_server:print_result(db_serv).
get_all_lines_in_file(Device, Accum)->
case io:get_line(Device, "") of
eof ->
file:close(Device),
Accum;
Line ->
get_all_lines_in_file(Device, Accum ++ [Line])
end.
get_all_words_in_line(Line, Accum)->
case string:chr(Line, $ ) of
0 ->
Accum ++ [Line];
Idx ->
get_all_words_in_line(string:substr(Line, Idx + 1), Accum ++ [lists:sublist(Line, Idx - 1)])
end.
process_all_lines([T|H], Accum)->
NewAccum = get_all_words_in_line(T, Accum),
process_all_lines(H, NewAccum);
process_all_lines([], Accum) ->
Accum.
process_all_words([T|H])->
word_server:process_word(word_server:start_word_server(), {word, T}),
process_all_words(H);
process_all_words([]) ->
io:format("processing word finished~n").
process_all_words_test_mode([T|H])->
word_server:process_word(word_server:start_word_server(), {test_word, T}),
process_all_words_test_mode(H);
process_all_words_test_mode([]) ->
io:format("processing word finished~n").
-module(word_server).
-import(db_server).
-import(dict_ext).
-export([start_word_server/0, process_word/2]).
start_word_server()->
spawn(fun loop_word_server/0).
loop_word_server()->
receive
{From, {word, Word}} ->
db_server:process_dict(db_serv, {dict, make_symbol_dict(Word, dict:new())}),
From ! {self(), "done"};
{From, {test_word, Word}} ->
dict_ext:print_dict(make_symbol_dict(Word, dict:new())),
From ! {self(), "done"};
{From, Other} ->
io:format("Invalid input in word server - ~p~n", [Other]),
From ! {self(), {error, Other}},
loop_word_server()
end.
make_symbol_dict(Word, Dict) when length(Word) > 0 ->
make_symbol_dict(string:substr(Word, 2), dict:update(lists:sublist(Word, 1), fun(Old)-> Old + 1 end, 1, Dict));
make_symbol_dict(Word, Dict) ->
Dict.
rpc(Pid, Word)->
Pid ! {self(), Word},
receive
{Pid, Responce}->
Responce
end.
process_word(Pid, Word)->
rpc(Pid, Word).
-module(db_server).
-export([register_db_server/0, process_dict/2, print_result/1, test/0]).
-import(dict_ext).
register_db_server()->
register(db_serv, start_db_server()).
start_db_server()->
spawn(fun prepare_db_server/0).
prepare_db_server()->
loop_db_server(dict:new()).
loop_db_server(GlobalDict)->
receive
{From, {dict, Dict}} ->
UpdatedDict = update_global_dict(GlobalDict, Dict),
From ! {self(), "done"},
loop_db_server(UpdatedDict);
{From, {print}} ->
dict_ext:print_dict(GlobalDict),
From ! {self(), "print"},
loop_db_server(GlobalDict);
{From, {finish}}->
io:format("db_sever shutdown~n"),
From ! {self(), "finish"};
{From, Other} ->
io:format("Invalid input in db server - ~p~n", [Other]),
From ! {self(), {error, Other}},
loop_db_server(GlobalDict)
end.
update_global_dict(Global, Local)->
dict:merge(fun(Key, Value1, Value2) ->
Value1 + Value2
end,
Global, Local).
db_rpc(Pid, Dict)->
Pid ! {self(), Dict},
receive
{NewPid, Responce}->
Responce
end.
process_dict(Pid, Dict)->
db_rpc(Pid, Dict).
print_result(Pid)->
db_rpc(Pid, {print}).
shutdown_db_server(Pid)->
io:format("finish~n"),
db_rpc(Pid, {finish}).
create_test_dict()->
D1 = dict:new(),
D2 = dict:store("a", 5, D1),
D3 = dict:store("b", 2, D2),
D4 = dict:store("c", 1, D3).
test()->
register_db_server(),
D = create_test_dict(),
process_dict(db_serv, {dict, D}),
process_dict(db_serv, {dict, D}),
print_result(db_serv),
shutdown_db_server(db_serv).
-module(dict_ext).
-export([print_dict/1]).
print_dict(Dict)->
print_list(dict:to_list(Dict)).
print_list([T|H])->
io:format("~p~n", [T]),
print_list(H);
print_list([]) ->
io:format("print done~n").
Posted via RSDN NNTP Server 2.1 beta