Porting Haskell list comprehensions to Standard ML
$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 ifx2 + y2 = z2
. Using a list comprehension, define a functionpyths ∷ Int → [(Int,Int,Int)]
that maps an integern
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
$endgroup$
add a comment |
$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 ifx2 + y2 = z2
. Using a list comprehension, define a functionpyths ∷ Int → [(Int,Int,Int)]
that maps an integern
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
$endgroup$
add a comment |
$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 ifx2 + y2 = z2
. Using a list comprehension, define a functionpyths ∷ Int → [(Int,Int,Int)]
that maps an integern
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
$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 ifx2 + y2 = z2
. Using a list comprehension, define a functionpyths ∷ Int → [(Int,Int,Int)]
that maps an integern
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
haskell sml
edited Apr 2 '18 at 0:44
Grant Miller
2332416
2332416
asked Oct 28 '17 at 22:53
TaeTae
1654
1654
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
$begingroup$
Feedback:
The Haskell
pyths
is not very efficient, and it contains duplicate answers.fun
instead ofval
/val rec
is syntactic sugar for function declarations.Instead of
generateIntsUpTo n
, useList.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'sList.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 foldr
s 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
$endgroup$
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
$begingroup$
Feedback:
The Haskell
pyths
is not very efficient, and it contains duplicate answers.fun
instead ofval
/val rec
is syntactic sugar for function declarations.Instead of
generateIntsUpTo n
, useList.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'sList.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 foldr
s 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
$endgroup$
add a comment |
$begingroup$
Feedback:
The Haskell
pyths
is not very efficient, and it contains duplicate answers.fun
instead ofval
/val rec
is syntactic sugar for function declarations.Instead of
generateIntsUpTo n
, useList.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'sList.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 foldr
s 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
$endgroup$
add a comment |
$begingroup$
Feedback:
The Haskell
pyths
is not very efficient, and it contains duplicate answers.fun
instead ofval
/val rec
is syntactic sugar for function declarations.Instead of
generateIntsUpTo n
, useList.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'sList.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 foldr
s 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
$endgroup$
Feedback:
The Haskell
pyths
is not very efficient, and it contains duplicate answers.fun
instead ofval
/val rec
is syntactic sugar for function declarations.Instead of
generateIntsUpTo n
, useList.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'sList.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 foldr
s 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
edited 20 mins ago
answered Mar 19 '18 at 11:12
Simon ShineSimon Shine
259211
259211
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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