Erlang: Make a ring










0















I'm quite new to Erlang (Reading through "Software for a Concurrent World"). From what I've read, we link two processes together to form a reliable system.



But if we need more than two process, I think we should connect them in a ring. Although this is slightly tangential to my actual question, please let me know if this is incorrect.



Given a list of PIDs:



[1,2,3,4,5]


I want to form these in a ring of My_Pid, Linked_Pid tuples:



[1,2,2,3,3,4,4,5,5,1]


I have trouble creating an elegant solution that adds the final 5,1 tuple.



Here is my attempt:



% linkedPairs takes [1,2,3] and returns [1,2,2,3]
linkedPairs() -> ;
linkedPairs([_]) -> ;
linkedPairs([X1,X2|Xs]) -> [X1, X2 | linkedPairs([X2|Xs])].

% joinLinks takes [1,2,2,3] and returns [1,2,2,3,3,1]
joinLinks([A, _|_]=P) ->
X, Y = lists:last(P)
P ++ [Y, A].

% makeRing takes [1,2,3] and returns [1,2,2,3,3,1]
makeRing(PIDs) -> joinLinks(linkedPairs(PIDs)).


I cringe when looking at my joinLinks function - list:last is slow (I think), and it doesn't look very "functional".



Is there a better, more idiomatic solution to this?



If other functional programmers (non-Erlang) stumble upon this, please post your solution - the concepts are the same.










share|improve this question




























    0















    I'm quite new to Erlang (Reading through "Software for a Concurrent World"). From what I've read, we link two processes together to form a reliable system.



    But if we need more than two process, I think we should connect them in a ring. Although this is slightly tangential to my actual question, please let me know if this is incorrect.



    Given a list of PIDs:



    [1,2,3,4,5]


    I want to form these in a ring of My_Pid, Linked_Pid tuples:



    [1,2,2,3,3,4,4,5,5,1]


    I have trouble creating an elegant solution that adds the final 5,1 tuple.



    Here is my attempt:



    % linkedPairs takes [1,2,3] and returns [1,2,2,3]
    linkedPairs() -> ;
    linkedPairs([_]) -> ;
    linkedPairs([X1,X2|Xs]) -> [X1, X2 | linkedPairs([X2|Xs])].

    % joinLinks takes [1,2,2,3] and returns [1,2,2,3,3,1]
    joinLinks([A, _|_]=P) ->
    X, Y = lists:last(P)
    P ++ [Y, A].

    % makeRing takes [1,2,3] and returns [1,2,2,3,3,1]
    makeRing(PIDs) -> joinLinks(linkedPairs(PIDs)).


    I cringe when looking at my joinLinks function - list:last is slow (I think), and it doesn't look very "functional".



    Is there a better, more idiomatic solution to this?



    If other functional programmers (non-Erlang) stumble upon this, please post your solution - the concepts are the same.










    share|improve this question


























      0












      0








      0








      I'm quite new to Erlang (Reading through "Software for a Concurrent World"). From what I've read, we link two processes together to form a reliable system.



      But if we need more than two process, I think we should connect them in a ring. Although this is slightly tangential to my actual question, please let me know if this is incorrect.



      Given a list of PIDs:



      [1,2,3,4,5]


      I want to form these in a ring of My_Pid, Linked_Pid tuples:



      [1,2,2,3,3,4,4,5,5,1]


      I have trouble creating an elegant solution that adds the final 5,1 tuple.



      Here is my attempt:



      % linkedPairs takes [1,2,3] and returns [1,2,2,3]
      linkedPairs() -> ;
      linkedPairs([_]) -> ;
      linkedPairs([X1,X2|Xs]) -> [X1, X2 | linkedPairs([X2|Xs])].

      % joinLinks takes [1,2,2,3] and returns [1,2,2,3,3,1]
      joinLinks([A, _|_]=P) ->
      X, Y = lists:last(P)
      P ++ [Y, A].

      % makeRing takes [1,2,3] and returns [1,2,2,3,3,1]
      makeRing(PIDs) -> joinLinks(linkedPairs(PIDs)).


      I cringe when looking at my joinLinks function - list:last is slow (I think), and it doesn't look very "functional".



      Is there a better, more idiomatic solution to this?



      If other functional programmers (non-Erlang) stumble upon this, please post your solution - the concepts are the same.










      share|improve this question
















      I'm quite new to Erlang (Reading through "Software for a Concurrent World"). From what I've read, we link two processes together to form a reliable system.



      But if we need more than two process, I think we should connect them in a ring. Although this is slightly tangential to my actual question, please let me know if this is incorrect.



      Given a list of PIDs:



      [1,2,3,4,5]


      I want to form these in a ring of My_Pid, Linked_Pid tuples:



      [1,2,2,3,3,4,4,5,5,1]


      I have trouble creating an elegant solution that adds the final 5,1 tuple.



      Here is my attempt:



      % linkedPairs takes [1,2,3] and returns [1,2,2,3]
      linkedPairs() -> ;
      linkedPairs([_]) -> ;
      linkedPairs([X1,X2|Xs]) -> [X1, X2 | linkedPairs([X2|Xs])].

      % joinLinks takes [1,2,2,3] and returns [1,2,2,3,3,1]
      joinLinks([A, _|_]=P) ->
      X, Y = lists:last(P)
      P ++ [Y, A].

      % makeRing takes [1,2,3] and returns [1,2,2,3,3,1]
      makeRing(PIDs) -> joinLinks(linkedPairs(PIDs)).


      I cringe when looking at my joinLinks function - list:last is slow (I think), and it doesn't look very "functional".



      Is there a better, more idiomatic solution to this?



      If other functional programmers (non-Erlang) stumble upon this, please post your solution - the concepts are the same.







      functional-programming erlang






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 13 '18 at 22:00









      Justin Wood

      7,05822037




      7,05822037










      asked Nov 13 '18 at 7:33









      hazhaz

      320422




      320422






















          3 Answers
          3






          active

          oldest

          votes


















          1















          But if we need more than two process, I think we should connect them
          in a ring.




          No. For instance, suppose you want to download the text of 10 different web pages. Instead of sending a request, then waiting for the server to respond, then sending the next request, etc., you can spawn a separate process for each request. Each spawned process only needs the pid of the main process, and the main process collects the results as they come in. When a spawned process gets a reply from a server, the spawned process sends a message to the main process with the results, then terminates. The spawned processes have no reason to send messages to each other. No ring.



          I would guess that it is unlikely that you will ever create a ring of processes in your erlang career.




          I have trouble creating an elegant solution that adds the final 5,1 tuple.




          You can create the four other processes passing them self(), which will be different for each spawned process. Then, you can create a separate branch of your create_ring() function that terminates the recursion and returns the pid of the last created process to the main process:



          init(N) ->
          LastPid = create_ring(....),

          create_ring(0, PrevPid) -> PrevPid;
          create_ring(N, PrevPid) when N > 0 ->
          Pid = spawn(?MODULE, loop, [PrevPid]),
          create_ring(.......).


          Then, the main process can call (not spawn) the same function that is being spawned by the other processes, passing the function the last pid that was returned by the create_ring() function:



          init(N) ->
          LastPid = create_ring(...),
          loop(LastPid).


          As a result, the main process will enter into the same message loop as the other processes, and the main process will have the last pid stored in the loop parameter variable to send messages to.



          In erlang, you will often find that while you are defining a function, you won't be able to do everything that you want in that function, so you need to call another function to do whatever it is that is giving you trouble, and if in the second function you find you can't do everything you need to do, then you need to call another function, etc. Applied to the ring problem above, I found that init() couldn't do everything I wanted in one function, so I defined the create_ring() function to handle part of the problem.






          share|improve this answer
































            3














            Use lists:zip with the original list and its 'rotated' version:



            1> L=[1,2,3].
            [1,2,3]
            2> lists:zip(L, tl(L) ++ [hd(L)]).
            [1,2,2,3,3,1]





            share|improve this answer























            • Ah, that's a great way to rotate it – I was searching for a rotate function. Cheers!

              – haz
              Nov 13 '18 at 7:44


















            2














            If you are manipulating long lists, you can avoid the creation of the intermediary list tl(L) ++ [hd(L)] using an helper function:



            1> L = lists:seq(1,5).
            [1,2,3,4,5]
            2> Link = fun Link([Last],First,Acc) -> lists:reverse([Last,First|Acc]);
            Link([X|T],First,Acc) -> Link(T,First,[X,hd(T)|Acc]) end.
            #Fun<erl_eval.42.127694169>
            3> Joinlinks = fun(List) -> Link(List,hd(List),) end.
            #Fun<erl_eval.6.127694169>
            4> Joinlinks(L).
            [1,2,2,3,3,4,4,5,5,1]
            5>





            share|improve this answer






















              Your Answer






              StackExchange.ifUsing("editor", function ()
              StackExchange.using("externalEditor", function ()
              StackExchange.using("snippets", function ()
              StackExchange.snippets.init();
              );
              );
              , "code-snippets");

              StackExchange.ready(function()
              var channelOptions =
              tags: "".split(" "),
              id: "1"
              ;
              initTagRenderer("".split(" "), "".split(" "), channelOptions);

              StackExchange.using("externalEditor", function()
              // Have to fire editor after snippets, if snippets enabled
              if (StackExchange.settings.snippets.snippetsEnabled)
              StackExchange.using("snippets", function()
              createEditor();
              );

              else
              createEditor();

              );

              function createEditor()
              StackExchange.prepareEditor(
              heartbeatType: 'answer',
              autoActivateHeartbeat: false,
              convertImagesToLinks: true,
              noModals: true,
              showLowRepImageUploadWarning: true,
              reputationToPostImages: 10,
              bindNavPrevention: true,
              postfix: "",
              imageUploader:
              brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
              contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
              allowUrls: true
              ,
              onDemand: true,
              discardSelector: ".discard-answer"
              ,immediatelyShowMarkdownHelp:true
              );



              );













              draft saved

              draft discarded


















              StackExchange.ready(
              function ()
              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53275962%2ferlang-make-a-ring%23new-answer', 'question_page');

              );

              Post as a guest















              Required, but never shown

























              3 Answers
              3






              active

              oldest

              votes








              3 Answers
              3






              active

              oldest

              votes









              active

              oldest

              votes






              active

              oldest

              votes









              1















              But if we need more than two process, I think we should connect them
              in a ring.




              No. For instance, suppose you want to download the text of 10 different web pages. Instead of sending a request, then waiting for the server to respond, then sending the next request, etc., you can spawn a separate process for each request. Each spawned process only needs the pid of the main process, and the main process collects the results as they come in. When a spawned process gets a reply from a server, the spawned process sends a message to the main process with the results, then terminates. The spawned processes have no reason to send messages to each other. No ring.



              I would guess that it is unlikely that you will ever create a ring of processes in your erlang career.




              I have trouble creating an elegant solution that adds the final 5,1 tuple.




              You can create the four other processes passing them self(), which will be different for each spawned process. Then, you can create a separate branch of your create_ring() function that terminates the recursion and returns the pid of the last created process to the main process:



              init(N) ->
              LastPid = create_ring(....),

              create_ring(0, PrevPid) -> PrevPid;
              create_ring(N, PrevPid) when N > 0 ->
              Pid = spawn(?MODULE, loop, [PrevPid]),
              create_ring(.......).


              Then, the main process can call (not spawn) the same function that is being spawned by the other processes, passing the function the last pid that was returned by the create_ring() function:



              init(N) ->
              LastPid = create_ring(...),
              loop(LastPid).


              As a result, the main process will enter into the same message loop as the other processes, and the main process will have the last pid stored in the loop parameter variable to send messages to.



              In erlang, you will often find that while you are defining a function, you won't be able to do everything that you want in that function, so you need to call another function to do whatever it is that is giving you trouble, and if in the second function you find you can't do everything you need to do, then you need to call another function, etc. Applied to the ring problem above, I found that init() couldn't do everything I wanted in one function, so I defined the create_ring() function to handle part of the problem.






              share|improve this answer





























                1















                But if we need more than two process, I think we should connect them
                in a ring.




                No. For instance, suppose you want to download the text of 10 different web pages. Instead of sending a request, then waiting for the server to respond, then sending the next request, etc., you can spawn a separate process for each request. Each spawned process only needs the pid of the main process, and the main process collects the results as they come in. When a spawned process gets a reply from a server, the spawned process sends a message to the main process with the results, then terminates. The spawned processes have no reason to send messages to each other. No ring.



                I would guess that it is unlikely that you will ever create a ring of processes in your erlang career.




                I have trouble creating an elegant solution that adds the final 5,1 tuple.




                You can create the four other processes passing them self(), which will be different for each spawned process. Then, you can create a separate branch of your create_ring() function that terminates the recursion and returns the pid of the last created process to the main process:



                init(N) ->
                LastPid = create_ring(....),

                create_ring(0, PrevPid) -> PrevPid;
                create_ring(N, PrevPid) when N > 0 ->
                Pid = spawn(?MODULE, loop, [PrevPid]),
                create_ring(.......).


                Then, the main process can call (not spawn) the same function that is being spawned by the other processes, passing the function the last pid that was returned by the create_ring() function:



                init(N) ->
                LastPid = create_ring(...),
                loop(LastPid).


                As a result, the main process will enter into the same message loop as the other processes, and the main process will have the last pid stored in the loop parameter variable to send messages to.



                In erlang, you will often find that while you are defining a function, you won't be able to do everything that you want in that function, so you need to call another function to do whatever it is that is giving you trouble, and if in the second function you find you can't do everything you need to do, then you need to call another function, etc. Applied to the ring problem above, I found that init() couldn't do everything I wanted in one function, so I defined the create_ring() function to handle part of the problem.






                share|improve this answer



























                  1












                  1








                  1








                  But if we need more than two process, I think we should connect them
                  in a ring.




                  No. For instance, suppose you want to download the text of 10 different web pages. Instead of sending a request, then waiting for the server to respond, then sending the next request, etc., you can spawn a separate process for each request. Each spawned process only needs the pid of the main process, and the main process collects the results as they come in. When a spawned process gets a reply from a server, the spawned process sends a message to the main process with the results, then terminates. The spawned processes have no reason to send messages to each other. No ring.



                  I would guess that it is unlikely that you will ever create a ring of processes in your erlang career.




                  I have trouble creating an elegant solution that adds the final 5,1 tuple.




                  You can create the four other processes passing them self(), which will be different for each spawned process. Then, you can create a separate branch of your create_ring() function that terminates the recursion and returns the pid of the last created process to the main process:



                  init(N) ->
                  LastPid = create_ring(....),

                  create_ring(0, PrevPid) -> PrevPid;
                  create_ring(N, PrevPid) when N > 0 ->
                  Pid = spawn(?MODULE, loop, [PrevPid]),
                  create_ring(.......).


                  Then, the main process can call (not spawn) the same function that is being spawned by the other processes, passing the function the last pid that was returned by the create_ring() function:



                  init(N) ->
                  LastPid = create_ring(...),
                  loop(LastPid).


                  As a result, the main process will enter into the same message loop as the other processes, and the main process will have the last pid stored in the loop parameter variable to send messages to.



                  In erlang, you will often find that while you are defining a function, you won't be able to do everything that you want in that function, so you need to call another function to do whatever it is that is giving you trouble, and if in the second function you find you can't do everything you need to do, then you need to call another function, etc. Applied to the ring problem above, I found that init() couldn't do everything I wanted in one function, so I defined the create_ring() function to handle part of the problem.






                  share|improve this answer
















                  But if we need more than two process, I think we should connect them
                  in a ring.




                  No. For instance, suppose you want to download the text of 10 different web pages. Instead of sending a request, then waiting for the server to respond, then sending the next request, etc., you can spawn a separate process for each request. Each spawned process only needs the pid of the main process, and the main process collects the results as they come in. When a spawned process gets a reply from a server, the spawned process sends a message to the main process with the results, then terminates. The spawned processes have no reason to send messages to each other. No ring.



                  I would guess that it is unlikely that you will ever create a ring of processes in your erlang career.




                  I have trouble creating an elegant solution that adds the final 5,1 tuple.




                  You can create the four other processes passing them self(), which will be different for each spawned process. Then, you can create a separate branch of your create_ring() function that terminates the recursion and returns the pid of the last created process to the main process:



                  init(N) ->
                  LastPid = create_ring(....),

                  create_ring(0, PrevPid) -> PrevPid;
                  create_ring(N, PrevPid) when N > 0 ->
                  Pid = spawn(?MODULE, loop, [PrevPid]),
                  create_ring(.......).


                  Then, the main process can call (not spawn) the same function that is being spawned by the other processes, passing the function the last pid that was returned by the create_ring() function:



                  init(N) ->
                  LastPid = create_ring(...),
                  loop(LastPid).


                  As a result, the main process will enter into the same message loop as the other processes, and the main process will have the last pid stored in the loop parameter variable to send messages to.



                  In erlang, you will often find that while you are defining a function, you won't be able to do everything that you want in that function, so you need to call another function to do whatever it is that is giving you trouble, and if in the second function you find you can't do everything you need to do, then you need to call another function, etc. Applied to the ring problem above, I found that init() couldn't do everything I wanted in one function, so I defined the create_ring() function to handle part of the problem.







                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited Nov 14 '18 at 0:19

























                  answered Nov 13 '18 at 23:17









                  7stud7stud

                  29.4k96083




                  29.4k96083























                      3














                      Use lists:zip with the original list and its 'rotated' version:



                      1> L=[1,2,3].
                      [1,2,3]
                      2> lists:zip(L, tl(L) ++ [hd(L)]).
                      [1,2,2,3,3,1]





                      share|improve this answer























                      • Ah, that's a great way to rotate it – I was searching for a rotate function. Cheers!

                        – haz
                        Nov 13 '18 at 7:44















                      3














                      Use lists:zip with the original list and its 'rotated' version:



                      1> L=[1,2,3].
                      [1,2,3]
                      2> lists:zip(L, tl(L) ++ [hd(L)]).
                      [1,2,2,3,3,1]





                      share|improve this answer























                      • Ah, that's a great way to rotate it – I was searching for a rotate function. Cheers!

                        – haz
                        Nov 13 '18 at 7:44













                      3












                      3








                      3







                      Use lists:zip with the original list and its 'rotated' version:



                      1> L=[1,2,3].
                      [1,2,3]
                      2> lists:zip(L, tl(L) ++ [hd(L)]).
                      [1,2,2,3,3,1]





                      share|improve this answer













                      Use lists:zip with the original list and its 'rotated' version:



                      1> L=[1,2,3].
                      [1,2,3]
                      2> lists:zip(L, tl(L) ++ [hd(L)]).
                      [1,2,2,3,3,1]






                      share|improve this answer












                      share|improve this answer



                      share|improve this answer










                      answered Nov 13 '18 at 7:36









                      pdexterpdexter

                      696614




                      696614












                      • Ah, that's a great way to rotate it – I was searching for a rotate function. Cheers!

                        – haz
                        Nov 13 '18 at 7:44

















                      • Ah, that's a great way to rotate it – I was searching for a rotate function. Cheers!

                        – haz
                        Nov 13 '18 at 7:44
















                      Ah, that's a great way to rotate it – I was searching for a rotate function. Cheers!

                      – haz
                      Nov 13 '18 at 7:44





                      Ah, that's a great way to rotate it – I was searching for a rotate function. Cheers!

                      – haz
                      Nov 13 '18 at 7:44











                      2














                      If you are manipulating long lists, you can avoid the creation of the intermediary list tl(L) ++ [hd(L)] using an helper function:



                      1> L = lists:seq(1,5).
                      [1,2,3,4,5]
                      2> Link = fun Link([Last],First,Acc) -> lists:reverse([Last,First|Acc]);
                      Link([X|T],First,Acc) -> Link(T,First,[X,hd(T)|Acc]) end.
                      #Fun<erl_eval.42.127694169>
                      3> Joinlinks = fun(List) -> Link(List,hd(List),) end.
                      #Fun<erl_eval.6.127694169>
                      4> Joinlinks(L).
                      [1,2,2,3,3,4,4,5,5,1]
                      5>





                      share|improve this answer



























                        2














                        If you are manipulating long lists, you can avoid the creation of the intermediary list tl(L) ++ [hd(L)] using an helper function:



                        1> L = lists:seq(1,5).
                        [1,2,3,4,5]
                        2> Link = fun Link([Last],First,Acc) -> lists:reverse([Last,First|Acc]);
                        Link([X|T],First,Acc) -> Link(T,First,[X,hd(T)|Acc]) end.
                        #Fun<erl_eval.42.127694169>
                        3> Joinlinks = fun(List) -> Link(List,hd(List),) end.
                        #Fun<erl_eval.6.127694169>
                        4> Joinlinks(L).
                        [1,2,2,3,3,4,4,5,5,1]
                        5>





                        share|improve this answer

























                          2












                          2








                          2







                          If you are manipulating long lists, you can avoid the creation of the intermediary list tl(L) ++ [hd(L)] using an helper function:



                          1> L = lists:seq(1,5).
                          [1,2,3,4,5]
                          2> Link = fun Link([Last],First,Acc) -> lists:reverse([Last,First|Acc]);
                          Link([X|T],First,Acc) -> Link(T,First,[X,hd(T)|Acc]) end.
                          #Fun<erl_eval.42.127694169>
                          3> Joinlinks = fun(List) -> Link(List,hd(List),) end.
                          #Fun<erl_eval.6.127694169>
                          4> Joinlinks(L).
                          [1,2,2,3,3,4,4,5,5,1]
                          5>





                          share|improve this answer













                          If you are manipulating long lists, you can avoid the creation of the intermediary list tl(L) ++ [hd(L)] using an helper function:



                          1> L = lists:seq(1,5).
                          [1,2,3,4,5]
                          2> Link = fun Link([Last],First,Acc) -> lists:reverse([Last,First|Acc]);
                          Link([X|T],First,Acc) -> Link(T,First,[X,hd(T)|Acc]) end.
                          #Fun<erl_eval.42.127694169>
                          3> Joinlinks = fun(List) -> Link(List,hd(List),) end.
                          #Fun<erl_eval.6.127694169>
                          4> Joinlinks(L).
                          [1,2,2,3,3,4,4,5,5,1]
                          5>






                          share|improve this answer












                          share|improve this answer



                          share|improve this answer










                          answered Nov 13 '18 at 12:45









                          PascalPascal

                          11.6k11425




                          11.6k11425



























                              draft saved

                              draft discarded
















































                              Thanks for contributing an answer to Stack Overflow!


                              • Please be sure to answer the question. Provide details and share your research!

                              But avoid


                              • Asking for help, clarification, or responding to other answers.

                              • Making statements based on opinion; back them up with references or personal experience.

                              To learn more, see our tips on writing great answers.




                              draft saved


                              draft discarded














                              StackExchange.ready(
                              function ()
                              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53275962%2ferlang-make-a-ring%23new-answer', 'question_page');

                              );

                              Post as a guest















                              Required, but never shown





















































                              Required, but never shown














                              Required, but never shown












                              Required, but never shown







                              Required, but never shown

































                              Required, but never shown














                              Required, but never shown












                              Required, but never shown







                              Required, but never shown







                              這個網誌中的熱門文章

                              Barbados

                              How to read a connectionString WITH PROVIDER in .NET Core?

                              Node.js Script on GitHub Pages or Amazon S3