[Erlang] - Моя первая практическая задача
От: DemAS http://demas.me
Дата: 17.02.09 11:12
Оценка:
Ну вот я ее и доделал.

После изучения основ 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
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.