Erlang 21 - 5. Records and Macros

5 रिकॉर्ड और मैक्रोज़




erlang

5 रिकॉर्ड और मैक्रोज़

बड़े कार्यक्रमों को आमतौर पर विभिन्न भागों के बीच एक अच्छी तरह से परिभाषित इंटरफ़ेस के साथ फाइलों के संग्रह के रूप में लिखा जाता है।

5.1 बड़ा उदाहरण कई फाइलों में विभाजित है

इसका वर्णन करने के लिए, पिछले अनुभाग से संदेशवाहक उदाहरण को निम्नलिखित पाँच फाइलों में विभाजित किया गया है:

  • mess_config.hrl

    कॉन्फ़िगरेशन डेटा के लिए हेडर फ़ाइल

  • mess_interface.hrl

    क्लाइंट और मैसेंजर के बीच इंटरफ़ेस परिभाषा

  • user_interface.erl

    उपयोगकर्ता इंटरफ़ेस के लिए कार्य

  • mess_client.erl

    मैसेंजर के क्लाइंट पक्ष के लिए कार्य

  • mess_server.erl

    मैसेंजर के सर्वर साइड के लिए कार्य

यह करते समय, शेल, क्लाइंट और सर्वर के बीच संदेश पासिंग इंटरफ़ेस को साफ किया जाता है और रिकॉर्ड का उपयोग करके परिभाषित किया जाता है । इसके अलावा, मैक्रोज़ पेश किए जाते हैं:

%%%----FILE mess_config.hrl----

%%% Configure the location of the server node,
-define(server_node, [email protected]).

%%%----END FILE----
%%%----FILE mess_interface.hrl----

%%% Message interface between client and server and client shell for
%%% messenger program 

%%%Messages from Client to server received in server/1 function.
-record(logon,{client_pid, username}).
-record(message,{client_pid, to_name, message}).
%%% {'EXIT', ClientPid, Reason}  (client terminated or unreachable.

%%% Messages from Server to Client, received in await_result/0 function 
-record(abort_client,{message}).
%%% Messages are: user_exists_at_other_node, 
%%%               you_are_not_logged_on
-record(server_reply,{message}).
%%% Messages are: logged_on
%%%               receiver_not_found
%%%               sent  (Message has been sent (no guarantee)
%%% Messages from Server to Client received in client/1 function
-record(message_from,{from_name, message}).

%%% Messages from shell to Client received in client/1 function
%%% spawn(mess_client, client, [server_node(), Name])
-record(message_to,{to_name, message}).
%%% logoff

%%%----END FILE----
%%%----FILE user_interface.erl----

%%% User interface to the messenger program
%%% login(Name)
%%%     One user at a time can log in from each Erlang node in the
%%%     system messenger: and choose a suitable Name. If the Name
%%%     is already logged in at another node or if someone else is
%%%     already logged in at the same node, login will be rejected
%%%     with a suitable error message.

%%% logoff()
%%%     Logs off anybody at that node

%%% message(ToName, Message)
%%%     sends Message to ToName. Error messages if the user of this 
%%%     function is not logged on or if ToName is not logged on at
%%%     any node.

-module(user_interface).
-export([logon/1, logoff/0, message/2]).
-include("mess_interface.hrl").
-include("mess_config.hrl").

logon(Name) ->
    case whereis(mess_client) of 
        undefined ->
            register(mess_client, 
                     spawn(mess_client, client, [?server_node, Name]));
        _ -> already_logged_on
    end.

logoff() ->
    mess_client ! logoff.

message(ToName, Message) ->
    case whereis(mess_client) of % Test if the client is running
        undefined ->
            not_logged_on;
        _ -> mess_client ! #message_to{to_name=ToName, message=Message},
             ok
end.

%%%----END FILE----
%%%----FILE mess_client.erl----

%%% The client process which runs on each user node

-module(mess_client).
-export([client/2]).
-include("mess_interface.hrl").

client(Server_Node, Name) ->
    {messenger, Server_Node} ! #logon{client_pid=self(), username=Name},
    await_result(),
    client(Server_Node).

client(Server_Node) ->
    receive
        logoff ->
            exit(normal);
        #message_to{to_name=ToName, message=Message} ->
            {messenger, Server_Node} ! 
                #message{client_pid=self(), to_name=ToName, message=Message},
            await_result();
        {message_from, FromName, Message} ->
            io:format("Message from ~p: ~p~n", [FromName, Message])
    end,
    client(Server_Node).

%%% wait for a response from the server
await_result() ->
    receive
        #abort_client{message=Why} ->
            io:format("~p~n", [Why]),
            exit(normal);
        #server_reply{message=What} ->
            io:format("~p~n", [What])
    after 5000 ->
            io:format("No response from server~n", []),
            exit(timeout)
    end.

%%%----END FILE---
%%%----FILE mess_server.erl----

%%% This is the server process of the messenger service

-module(mess_server).
-export([start_server/0, server/0]).
-include("mess_interface.hrl").

server() ->
    process_flag(trap_exit, true),
    server([]).

%%% the user list has the format [{ClientPid1, Name1},{ClientPid22, Name2},...]
server(User_List) ->
    io:format("User list = ~p~n", [User_List]),
    receive
        #logon{client_pid=From, username=Name} ->
            New_User_List = server_logon(From, Name, User_List),
            server(New_User_List);
        {'EXIT', From, _} ->
            New_User_List = server_logoff(From, User_List),
            server(New_User_List);
        #message{client_pid=From, to_name=To, message=Message} ->
            server_transfer(From, To, Message, User_List),
            server(User_List)
    end.

%%% Start the server
start_server() ->
    register(messenger, spawn(?MODULE, server, [])).

%%% Server adds a new user to the user list
server_logon(From, Name, User_List) ->
    %% check if logged on anywhere else
    case lists:keymember(Name, 2, User_List) of
        true ->
            From ! #abort_client{message=user_exists_at_other_node},
            User_List;
        false ->
            From ! #server_reply{message=logged_on},
            link(From),
            [{From, Name} | User_List]        %add user to the list
    end.

%%% Server deletes a user from the user list
server_logoff(From, User_List) ->
    lists:keydelete(From, 1, User_List).

%%% Server transfers a message between user
server_transfer(From, To, Message, User_List) ->
    %% check that the user is logged on and who he is
    case lists:keysearch(From, 1, User_List) of
        false ->
            From ! #abort_client{message=you_are_not_logged_on};
        {value, {_, Name}} ->
            server_transfer(From, Name, To, Message, User_List)
    end.
%%% If the user exists, send the message
server_transfer(From, Name, To, Message, User_List) ->
    %% Find the receiver and send the message
    case lists:keysearch(To, 2, User_List) of
        false ->
            From ! #server_reply{message=receiver_not_found};
        {value, {ToPid, To}} ->
            ToPid ! #message_from{from_name=Name, message=Message}, 
            From !  #server_reply{message=sent} 
    end.

%%%----END FILE---

5.2 हैडर फाइलें

जैसा कि ऊपर दिखाया गया है, कुछ फाइलों में एक्सटेंशन .hrl । ये ऐसी हेडर फाइलें हैं, जिन्हें .erl फाइलों में शामिल किया गया है:

-include("File_Name").

उदाहरण के लिए:

-include("mess_interface.hrl").

फ़ाइल के ऊपर मामले में मैसेंजर उदाहरण में अन्य सभी फ़ाइलों के समान निर्देशिका से प्राप्त किया जाता है। (* मैनुअल *)।

.hrl फ़ाइलों में कोई भी मान्य Erlang कोड हो सकता है, लेकिन रिकॉर्ड और मैक्रो परिभाषाओं के लिए सबसे अधिक बार उपयोग किया जाता है।

5.3 रिकॉर्ड

एक रिकॉर्ड के रूप में परिभाषित किया गया है:

-record(name_of_record,{field_name1, field_name2, field_name3, ......}).

उदाहरण के लिए:

-record(message_to,{to_name, message}).

यह इसके बराबर है:

{message_to, To_Name, Message}

एक रिकॉर्ड बनाना एक उदाहरण द्वारा सबसे अच्छा चित्रण किया गया है:

#message_to{message="hello", to_name=fred)

यह बनाता है:

{message_to, fred, "hello"}

ध्यान दें कि जब आप इसे बनाते हैं तो आपको रिकॉर्ड के विभिन्न भागों में मूल्यों को निर्दिष्ट करने वाले आदेश के बारे में चिंता करने की ज़रूरत नहीं है। रिकॉर्ड का उपयोग करने का लाभ यह है कि हेडर फ़ाइलों में उनकी परिभाषाओं को रखकर आप आसानी से उन इंटरफेस को परिभाषित कर सकते हैं जिन्हें बदलना आसान है। उदाहरण के लिए, यदि आप रिकॉर्ड में कोई नया फ़ील्ड जोड़ना चाहते हैं, तो आपको केवल उस कोड को बदलना होगा जहाँ नए फ़ील्ड का उपयोग किया गया है और हर स्थान पर रिकॉर्ड को संदर्भित नहीं किया गया है। यदि आप रिकॉर्ड बनाते समय किसी क्षेत्र को छोड़ देते हैं, तो उसे परमाणु का मान undefined हो जाता है। (* मैनुअल *)

अभिलेखों से मेल खाते हुए पैटर्न रिकॉर्ड बनाने के समान है। उदाहरण के लिए, किसी case अंदर या receive :

#message_to{to_name=ToName, message=Message} ->

यह समान है:

{message_to, ToName, Message}

5.4 मैक्रोज़

एक और चीज जो मैसेंजर में जोड़ी गई है वह एक मैक्रो है। फ़ाइल mess_config.hrl में परिभाषा है:

%%% Configure the location of the server node,
-define(server_node, [email protected]).

यह फ़ाइल mess_server.erl में शामिल है:

-include("mess_config.hrl").

mess_server.erl में mess_server.erl की हर घटना को अब [email protected] द्वारा बदल दिया जाता है।

सर्वर प्रक्रिया को स्पॉन करते समय एक मैक्रो का उपयोग किया जाता है:

spawn(?MODULE, server, [])

यह एक मानक मैक्रो है (जो सिस्टम द्वारा परिभाषित है, उपयोगकर्ता द्वारा नहीं)। ?MODULE को हमेशा वर्तमान मॉड्यूल के नाम से प्रतिस्थापित किया जाता है (अर्थात, फ़ाइल की शुरुआत के पास -module परिभाषा)। उदाहरण के लिए, पैरामीटर (* मैनुअल *) के साथ मैक्रोज़ का उपयोग करने के अधिक उन्नत तरीके हैं।

मैसेंजर उदाहरण में तीन Erlang ( .erl ) फाइलें व्यक्तिगत रूप से ऑब्जेक्ट कोड फ़ाइल ( .beam ) में संकलित की जाती हैं। Erlang सिस्टम इन फ़ाइलों को सिस्टम में तब लोड और लिंक करता है जब उन्हें कोड के निष्पादन के दौरान संदर्भित किया जाता है। इस मामले में, उन्हें बस हमारी वर्तमान कार्यशील निर्देशिका में रखा जाता है (अर्थात, आपने "सीडी" किया है)। अन्य निर्देशिकाओं में .beam फ़ाइलें डालने के तरीके हैं।

संदेशवाहक उदाहरण में, संदेश भेजे जाने के बारे में कोई धारणा नहीं बनाई गई है। यह कोई भी वैध एर्लैंग शब्द हो सकता है।