I just started learning functional programming in SML and I want to know how I can combine the following two functions into a single function. The function isolate deletes the duplicates of a list of any type ('a) using the helper function 'removes'.
fun isolate [] = []
| isolate (l as x::xs) = x::isolate(remove(x,xs))
fun remove (x,[]) = []
| remove (x,l as y::ys) = if x = y then remove(x,ys) else y::remove(x,ys)
So, for the purpose of better understanding the constructs in the SML, how would you include the function remove within isolate? This may seem trivial, but I have thinking about it and can't figure it out. Thank you for your help!
One method would be to just define remove inside isolate.
fun isolate [] = []
| isolate (l as x::xs) =
let fun remove (x,[]) = []
| remove (x,l as y::ys) = if x = y
then remove(x,ys)
else y::remove(x,ys)
in
x::isolate(remove(x,xs))
end
Alternately, to make deduplication one function, though all this really does is use the library function List.filter to do the same thing that remove does.
fun isolate [] = []
| isolate (x::xs) = x::isolate(List.filter (fn y => y <> x) xs)
My idea: define a nested function to check if there are duplicated elements in the list:
fun set(nums:int list)=
let fun duplicate(x:int, l:int list)=
if null l
then false
else hd l=x orelse duplicate(x,tl l)
in
if null nums
then []
else
let val s=set(tl nums)
in if duplicate(hd nums,s)
then s
else hd nums::s
end
end
But it will give a list that only remains the last one for every duplicated elements.
I want to propound the following solutions of this problem:
fun remove_duplicates(xs: int list) =
let
fun check(xs: int list, item: int) =
if null xs
then false
else if hd xs = item
then true
else check (tl xs, item)
fun go_through_list(xs: int list) =
if null xs
then []
else if check(tl xs, hd xs)
then go_through_list(tl xs)
else hd xs :: go_through_list(tl xs)
in
go_through_list(xs)
end
It's more lines of code than in the solution propounded by #qaphla
My idea is to first sort the list, then recursively return a new list without duplicates:
fun remove_duplicates(l: int list) =
if null(l)
then []
else if null(tl l)
then l
else
let
fun compare(x: int, y: int) = x > y
fun sort(l: int list) = ListMergeSort.sort(compare) l
val l_sorted = sort(l)
in
if (hd l_sorted) = (hd (tl l_sorted))
then remove_duplicates(tl l_sorted)
else (hd l_sorted)::remove_duplicates(tl l_sorted)
end
Related
I'm having an issue with a recursive function that runs into a stack overflow on larger data sets so I've attempted to rewrite the function to use continuous recursion but to say I'm new to this would be an understatement. In the below example the first function, processList, gives the desired results on a small data set. The second function, processListCont, seems to work however I know there must be a bug since when I run the same small data set through it I get different results. Would processListCont be the correct way to express the processList function or am I missing something?
open System
type Something(id) =
member val id = id with get, set
member val children : list<Something> = [] with get, set
member val processed : bool = false with get, set
let rec processList (item:Something, itemList:list<Something>) =
for child in item.children do
let parent = itemList |> Seq.find (fun (i:Something) -> i.id = child.id)
if parent.processed = false then
parent.processed <- true
processList(parent, itemList)
let processListCont (item:Something, itemList:list<Something>) =
let rec _processListCont (item:Something, itemList:list<Something>, f) =
for child in item.children do
let parent = itemList |> Seq.find (fun (i:Something) -> i.id = child.id)
if parent.processed = false then
parent.processed <- true
f(parent, itemList)
_processListCont(item, itemList, (fun (item:Something, itemList:list<Something>) -> ()))
[<EntryPoint>]
let main argv =
// generate some data
let count = 10000
let idList = List.init count (fun index -> index)
let items = [for (id) in idList -> Something id]
let rnd = System.Random()
for i in items do
i.children <- List.init 100 (fun _ -> Something (rnd.Next(0, count - 1)))
// process the list
for i in items do
processList(i, items)
Console.WriteLine("Processing completed successfully")
Console.ReadKey()
|> ignore
0
The main issue is that you are calling the continuation f in the body of the for loop, but your non-tail-recursive version makes a recurisve call here.
This is tricky because you want to make a recursive call and the continuation should be "run the rest of the for loop". To express this, you'll need to use pattern matching instead of for loop.
I did not have a small example to test this, but I think something like this should do the trick:
let rec processListCont (item:Something, itemList:list<Something>) cont =
let rec loop (children:list<Something>) cont =
match children with
| child::tail ->
let parent = itemList |> Seq.find (fun (i:Something) -> i.id = child.id)
if parent.processed = false then
parent.processed <- true
processListCont (parent, itemList) (fun () -> loop tail cont)
| [] -> cont ()
loop item.children cont
Your code is unidiomatic in F# nonetheless consider the following example.
Suppose you want to add a list of numbers. You could write a function like this:
let rec add (l:int list) :int =
match l with
| [] -> 0
| x::xs -> x + (add xs)
but this would overflow the stack very quickly. Instead you could use cps to allow the code to become tail recursive:
type
cont = int -> int
let rec add2 (l:int list) (k:cont):int =
match l with
| [] -> k 0
| x::xs -> add2 xs (fun a -> k (a + x))
which you can use like this:
printfn "%i" (add2 [1..10000] id)
In a similar fashion you could rewrite your function like this:
type cont2 = Something list->unit
let rec p (item:Something, itemList:list<Something>) (k:cont2) =
match item.children with
| [] -> k []
| child::xs ->
let parent = itemList |> Seq.find (fun (i:Something) -> i.id = child.id)
if parent.processed = false then
parent.processed <- true
p (parent, itemList) (fun _ ->k xs)
else
k xs
let p2 (item:Something,itemList:Something list) = p (item,itemList) ignore
and you can call it like this:
for i in items do
p2(i, items)
I am new to SML.I got this sorting algo to implement where in each iteration,I have to pick minimum element from the list, remove it and create sorted list.
I did below coding to solve the problem.
I wrote 2 helper functions to pickup minimum element from the list and remove one element from the list.
fun minList(x::xs) =List.foldl (fn (x,y)=> if x<y then x else y) x (x::xs);
fun remElem(x, l) =
case l of
[] => []
| (x1::x2::xs) => if x1=x then (x2::xs) else (x1::xs)
;
Above two programs ran successfully.
Below is my sorting code.
fun simpSort(xs)=
let fun aux(xs,acc)=
case xs of
[] =>acc
| [x] => [x]
| (x::xs) => let val m = minList(xs)
in
aux(remElem(m,xs),acc#[m])
end
in aux(xs,[])
end;
This sorting program is giving error.
simpSort([3,1]);
uncaught exception Match [nonexhaustive match failure]
raised at: stdIn:433.59
Please advise.
Since you've solved your problem, here are some hints for improving a working version of your code:
Find the minimum of a list in a way that supports empty lists:
fun minimum [] = NONE
| minimum (x::xs) = SOME (foldl Int.min x xs)
Simplify pattern matching in the function that removes the first occurrence of an element from a list:
fun remove (_, []) = []
| remove (y, x::xs) =
if x = y
then xs
else x :: remove (y, xs)
Use those in combination to write simpSort:
fun simpSort xs =
case minimum xs of
NONE => []
| SOME x => x :: simpSort (remove (x, xs))
I shouldn't have to say that this sorting algorithm is terribly inefficient. :-P
I'm required to write a function that takes a list and splits it into 2 lists. The first list will hold elements in odd position and 2nd list hold elements in even position. Here's my attempt which gives me the following warning:
Warning: type vars not generalized because of
value restriction are instantiated to dummy types (X1,X2,...)
How to improve this?
fun splt (lst: int list) =
let
fun splt2 (lst: int list, count: int, lst1: int list, lst2: int list) =
if null lst
then []
else if (count mod 2 = 0)
then splt2 (tl lst, count+1, hd lst::lst1, lst2)
else splt2 (tl lst, count+1, lst1, hd lst::lst2)
in
splt2 (lst,1,[],[])
end
Here's a 2nd correct implementation that I found but I'm mainly interested in fixing the 1st one!!
I want to split a list into a tupple of odd and even elements
fun split [] = ([], [])
| split [x] = ([x], [])
| split (x1::x2::xs) =
let
val (ys, zs) = split xs
in
((x1::ys), (x2::zs))
end;
UPDATE: Improvement is just replace
if null lst then
[]
with this:
if null lst then
[lst1]#[lst2]
Here's some feedback for your code:
Give the function a proper name, like split or partition. The connotations that I have for those names are: Splitting (or exploding) takes something and returns one list of sub-components (e.g. string → char list), while partitioning takes a list of something and divides into two based on a predicate (e.g. List.partition), but they're not really set in stone.
Make up some variable names that aren't lst, since this is just an abbreviation of the type - surely redundant when even the type is there. For generic methods, good names can be hard to come by. A lot of ML code uses stuff like xs to imply a generic, plural form.
Ditch the type annotations; you'll get a polymorphic function that reads more easily:
fun split input =
let
fun split' (xys, count, xs, ys) = ...
in
split' (input, 1, [], [])
end
But really, the version you found online has some advantages: Pattern matching ensures that your lists have the right form before the function body is triggered, which minimizes run-time bugs. The functions hd and tl don't.
You could optimize the order of the cases slightly; i.e. list the most common case first. The parenthesis around x::xs and y::ys is unnecessary. Also, the two other cases (of one or zero elements) can be combined for brevity, but it doesn't matter much.
fun split (x1::x2::xs) =
let
val (ys, zs) = split xs
in
(x1::ys, x2::zs)
end
| split rest = (rest, [])
You could also use case-of instead of let-in-end:
fun split (x1::x2::xs) =
(case split xs of
(ys, zs) => (x1::ys, x2::zs))
| split rest = (rest, [])
Lastly, you may want to make this function tail-recursive:
fun split xys =
let fun split' (x1::x2::xs, ys, zs) = split' (xs, x1::ys, x2::zs)
| split' (rest, ys, zs) = (rev (rest # ys), rev zs)
in
split' (xys, [], [])
end
To help get you over the error you are encountering
you need to look at the type of the function which you have given
val splt = fn : int list -> 'a list
and ask yourself what does an 'a list hold?
- val foo = "foo"::(splt[1,2,3,4,5]);
val foo = ["foo"] : string list
- val bar = 52::splt[1,2,3,4,5];
val bar = [52] : int list
it can hold anything, but the compiler cannot tell by itself.
I'm trying to figure out how to return a list of the indexes of occurrences of a specific value in another list.
i.e.
indexes(1, [1,2,1,1,2,2,1]);
val it = [1,3,4,7] int list
I'm trying to figure out how lists work and trying to get better at recursion so I don't want to use List.nth (or any library functions) and I don't want to move into pattern matching quiet yet.
This is what I have so far
fun index(x, L) =
if null L then 0
else if x=hd(L) then
1
else
1 + index(x,tl L);
fun inde(x, L) =
if null L then []
else if x=hd(L) then
index(x, tl L) :: inde(x, tl L)
else
inde(x, tl L);
index(4, [4,2,1,3,1,1]);
inde(1,[1,2,1,1,2,2,1]);
This gives me something like [2, 1, 3, 0]. I guess I'm just having a hard time incrementing things properly to get the index. The index function itself works correctly though.
Instead you could also make two passes over the list: first add an index to each element in the list, and second grap the index of the right elements:
fun addIndex (xs, i) =
if null xs then []
else (hd xs, i) :: addIndex(tl xs, i+1)
fun fst (x,y) = x
fun snd (x,y) = y
fun indexi(n, xs) =
if fst(hd xs) = n then ... :: indexi(n, tl xs)
else indexi(n, tl xs)
(I left out part of indexi for the exercise.)
Where addIndex([10,20,30],0) gives you [(10,0),(20,1),(30,2)]. Now you can use addIndex and indexi to implement your original index function:
fun index(n, xs) = indexi(n, addIndex(xs, 0))
When you get that to work, you can try to merge addIndex and indexi into one function that does both.
However, you really want to write this with pattern matching, see for instance addIndex written using patterns:
fun addIndex ([], _) = []
| addIndex (x::xs, i) = (x,i) :: addIndex(xs, i+1)
If you do index(1,[2]), it gives 1, which is not correct. When the list is empty, it gives you zero. In a function like this, you'd probably want to use SOME/NONE feature.
For an assignment, i have written the following code in recursion. It takes a list of a vector data type, and a vector and calculates to closeness of the two vectors. This method works fine, but i don't know how to do the recursive version.
let romulus_iter (x:vector list) (vec:vector) =
let vector_close_hash = Hashtbl.create 10 in
let prevkey = ref 10000.0 in (* Define previous key to be a large value since we intially want to set closefactor to prev key*)
if List.length x = 0 then
{a=0.;b=0.}
else
begin
Hashtbl.clear vector_close_hash;
for i = 0 to (List.length x)-1 do
let vecinquestion = {a=(List.nth x i).a;b=(List.nth x i).b} in
let closefactor = vec_close vecinquestion vec in
if (closefactor < !prevkey) then
begin
prevkey := closefactor;
Hashtbl.add vector_close_hash closefactor vecinquestion
end
done;
Hashtbl.find vector_close_hash !prevkey
end;;
The general recursive equivalent of
for i = 0 to (List.length x)-1 do
f (List.nth x i)
done
is this:
let rec loop = function
| x::xs -> f x; loop xs
| [] -> ()
Note that just like a for-loop, this function only returns unit, though you can define a similar recursive function that returns a meaningful value (and in fact that's what most do). You can also use List.iter, which is meant just for this situation where you're applying an impure function that doesn't return anything meaningful to each item in the list:
List.iter f x