Bottom up merge sort in Haskell

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
2
down vote

favorite












Although this sort works on Int lists < length million, when the stack overflows, the last clause of the merge function definition looks odd to me, recursing in 2 places. Is there clearer way to define that definition and how can the stack limitation be avoided?



msort :: Ord a => [a] -> [a]
msort =
msort xs = concat . merge . split $ xs

merge :: Ord a => [[a]] -> [[a]]
merge =
merge [x] = [x]
merge (l1:l2:ls) = merge $ mpair l1 l2 : merge ls

mpair :: Ord a => [a] -> [a] -> [a]
mpair l2 = l2
mpair l1 = l1
mpair l1@(x:xs) l2@(y:ys) | x >= y = x : mpair xs l2
| otherwise = y : mpair l1 ys

split :: [a] -> [[a]]
split xs = [[x]| x<-xs]






share|improve this question

























    up vote
    2
    down vote

    favorite












    Although this sort works on Int lists < length million, when the stack overflows, the last clause of the merge function definition looks odd to me, recursing in 2 places. Is there clearer way to define that definition and how can the stack limitation be avoided?



    msort :: Ord a => [a] -> [a]
    msort =
    msort xs = concat . merge . split $ xs

    merge :: Ord a => [[a]] -> [[a]]
    merge =
    merge [x] = [x]
    merge (l1:l2:ls) = merge $ mpair l1 l2 : merge ls

    mpair :: Ord a => [a] -> [a] -> [a]
    mpair l2 = l2
    mpair l1 = l1
    mpair l1@(x:xs) l2@(y:ys) | x >= y = x : mpair xs l2
    | otherwise = y : mpair l1 ys

    split :: [a] -> [[a]]
    split xs = [[x]| x<-xs]






    share|improve this question





















      up vote
      2
      down vote

      favorite









      up vote
      2
      down vote

      favorite











      Although this sort works on Int lists < length million, when the stack overflows, the last clause of the merge function definition looks odd to me, recursing in 2 places. Is there clearer way to define that definition and how can the stack limitation be avoided?



      msort :: Ord a => [a] -> [a]
      msort =
      msort xs = concat . merge . split $ xs

      merge :: Ord a => [[a]] -> [[a]]
      merge =
      merge [x] = [x]
      merge (l1:l2:ls) = merge $ mpair l1 l2 : merge ls

      mpair :: Ord a => [a] -> [a] -> [a]
      mpair l2 = l2
      mpair l1 = l1
      mpair l1@(x:xs) l2@(y:ys) | x >= y = x : mpair xs l2
      | otherwise = y : mpair l1 ys

      split :: [a] -> [[a]]
      split xs = [[x]| x<-xs]






      share|improve this question











      Although this sort works on Int lists < length million, when the stack overflows, the last clause of the merge function definition looks odd to me, recursing in 2 places. Is there clearer way to define that definition and how can the stack limitation be avoided?



      msort :: Ord a => [a] -> [a]
      msort =
      msort xs = concat . merge . split $ xs

      merge :: Ord a => [[a]] -> [[a]]
      merge =
      merge [x] = [x]
      merge (l1:l2:ls) = merge $ mpair l1 l2 : merge ls

      mpair :: Ord a => [a] -> [a] -> [a]
      mpair l2 = l2
      mpair l1 = l1
      mpair l1@(x:xs) l2@(y:ys) | x >= y = x : mpair xs l2
      | otherwise = y : mpair l1 ys

      split :: [a] -> [[a]]
      split xs = [[x]| x<-xs]








      share|improve this question










      share|improve this question




      share|improve this question









      asked Jul 11 at 22:45









      newbie123

      111




      111




















          2 Answers
          2






          active

          oldest

          votes

















          up vote
          0
          down vote













          You've spotted the problem. Your merge function is flawed. For each pair it processes, it introduces an extra unnecessary call to merge. That is, for a list like [1,2,3,4,5,6], instead of your first merge call expanding directly to:



          merge [[1],[2],[3],[4],[5],[6]]
          = merge $ mpair [1] [2] : mpair [3] [4] : mpair [5] [6] :


          it expands to:



          merge [[1],[2],[3],[4],[5],[6]]
          = merge $ mpair [1] [2] : merge $ mpair [3] [4]
          : merge $ mpair [5] [6] : merge


          As a result, your count of merge calls is O(n) and your count of mpair calls is O(n^2) (or similar -- I didn't check exactly). when they should be O(log n) and O(n log n) respectively.



          Instead, you want to split merge up into two functions:



          merge :: Ord a => [[a]] -> [[a]]
          merge =
          merge [x] = [x]
          merge ls = merge (mergePairs ls)

          mergePairs :: Ord a => [[a]] -> [[a]]
          mergePairs (l1:l2:ls) = mpair l1 l2 : mergePairs ls
          mergePairs ls = ls


          This will speed up the algorithm enormously, and it will now run on tens of millions of integers.






          share|improve this answer






























            up vote
            -1
            down vote













            See mergeAll.



            They insert a bang to adjust the evaluation order, and push the second recursion down into the next definition.



            You could experiment with the mutant offspring of the two implementations to try and understand reasons for their difference.






            share|improve this answer





















              Your Answer




              StackExchange.ifUsing("editor", function ()
              return StackExchange.using("mathjaxEditing", function ()
              StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
              StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
              );
              );
              , "mathjax-editing");

              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: "196"
              ;
              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',
              convertImagesToLinks: false,
              noModals: false,
              showLowRepImageUploadWarning: true,
              reputationToPostImages: null,
              bindNavPrevention: true,
              postfix: "",
              onDemand: true,
              discardSelector: ".discard-answer"
              ,immediatelyShowMarkdownHelp:true
              );



              );








               

              draft saved


              draft discarded


















              StackExchange.ready(
              function ()
              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f198330%2fbottom-up-merge-sort-in-haskell%23new-answer', 'question_page');

              );

              Post as a guest






























              2 Answers
              2






              active

              oldest

              votes








              2 Answers
              2






              active

              oldest

              votes









              active

              oldest

              votes






              active

              oldest

              votes








              up vote
              0
              down vote













              You've spotted the problem. Your merge function is flawed. For each pair it processes, it introduces an extra unnecessary call to merge. That is, for a list like [1,2,3,4,5,6], instead of your first merge call expanding directly to:



              merge [[1],[2],[3],[4],[5],[6]]
              = merge $ mpair [1] [2] : mpair [3] [4] : mpair [5] [6] :


              it expands to:



              merge [[1],[2],[3],[4],[5],[6]]
              = merge $ mpair [1] [2] : merge $ mpair [3] [4]
              : merge $ mpair [5] [6] : merge


              As a result, your count of merge calls is O(n) and your count of mpair calls is O(n^2) (or similar -- I didn't check exactly). when they should be O(log n) and O(n log n) respectively.



              Instead, you want to split merge up into two functions:



              merge :: Ord a => [[a]] -> [[a]]
              merge =
              merge [x] = [x]
              merge ls = merge (mergePairs ls)

              mergePairs :: Ord a => [[a]] -> [[a]]
              mergePairs (l1:l2:ls) = mpair l1 l2 : mergePairs ls
              mergePairs ls = ls


              This will speed up the algorithm enormously, and it will now run on tens of millions of integers.






              share|improve this answer



























                up vote
                0
                down vote













                You've spotted the problem. Your merge function is flawed. For each pair it processes, it introduces an extra unnecessary call to merge. That is, for a list like [1,2,3,4,5,6], instead of your first merge call expanding directly to:



                merge [[1],[2],[3],[4],[5],[6]]
                = merge $ mpair [1] [2] : mpair [3] [4] : mpair [5] [6] :


                it expands to:



                merge [[1],[2],[3],[4],[5],[6]]
                = merge $ mpair [1] [2] : merge $ mpair [3] [4]
                : merge $ mpair [5] [6] : merge


                As a result, your count of merge calls is O(n) and your count of mpair calls is O(n^2) (or similar -- I didn't check exactly). when they should be O(log n) and O(n log n) respectively.



                Instead, you want to split merge up into two functions:



                merge :: Ord a => [[a]] -> [[a]]
                merge =
                merge [x] = [x]
                merge ls = merge (mergePairs ls)

                mergePairs :: Ord a => [[a]] -> [[a]]
                mergePairs (l1:l2:ls) = mpair l1 l2 : mergePairs ls
                mergePairs ls = ls


                This will speed up the algorithm enormously, and it will now run on tens of millions of integers.






                share|improve this answer

























                  up vote
                  0
                  down vote










                  up vote
                  0
                  down vote









                  You've spotted the problem. Your merge function is flawed. For each pair it processes, it introduces an extra unnecessary call to merge. That is, for a list like [1,2,3,4,5,6], instead of your first merge call expanding directly to:



                  merge [[1],[2],[3],[4],[5],[6]]
                  = merge $ mpair [1] [2] : mpair [3] [4] : mpair [5] [6] :


                  it expands to:



                  merge [[1],[2],[3],[4],[5],[6]]
                  = merge $ mpair [1] [2] : merge $ mpair [3] [4]
                  : merge $ mpair [5] [6] : merge


                  As a result, your count of merge calls is O(n) and your count of mpair calls is O(n^2) (or similar -- I didn't check exactly). when they should be O(log n) and O(n log n) respectively.



                  Instead, you want to split merge up into two functions:



                  merge :: Ord a => [[a]] -> [[a]]
                  merge =
                  merge [x] = [x]
                  merge ls = merge (mergePairs ls)

                  mergePairs :: Ord a => [[a]] -> [[a]]
                  mergePairs (l1:l2:ls) = mpair l1 l2 : mergePairs ls
                  mergePairs ls = ls


                  This will speed up the algorithm enormously, and it will now run on tens of millions of integers.






                  share|improve this answer















                  You've spotted the problem. Your merge function is flawed. For each pair it processes, it introduces an extra unnecessary call to merge. That is, for a list like [1,2,3,4,5,6], instead of your first merge call expanding directly to:



                  merge [[1],[2],[3],[4],[5],[6]]
                  = merge $ mpair [1] [2] : mpair [3] [4] : mpair [5] [6] :


                  it expands to:



                  merge [[1],[2],[3],[4],[5],[6]]
                  = merge $ mpair [1] [2] : merge $ mpair [3] [4]
                  : merge $ mpair [5] [6] : merge


                  As a result, your count of merge calls is O(n) and your count of mpair calls is O(n^2) (or similar -- I didn't check exactly). when they should be O(log n) and O(n log n) respectively.



                  Instead, you want to split merge up into two functions:



                  merge :: Ord a => [[a]] -> [[a]]
                  merge =
                  merge [x] = [x]
                  merge ls = merge (mergePairs ls)

                  mergePairs :: Ord a => [[a]] -> [[a]]
                  mergePairs (l1:l2:ls) = mpair l1 l2 : mergePairs ls
                  mergePairs ls = ls


                  This will speed up the algorithm enormously, and it will now run on tens of millions of integers.







                  share|improve this answer















                  share|improve this answer



                  share|improve this answer








                  edited Jul 17 at 23:21


























                  answered Jul 17 at 22:56









                  K. A. Buhr

                  2263




                  2263






















                      up vote
                      -1
                      down vote













                      See mergeAll.



                      They insert a bang to adjust the evaluation order, and push the second recursion down into the next definition.



                      You could experiment with the mutant offspring of the two implementations to try and understand reasons for their difference.






                      share|improve this answer

























                        up vote
                        -1
                        down vote













                        See mergeAll.



                        They insert a bang to adjust the evaluation order, and push the second recursion down into the next definition.



                        You could experiment with the mutant offspring of the two implementations to try and understand reasons for their difference.






                        share|improve this answer























                          up vote
                          -1
                          down vote










                          up vote
                          -1
                          down vote









                          See mergeAll.



                          They insert a bang to adjust the evaluation order, and push the second recursion down into the next definition.



                          You could experiment with the mutant offspring of the two implementations to try and understand reasons for their difference.






                          share|improve this answer













                          See mergeAll.



                          They insert a bang to adjust the evaluation order, and push the second recursion down into the next definition.



                          You could experiment with the mutant offspring of the two implementations to try and understand reasons for their difference.







                          share|improve this answer













                          share|improve this answer



                          share|improve this answer











                          answered Jul 12 at 11:14









                          Gurkenglas

                          2,648411




                          2,648411






















                               

                              draft saved


                              draft discarded


























                               


                              draft saved


                              draft discarded














                              StackExchange.ready(
                              function ()
                              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f198330%2fbottom-up-merge-sort-in-haskell%23new-answer', 'question_page');

                              );

                              Post as a guest













































































                              Popular posts from this blog

                              Chat program with C++ and SFML

                              Function to Return a JSON Like Objects Using VBA Collections and Arrays

                              Will my employers contract hold up in court?