2011-02-27 11 views
5

मैं erlang.org course से इस अभ्यास कर रहा हूँ:Erlang पाठ्यक्रम संगामिति व्यायाम: मेरा उत्तर सुधार किया जा सकता है?

2) एक समारोह है जो एक अंगूठी में एन प्रक्रियाओं शुरू होता है, और अंगूठी में सभी प्रक्रियाओं के चारों ओर एक संदेश एम बार भेजता लिखें। बाद संदेशों प्रक्रियाओं भेज दिया गया है शान से समाप्त करना चाहिए।

-module(ring). 
-export([start/2, node/2]). 

node(NodeNumber, NumberOfNodes) -> 
    NextNodeNumber = (NodeNumber + 1) rem NumberOfNodes, 
    NextNodeName = node_name(NextNodeNumber), 
    receive 
    CircuitNumber -> 
     io:format("Node ~p Circuit ~p~n", [NodeNumber, CircuitNumber]), 
     LastNode = NodeNumber =:= NumberOfNodes - 1, 
     NextCircuitNumber = case LastNode of 
          true -> 
          CircuitNumber - 1; 
          false -> 
          CircuitNumber 
         end, 
     if 
     NextCircuitNumber > 0 -> 
      NextNodeName ! NextCircuitNumber; 
     true -> 
      ok 
     end, 
     if 
     CircuitNumber > 1 -> 
      node(NodeNumber, NumberOfNodes); 
     true -> 
      ok 
     end 
    end. 

start(NumberOfNodes, NumberOfCircuits) -> 
    lists:foreach(fun(NodeNumber) -> 
        register(node_name(NodeNumber), 
          spawn(ring, node, [NodeNumber, NumberOfNodes])) 
       end, 
       lists:seq(0, NumberOfNodes - 1)), 
    node_name(0) ! NumberOfCircuits, 
    ok. 

node_name(NodeNumber) -> 
    list_to_atom(lists:flatten(io_lib:format("node~w", [NodeNumber]))). 

यहाँ इसके उत्पादन किया जाता है::

17> ring:start(3, 2). 
Node 0 Circuit 2 
ok 
Node 1 Circuit 2 
Node 2 Circuit 2 
Node 0 Circuit 1 
Node 1 Circuit 1 
Node 2 Circuit 1 

अगर मैं वास्तव में Erlang पता था, मैं अलग तरह से इस कोड को सुधार कर सकता है के लिए

यहाँ मैं क्या लेकर आए हैं क्या है? और विशेष रूप से:

  • क्या बयान के अंतिम दो में कुछ भी नहीं "सत्य" खंड निर्दिष्ट करने का कोई विकल्प है?

  • Am मैं वास्तव में शान से समाप्त? क्या पंजीकृत एक प्रक्रिया समाप्त होने पर कोई विशेष कार्रवाई की आवश्यकता है?

उत्तर

6

एरलांग में आपका स्वागत है! मुझे उम्मीद है कि आप इसे जितना करते हैं उतना आनंद लेंगे।

क्या बयान के अंतिम दो में कुछ भी नहीं "सत्य" खंड निर्दिष्ट करने का कोई विकल्प है?

तुम सिर्फ इन बंद छोड़ सकते हैं। मैंने इसके साथ अपना कोड चलाया:

if NextCircuitNumber > 0 -> 
    NextNodeName ! NextCircuitNumber 
end, 
if CircuitNumber > 1 -> 
    node(NodeNumber, NumberOfNodes) 
end 

और यह मेरे लिए काम किया।

क्या मैं वास्तव में कृपापूर्वक समाप्त कर रहा हूं? क्या पंजीकृत एक प्रक्रिया समाप्त होने पर कोई विशेष कार्रवाई की आवश्यकता है?

हाँ, आप कर रहे हैं। आप i(). कमांड चलाकर इसे सत्यापित कर सकते हैं। यह आपको प्रक्रियाओं की सूची दिखाएगा, और यदि आपकी पंजीकृत प्रक्रियाएं समाप्त नहीं हो रही हैं, तो आप node0, node1 आदि जैसे बाएं अपनी पंजीकृत प्रक्रियाओं को छोड़ देंगे। आप भी अपना प्रोग्राम दूसरी बार चलाने में सक्षम नहीं होंगे, क्योंकि यह पहले से पंजीकृत नाम पंजीकृत करने का प्रयास करने में त्रुटि होगी।

जहां तक ​​आप कोड को बेहतर बनाने के लिए अन्य चीजें कर सकते हैं, वहां बहुत कुछ नहीं है क्योंकि आपका कोड मूल रूप से ठीक है। एक चीज जो मैं कर सकता हूं वह NextNodeName चर छोड़ देता है। तुम बस सीधे node_name(NextNodeNumber) करने के लिए और है कि काम करता है संदेश भेज सकते हैं।

इसके अलावा, आप शायद चीजों को बेहतर बनाने के लिए थोड़ा अधिक पैटर्न मिलान कर सकते हैं। उदाहरण के लिए, आपके कोड के साथ खेलने के दौरान किए गए एक बदलाव को NumberOfNodes पास करने के बजाय अंतिम नोड (NumberOfNodes - 1) की संख्या में गुजरकर प्रक्रियाओं को जन्म देना था।फिर, मैं कर सकता इस

node(LastNode, LastNode) -> 
    % Do things specific to the last node, like passing message back to node0 
    % and decrementing the CircuitNumber 
node(NodeNumber, LastNode) -> 
    % Do things for every other node. 

मुझे अपने node समारोह में case और if तर्क से कुछ को साफ और यह सब एक छोटे से tidier करने की अनुमति दी है कि जैसे मेरी node/2 समारोह शीर्षक में पैटर्न मैच।

आशा है कि मदद करता है, और शुभकामनाएं।

+0

अच्छी टिप्पणियां यहां! –

5

चलें कोड के माध्यम से चलना:

-module(ring). 
-export([start/2, node/2]). 

नाम node एक है मैं से बचने क्योंकि Erlang में एक नोड() कुछ मशीन पर चल रहा एक Erlang वी एम का अर्थ है - आम तौर पर कई नोड्स कई मशीनों पर चलने । मैं इसे ring_proc या ऐसा कुछ कहूंगा।

node(NodeNumber, NumberOfNodes) -> 
    NextNodeNumber = (NodeNumber + 1) rem NumberOfNodes, 
    NextNodeName = node_name(NextNodeNumber), 

इसी को हम अंडे देने के लिए कोशिश कर रहे हैं, और हम अगले नोड के लिए एक नंबर और अगले नोड का नाम मिलता है। चलो एक अन्तराल के रूप में node_name/1 को देखो:

node_name(NodeNumber) -> 
    list_to_atom(lists:flatten(io_lib:format("node~w", [NodeNumber]))). 

यह समारोह एक बुरा विचार है। आपको एक स्थानीय नाम की आवश्यकता होगी जिसे परमाणु होना चाहिए, इसलिए आपने एक ऐसा फ़ंक्शन बनाया है जो मनमाने ढंग से ऐसे नाम बना सकता है। यहां चेतावनी यह है कि परमाणु तालिका कचरा और सीमित नहीं है, इसलिए यदि संभव हो तो हमें इससे बचना चाहिए। इस समस्या को हल करने की चाल इसके बजाय पिड्स को पास करना और रिवर्स में अंगूठी बनाना है। अंतिम प्रक्रिया तो अंगूठी की शादी होगी:

mk_ring(N) -> 
    Pid = spawn(fun() -> ring(none) end), 
    mk_ring(N, Pid, Pid). 

mk_ring(0, NextPid, Initiator) -> 
    Initiator ! {set_next, NextPid}, 
    Initiator; 
mk_ring(N, NextPid, Initiator) -> 
    Pid = spawn(fun() -> ring(NextPid) end), 
    mk_ring(N-1, Pid, Initiator). 

और फिर हम अपने प्रारंभ समारोह पुनर्लेखन कर सकते हैं:

start(NumberOfNodes, NumberOfCircuits) -> 
    RingStart = mk_ring(NumberOfNodes) 
    RingStart ! {operate, NumberOfCircuits, self()}, 
    receive 
    done -> 
     RingStart ! stop 
    end, 
    ok. 

अंगूठी कोड तो की तर्ज पर कुछ है:

ring(NextPid) -> 
    receive 
    {set_next, Pid} -> 
     ring(Pid); 
    {operate, N, Who} -> 
     ring_ping(N, NextPid), 
     Who ! done, 
     ring(NextPid); 
    ping -> 
     NextPid ! ping, 
     ring(NextPid); 
    stop -> 
     NextPid ! stop, 
     ok 
    end. 

और अंगूठी के आसपास कुछ आग N बार:

ring_ping(0, _Next) -> ok; 
ring_ping(N, Next) -> 
    Next ! ping 
    receive 
    ping -> 
     ring_ping(N-1, Next) 
    end. 

(इस कोड में से कोई भी इस तरह से परीक्षण नहीं किया गया है, इसलिए यह बहुत गलत हो सकता है)।

अपने कोड से बाकी के लिए:

receive 
    CircuitNumber -> 
    io:format("Node ~p Circuit ~p~n", [NodeNumber, CircuitNumber]), 

मैं कुछ परमाणु के साथ CircuitNumber को टैग चाहते हैं: {run, CN}

NextCN = if NodeNumber =:= NumberOfNodes - 1 -> CN -1; 
       NodeNumber =/= NumberOfNodes - 1 -> CN 
      end, 

अगले भाग यहाँ:

LastNode = NodeNumber =:= NumberOfNodes - 1, 
    NextCircuitNumber = case LastNode of 
         true -> 
         CircuitNumber - 1; 
         false -> 
         CircuitNumber 
        end, 

यह एक करता है, तो साथ किया जा सकता

if 
    NextCircuitNumber > 0 -> 
     NextNodeName ! NextCircuitNumber; 
    true -> 
     ok 
    end, 
    if 
    CircuitNumber > 1 -> 
     node(NodeNumber, NumberOfNodes); 
    true -> 
     ok 
    end 

, true मामले की जरूरत है जब तक आप इसे कभी नहीं मारा। अगर if में कुछ भी मेल नहीं खाता है तो प्रक्रिया क्रैश हो जाएगी। कोड को दोबारा संभव करना संभव है क्योंकि निर्माण संकेतों पर निर्भर न करें, जैसे कि मेरे संकेतों के उपरोक्त कोड।


इस समस्या से कई परेशानियों से बचा जा सकता है। वर्तमान कोड के साथ एक समस्या यह है कि अगर अंगूठी में कुछ दुर्घटनाग्रस्त हो जाता है, तो यह टूट जाता है। हम अंगूठी को एक साथ जोड़ने के लिए spawn के बजाय spawn_link का उपयोग कर सकते हैं, इसलिए ऐसी त्रुटियां पूरी अंगूठी को नष्ट कर देगी। इसके अलावा हमारे ring_ping फ़ंक्शन क्रैश हो जाएगा जब यह रिंग चालू होने पर एक संदेश भेजा जाता है। यह कम किया जा सकता है, सबसे आसान तरीका शायद अंगूठी प्रक्रिया यह जानता है ऐसी है कि वह वर्तमान में काम कर रहा है की स्थिति में परिवर्तन और ring में ring_ping गुना है। आखिरकार, हमें शायद शुरुआती स्पॉन को भी लिंक करना चाहिए ताकि हम लाइव रिंग के साथ खत्म न हों, लेकिन किसी के पास इसका कोई संदर्भ नहीं है। शायद हम प्रारंभिक प्रक्रिया को पंजीकृत कर सकते हैं ताकि बाद में अंगूठी पकड़ना आसान हो।

start समारोह दो तरीकों से भी बुरा है। सबसे पहले, हम make_ref() का उपयोग करना चाहिए के साथ एक अद्वितीय संदेश को टैग और टैग प्राप्त करने के लिए है, तो किसी अन्य प्रक्रिया भयावह नहीं किया जा सकता और जब तक अंगूठी काम करता है बस शुरू करने के लिए प्रक्रिया done भेजें। हमें यह काम करते समय, मॉनिटर पर भी रिंग पर जोड़ना चाहिए। अन्यथा हमें कभी सूचित नहीं किया जाएगा, जब हम done संदेश (टैग के साथ) की प्रतीक्षा कर रहे हैं तो रिंग क्रैश होना चाहिए। ओटीपी दोनों तरह से इसके तुल्यकालिक कॉल में करता है।


अंत में, अंत में: नहीं, आपको पंजीकरण को साफ करने की आवश्यकता नहीं है।

3

मेरे साथियों ने कुछ उत्कृष्ट अंक बना दिया है। मैं यह भी कहना चाहूंगा कि वास्तव में एक अंगूठी बनाने की बजाय प्रक्रियाओं को पंजीकृत करके समस्या का प्रारंभिक इरादा से बचा जा रहा है।

-module(ring). 
-export([start/3]). 
-record(message, {data, rounds, total_nodes, first_node}). 

start(TotalNodes, Rounds, Data) -> 
    FirstNode = spawn_link(fun() -> loop(1, 0) end), 
    Message = #message{data=Data, rounds=Rounds, total_nodes=TotalNodes, 
         first_node=FirstNode}, 
    FirstNode ! Message, ok. 

loop(Id, NextNode) when not is_pid(NextNode) -> 
    receive 
     M=#message{total_nodes=Total, first_node=First} when Id =:= Total -> 
      First ! M, 
      loop(Id, First); 
     M=#message{} -> 
      Next = spawn_link(fun() -> loop(Id+1, 0) end), 
      Next ! M, 
      loop(Id, Next) 
    end; 
loop(Id, NextNode) -> 
    receive 
     M=#message{rounds=0} -> 
      io:format("node: ~w, stopping~n", [Id]), 
      NextNode ! M; 
     M=#message{data=D, rounds=R, total_nodes=Total} -> 
      io:format("node: ~w, message: ~p~n", [Id, D]), 
      if Id =:= Total -> NextNode ! M#message{rounds=R-1}; 
       Id =/= Total -> NextNode ! M 
      end, 
      loop(Id, NextNode) 
    end. 

यह समाधान रिकॉर्ड का उपयोग करता है: यहाँ एक संभव समाधान है। यदि आप उनके साथ अपरिचित हैं, तो उनके बारे में सभी को पढ़ें here

प्रत्येक नोड एक loop/2 समारोह द्वारा परिभाषित किया गया है। loop/2 का पहला खंड अंगूठी (निर्माण चरण) बनाने के साथ संबंधित है, और दूसरा खंड संदेश (डेटा चरण) प्रिंट करने के साथ संबंधित है। ध्यान दें कि सभी खंड rounds=0 खंड है, जो इंगित करता है कि नोड अपने कार्य के साथ किया जाता है और मौत हो जाए छोड़कर loop/2 के लिए एक कॉल में खत्म हो। यह सुंदर समाप्ति से क्या मतलब है। यह भी ध्यान दें कि निर्माण चरण में नोड को बताने के लिए इस्तेमाल किया गया हैक - NextNode एक पिड नहीं बल्कि एक पूर्णांक है।

संबंधित मुद्दे