Porting Haskell list comprehensions to Standard ML












4












$begingroup$


Out of curiosity I'm porting the Haskell exercises of Programming in Haskell by Graham Hutton to Standard ML. At the beginning everything seemed pretty similar until I reached list comprehensions:




A triple (x,y,z) of positive integers is called pythagorean if x2 + y2 = z2. Using a list comprehension, define a function pyths ∷ Int → [(Int,Int,Int)] that maps an integer n to all such triples with components in [1..n].




The Haskell version is pretty simple:



pyths ∷ Int → [(Int, Int, Int)]
pyths n = [(x, y, z) |
x ← [1..n],
y ← [1..n],
z ← [1..n],
x^2 + y^2 == z^2]


My Standard ML attempt not so much:



(* No list comprehensions in SML so we need a helper function *)
val generateIntsUpTo = fn n =>
let val rec helper = fn current => fn xs =>
if current <= n
(* then current :: helper (current + 1) xs *)
then helper (current + 1) (current :: xs)
else xs
in
rev (helper 1 )
end

(* Now we can find the pyths *)
val pyths = fn n =>
let
val numbers = generateIntsUpTo n
in
foldr (fn (x, acc) =>
foldr (fn (y, acc) =>
foldr (fn (z, acc) =>
if x * x + y * y = z * z
then (x, y, z) :: acc
else acc
) acc numbers
) acc numbers
) numbers
end


The first thing that I don't like is the call to rev at the end of generateIntsUpTo but I did it so I could accomplish a tail recursive function. Commented out is the line that let's me avoid the reversion of the list but then it's not tail recursive.



Besides that the three nested foldr calls. I have been thinking on write my own recursive functions but I wanted to do it using the minimum amount of custom code.










share|improve this question











$endgroup$

















    4












    $begingroup$


    Out of curiosity I'm porting the Haskell exercises of Programming in Haskell by Graham Hutton to Standard ML. At the beginning everything seemed pretty similar until I reached list comprehensions:




    A triple (x,y,z) of positive integers is called pythagorean if x2 + y2 = z2. Using a list comprehension, define a function pyths ∷ Int → [(Int,Int,Int)] that maps an integer n to all such triples with components in [1..n].




    The Haskell version is pretty simple:



    pyths ∷ Int → [(Int, Int, Int)]
    pyths n = [(x, y, z) |
    x ← [1..n],
    y ← [1..n],
    z ← [1..n],
    x^2 + y^2 == z^2]


    My Standard ML attempt not so much:



    (* No list comprehensions in SML so we need a helper function *)
    val generateIntsUpTo = fn n =>
    let val rec helper = fn current => fn xs =>
    if current <= n
    (* then current :: helper (current + 1) xs *)
    then helper (current + 1) (current :: xs)
    else xs
    in
    rev (helper 1 )
    end

    (* Now we can find the pyths *)
    val pyths = fn n =>
    let
    val numbers = generateIntsUpTo n
    in
    foldr (fn (x, acc) =>
    foldr (fn (y, acc) =>
    foldr (fn (z, acc) =>
    if x * x + y * y = z * z
    then (x, y, z) :: acc
    else acc
    ) acc numbers
    ) acc numbers
    ) numbers
    end


    The first thing that I don't like is the call to rev at the end of generateIntsUpTo but I did it so I could accomplish a tail recursive function. Commented out is the line that let's me avoid the reversion of the list but then it's not tail recursive.



    Besides that the three nested foldr calls. I have been thinking on write my own recursive functions but I wanted to do it using the minimum amount of custom code.










    share|improve this question











    $endgroup$















      4












      4








      4





      $begingroup$


      Out of curiosity I'm porting the Haskell exercises of Programming in Haskell by Graham Hutton to Standard ML. At the beginning everything seemed pretty similar until I reached list comprehensions:




      A triple (x,y,z) of positive integers is called pythagorean if x2 + y2 = z2. Using a list comprehension, define a function pyths ∷ Int → [(Int,Int,Int)] that maps an integer n to all such triples with components in [1..n].




      The Haskell version is pretty simple:



      pyths ∷ Int → [(Int, Int, Int)]
      pyths n = [(x, y, z) |
      x ← [1..n],
      y ← [1..n],
      z ← [1..n],
      x^2 + y^2 == z^2]


      My Standard ML attempt not so much:



      (* No list comprehensions in SML so we need a helper function *)
      val generateIntsUpTo = fn n =>
      let val rec helper = fn current => fn xs =>
      if current <= n
      (* then current :: helper (current + 1) xs *)
      then helper (current + 1) (current :: xs)
      else xs
      in
      rev (helper 1 )
      end

      (* Now we can find the pyths *)
      val pyths = fn n =>
      let
      val numbers = generateIntsUpTo n
      in
      foldr (fn (x, acc) =>
      foldr (fn (y, acc) =>
      foldr (fn (z, acc) =>
      if x * x + y * y = z * z
      then (x, y, z) :: acc
      else acc
      ) acc numbers
      ) acc numbers
      ) numbers
      end


      The first thing that I don't like is the call to rev at the end of generateIntsUpTo but I did it so I could accomplish a tail recursive function. Commented out is the line that let's me avoid the reversion of the list but then it's not tail recursive.



      Besides that the three nested foldr calls. I have been thinking on write my own recursive functions but I wanted to do it using the minimum amount of custom code.










      share|improve this question











      $endgroup$




      Out of curiosity I'm porting the Haskell exercises of Programming in Haskell by Graham Hutton to Standard ML. At the beginning everything seemed pretty similar until I reached list comprehensions:




      A triple (x,y,z) of positive integers is called pythagorean if x2 + y2 = z2. Using a list comprehension, define a function pyths ∷ Int → [(Int,Int,Int)] that maps an integer n to all such triples with components in [1..n].




      The Haskell version is pretty simple:



      pyths ∷ Int → [(Int, Int, Int)]
      pyths n = [(x, y, z) |
      x ← [1..n],
      y ← [1..n],
      z ← [1..n],
      x^2 + y^2 == z^2]


      My Standard ML attempt not so much:



      (* No list comprehensions in SML so we need a helper function *)
      val generateIntsUpTo = fn n =>
      let val rec helper = fn current => fn xs =>
      if current <= n
      (* then current :: helper (current + 1) xs *)
      then helper (current + 1) (current :: xs)
      else xs
      in
      rev (helper 1 )
      end

      (* Now we can find the pyths *)
      val pyths = fn n =>
      let
      val numbers = generateIntsUpTo n
      in
      foldr (fn (x, acc) =>
      foldr (fn (y, acc) =>
      foldr (fn (z, acc) =>
      if x * x + y * y = z * z
      then (x, y, z) :: acc
      else acc
      ) acc numbers
      ) acc numbers
      ) numbers
      end


      The first thing that I don't like is the call to rev at the end of generateIntsUpTo but I did it so I could accomplish a tail recursive function. Commented out is the line that let's me avoid the reversion of the list but then it's not tail recursive.



      Besides that the three nested foldr calls. I have been thinking on write my own recursive functions but I wanted to do it using the minimum amount of custom code.







      haskell sml






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Apr 2 '18 at 0:44









      Grant Miller

      2332416




      2332416










      asked Oct 28 '17 at 22:53









      TaeTae

      1654




      1654






















          1 Answer
          1






          active

          oldest

          votes


















          2












          $begingroup$

          Feedback:




          • The Haskell pyths is not very efficient, and it contains duplicate answers.


          • fun instead of val/val rec is syntactic sugar for function declarations.


          • Instead of generateIntsUpTo n, use List.tabulate (n, fn i => i+1).


          • Using rev to achieve a tail-recursive function can be a good choice. Neither SML/NJ's or Moscow ML's List.tabulate is tail-recursive, though. If you're worrying about performance here, consider the fact that you don't actually need to store these numbers in lists -- this is purely a convenience so that you can use list comprehensions to iterate their combinations.



          Pythagorean triples in Standard ML




          I wanted to do it using the minimum amount of custom code.




          To achieve the same efficiency as the Haskell pyths and use the minimum amount of custom code, here is one version that uses List.filter, List.concat and List.tabulate:



          fun isPythTriple (x, y, z) = x*x + y*y = z*z
          fun tab1 n f = List.tabulate (n, fn i => f (i+1))
          fun pyths n =
          List.filter isPythTriple (
          List.concat (tab1 n (fn x =>
          List.concat (tab1 n (fn y =>
          tab1 n (fn z => (x,y,z)))))))


          This takes several seconds for pyths 10; there really is not reason to generate O(n³) list elements when the solution subset is so sparse.




          I have been thinking on write my own recursive functions




          Writing your own helper functions is really not something that should be avoided. Generally, using library functions is good, but Standard ML's library is somewhat limited. For example, List.tabulate can't iterate a range of numbers without generating the list in memory. And as you're hinting at, the multiple nested foldrs does not make the code particularly readable.



          You could for example combine List.tabulate and List.filter to reduce memory consumption:



          fun tabfilter (from, to, f) =
          if from > to then else
          case f from of
          SOME value => value :: tabfilter (from+1, to, f)
          | NONE => tabfilter (from+1, to, f)

          fun isPythTriple (x, y, z) = x*x + y*y = z*z

          fun pyths n =
          List.concat (tabfilter (1, n, fn x =>
          SOME (List.concat (tabfilter (1, n, fn y =>
          SOME (tabfilter (1, n, fn z => Option.filter isPythTriple (x, y, z))))))))


          This runs orders of magnitude faster. Still, it is a little convoluted.



          A plain recursive version (that generates the triples backwards):



          fun isPythTriple (x, y, z) = x*x + y*y = z*z
          fun pyths n =
          let fun loop (0, _, _) =
          | loop (x, 0, _) = loop (x-1, n, n)
          | loop (x, y, 0) = loop (x, y-1, n)
          | loop (t as (x, y, z)) =
          if isPythTriple t then t :: loop (x,y,z-1) else loop (x,y,z-1)
          in loop (n, n, n) end





          share|improve this answer











          $endgroup$













            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',
            autoActivateHeartbeat: false,
            convertImagesToLinks: false,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            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%2fcodereview.stackexchange.com%2fquestions%2f179054%2fporting-haskell-list-comprehensions-to-standard-ml%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            2












            $begingroup$

            Feedback:




            • The Haskell pyths is not very efficient, and it contains duplicate answers.


            • fun instead of val/val rec is syntactic sugar for function declarations.


            • Instead of generateIntsUpTo n, use List.tabulate (n, fn i => i+1).


            • Using rev to achieve a tail-recursive function can be a good choice. Neither SML/NJ's or Moscow ML's List.tabulate is tail-recursive, though. If you're worrying about performance here, consider the fact that you don't actually need to store these numbers in lists -- this is purely a convenience so that you can use list comprehensions to iterate their combinations.



            Pythagorean triples in Standard ML




            I wanted to do it using the minimum amount of custom code.




            To achieve the same efficiency as the Haskell pyths and use the minimum amount of custom code, here is one version that uses List.filter, List.concat and List.tabulate:



            fun isPythTriple (x, y, z) = x*x + y*y = z*z
            fun tab1 n f = List.tabulate (n, fn i => f (i+1))
            fun pyths n =
            List.filter isPythTriple (
            List.concat (tab1 n (fn x =>
            List.concat (tab1 n (fn y =>
            tab1 n (fn z => (x,y,z)))))))


            This takes several seconds for pyths 10; there really is not reason to generate O(n³) list elements when the solution subset is so sparse.




            I have been thinking on write my own recursive functions




            Writing your own helper functions is really not something that should be avoided. Generally, using library functions is good, but Standard ML's library is somewhat limited. For example, List.tabulate can't iterate a range of numbers without generating the list in memory. And as you're hinting at, the multiple nested foldrs does not make the code particularly readable.



            You could for example combine List.tabulate and List.filter to reduce memory consumption:



            fun tabfilter (from, to, f) =
            if from > to then else
            case f from of
            SOME value => value :: tabfilter (from+1, to, f)
            | NONE => tabfilter (from+1, to, f)

            fun isPythTriple (x, y, z) = x*x + y*y = z*z

            fun pyths n =
            List.concat (tabfilter (1, n, fn x =>
            SOME (List.concat (tabfilter (1, n, fn y =>
            SOME (tabfilter (1, n, fn z => Option.filter isPythTriple (x, y, z))))))))


            This runs orders of magnitude faster. Still, it is a little convoluted.



            A plain recursive version (that generates the triples backwards):



            fun isPythTriple (x, y, z) = x*x + y*y = z*z
            fun pyths n =
            let fun loop (0, _, _) =
            | loop (x, 0, _) = loop (x-1, n, n)
            | loop (x, y, 0) = loop (x, y-1, n)
            | loop (t as (x, y, z)) =
            if isPythTriple t then t :: loop (x,y,z-1) else loop (x,y,z-1)
            in loop (n, n, n) end





            share|improve this answer











            $endgroup$


















              2












              $begingroup$

              Feedback:




              • The Haskell pyths is not very efficient, and it contains duplicate answers.


              • fun instead of val/val rec is syntactic sugar for function declarations.


              • Instead of generateIntsUpTo n, use List.tabulate (n, fn i => i+1).


              • Using rev to achieve a tail-recursive function can be a good choice. Neither SML/NJ's or Moscow ML's List.tabulate is tail-recursive, though. If you're worrying about performance here, consider the fact that you don't actually need to store these numbers in lists -- this is purely a convenience so that you can use list comprehensions to iterate their combinations.



              Pythagorean triples in Standard ML




              I wanted to do it using the minimum amount of custom code.




              To achieve the same efficiency as the Haskell pyths and use the minimum amount of custom code, here is one version that uses List.filter, List.concat and List.tabulate:



              fun isPythTriple (x, y, z) = x*x + y*y = z*z
              fun tab1 n f = List.tabulate (n, fn i => f (i+1))
              fun pyths n =
              List.filter isPythTriple (
              List.concat (tab1 n (fn x =>
              List.concat (tab1 n (fn y =>
              tab1 n (fn z => (x,y,z)))))))


              This takes several seconds for pyths 10; there really is not reason to generate O(n³) list elements when the solution subset is so sparse.




              I have been thinking on write my own recursive functions




              Writing your own helper functions is really not something that should be avoided. Generally, using library functions is good, but Standard ML's library is somewhat limited. For example, List.tabulate can't iterate a range of numbers without generating the list in memory. And as you're hinting at, the multiple nested foldrs does not make the code particularly readable.



              You could for example combine List.tabulate and List.filter to reduce memory consumption:



              fun tabfilter (from, to, f) =
              if from > to then else
              case f from of
              SOME value => value :: tabfilter (from+1, to, f)
              | NONE => tabfilter (from+1, to, f)

              fun isPythTriple (x, y, z) = x*x + y*y = z*z

              fun pyths n =
              List.concat (tabfilter (1, n, fn x =>
              SOME (List.concat (tabfilter (1, n, fn y =>
              SOME (tabfilter (1, n, fn z => Option.filter isPythTriple (x, y, z))))))))


              This runs orders of magnitude faster. Still, it is a little convoluted.



              A plain recursive version (that generates the triples backwards):



              fun isPythTriple (x, y, z) = x*x + y*y = z*z
              fun pyths n =
              let fun loop (0, _, _) =
              | loop (x, 0, _) = loop (x-1, n, n)
              | loop (x, y, 0) = loop (x, y-1, n)
              | loop (t as (x, y, z)) =
              if isPythTriple t then t :: loop (x,y,z-1) else loop (x,y,z-1)
              in loop (n, n, n) end





              share|improve this answer











              $endgroup$
















                2












                2








                2





                $begingroup$

                Feedback:




                • The Haskell pyths is not very efficient, and it contains duplicate answers.


                • fun instead of val/val rec is syntactic sugar for function declarations.


                • Instead of generateIntsUpTo n, use List.tabulate (n, fn i => i+1).


                • Using rev to achieve a tail-recursive function can be a good choice. Neither SML/NJ's or Moscow ML's List.tabulate is tail-recursive, though. If you're worrying about performance here, consider the fact that you don't actually need to store these numbers in lists -- this is purely a convenience so that you can use list comprehensions to iterate their combinations.



                Pythagorean triples in Standard ML




                I wanted to do it using the minimum amount of custom code.




                To achieve the same efficiency as the Haskell pyths and use the minimum amount of custom code, here is one version that uses List.filter, List.concat and List.tabulate:



                fun isPythTriple (x, y, z) = x*x + y*y = z*z
                fun tab1 n f = List.tabulate (n, fn i => f (i+1))
                fun pyths n =
                List.filter isPythTriple (
                List.concat (tab1 n (fn x =>
                List.concat (tab1 n (fn y =>
                tab1 n (fn z => (x,y,z)))))))


                This takes several seconds for pyths 10; there really is not reason to generate O(n³) list elements when the solution subset is so sparse.




                I have been thinking on write my own recursive functions




                Writing your own helper functions is really not something that should be avoided. Generally, using library functions is good, but Standard ML's library is somewhat limited. For example, List.tabulate can't iterate a range of numbers without generating the list in memory. And as you're hinting at, the multiple nested foldrs does not make the code particularly readable.



                You could for example combine List.tabulate and List.filter to reduce memory consumption:



                fun tabfilter (from, to, f) =
                if from > to then else
                case f from of
                SOME value => value :: tabfilter (from+1, to, f)
                | NONE => tabfilter (from+1, to, f)

                fun isPythTriple (x, y, z) = x*x + y*y = z*z

                fun pyths n =
                List.concat (tabfilter (1, n, fn x =>
                SOME (List.concat (tabfilter (1, n, fn y =>
                SOME (tabfilter (1, n, fn z => Option.filter isPythTriple (x, y, z))))))))


                This runs orders of magnitude faster. Still, it is a little convoluted.



                A plain recursive version (that generates the triples backwards):



                fun isPythTriple (x, y, z) = x*x + y*y = z*z
                fun pyths n =
                let fun loop (0, _, _) =
                | loop (x, 0, _) = loop (x-1, n, n)
                | loop (x, y, 0) = loop (x, y-1, n)
                | loop (t as (x, y, z)) =
                if isPythTriple t then t :: loop (x,y,z-1) else loop (x,y,z-1)
                in loop (n, n, n) end





                share|improve this answer











                $endgroup$



                Feedback:




                • The Haskell pyths is not very efficient, and it contains duplicate answers.


                • fun instead of val/val rec is syntactic sugar for function declarations.


                • Instead of generateIntsUpTo n, use List.tabulate (n, fn i => i+1).


                • Using rev to achieve a tail-recursive function can be a good choice. Neither SML/NJ's or Moscow ML's List.tabulate is tail-recursive, though. If you're worrying about performance here, consider the fact that you don't actually need to store these numbers in lists -- this is purely a convenience so that you can use list comprehensions to iterate their combinations.



                Pythagorean triples in Standard ML




                I wanted to do it using the minimum amount of custom code.




                To achieve the same efficiency as the Haskell pyths and use the minimum amount of custom code, here is one version that uses List.filter, List.concat and List.tabulate:



                fun isPythTriple (x, y, z) = x*x + y*y = z*z
                fun tab1 n f = List.tabulate (n, fn i => f (i+1))
                fun pyths n =
                List.filter isPythTriple (
                List.concat (tab1 n (fn x =>
                List.concat (tab1 n (fn y =>
                tab1 n (fn z => (x,y,z)))))))


                This takes several seconds for pyths 10; there really is not reason to generate O(n³) list elements when the solution subset is so sparse.




                I have been thinking on write my own recursive functions




                Writing your own helper functions is really not something that should be avoided. Generally, using library functions is good, but Standard ML's library is somewhat limited. For example, List.tabulate can't iterate a range of numbers without generating the list in memory. And as you're hinting at, the multiple nested foldrs does not make the code particularly readable.



                You could for example combine List.tabulate and List.filter to reduce memory consumption:



                fun tabfilter (from, to, f) =
                if from > to then else
                case f from of
                SOME value => value :: tabfilter (from+1, to, f)
                | NONE => tabfilter (from+1, to, f)

                fun isPythTriple (x, y, z) = x*x + y*y = z*z

                fun pyths n =
                List.concat (tabfilter (1, n, fn x =>
                SOME (List.concat (tabfilter (1, n, fn y =>
                SOME (tabfilter (1, n, fn z => Option.filter isPythTriple (x, y, z))))))))


                This runs orders of magnitude faster. Still, it is a little convoluted.



                A plain recursive version (that generates the triples backwards):



                fun isPythTriple (x, y, z) = x*x + y*y = z*z
                fun pyths n =
                let fun loop (0, _, _) =
                | loop (x, 0, _) = loop (x-1, n, n)
                | loop (x, y, 0) = loop (x, y-1, n)
                | loop (t as (x, y, z)) =
                if isPythTriple t then t :: loop (x,y,z-1) else loop (x,y,z-1)
                in loop (n, n, n) end






                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited 20 mins ago

























                answered Mar 19 '18 at 11:12









                Simon ShineSimon Shine

                259211




                259211






























                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Code Review Stack Exchange!


                    • 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.


                    Use MathJax to format equations. MathJax reference.


                    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%2fcodereview.stackexchange.com%2fquestions%2f179054%2fporting-haskell-list-comprehensions-to-standard-ml%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







                    Popular posts from this blog

                    Список кардиналов, возведённых папой римским Каликстом III

                    Deduzione

                    Mysql.sock missing - “Can't connect to local MySQL server through socket”