Reputation:
The goal is to combine (append) two lists while removing the duplicates.
let rec find_dup a lst =
match lst with
| [] -> false
| hd::tl -> if (hd == a) then true else find_dup a tl;;
let rec app lst2 lst1 =
match lst1 with
| [] -> lst2
| hd::tl -> if (find_dup hd lst2) then (app tl lst2)
else hd::app tl lst2
;;
I have my code like this but when the test case is
app [4;5;6;7] [1;2;3;4]
the answer should be [1;2;3;4;5;6;7]
but I keep getting
- : int list = [1; 2; 5; 3; 6; 4; 7]
What is going on?
Upvotes: -1
Views: 92
Reputation: 36451
Note that your find_dup
can be a little more terse without sacrificing clarity by using the short-circuiting nature of ||
. You also want to avoid physical equality (==
) as this will only work as expected in very few circumstances.
let rec find_dup a = function
| [] -> false
| hd::tl -> hd = a || find_dup a tl
We can see what glennsl is talking about if we trace out your example.
app [4; 5; 6; 7] [1; 2; 3; 4]
find_dup 1 [4; 5; 6; 7] -> false
1 :: app [2; 3; 4] [4; 5; 6; 7]
find_dup 4 [2; 3; 4] -> true
1 :: app [5; 6; 7] [2; 3; 4]
find_dup 2 [5; 6; 7] -> false
1 :: 2 :: app [3; 4] [5; 6; 7]
find_dup 5 [3; 4] -> false
1 :: 2 :: 5 :: app [6; 7] [3; 4]
find_dup 3 [6; 7] -> false
1 :: 2 :: 5 :: 3 :: app [4] [6; 7]
find_dup 6 [4] -> false
1 :: 2 :: 5 :: 3 :: 6 :: app [7] [4]
find_dup 4 [7] -> false
1 :: 2 :: 5 :: 3 :: 6 :: 4 :: app [] [7]
find_dup 7 [] -> false
1 :: 2 :: 5 :: 3 :: 6 :: 4 :: 7 :: app [] []
[1; 2; 5; 3; 6; 4; 7]
If we get rid of the reversal of lists:
let rec find_dup a lst =
match lst with
| [] -> false
| hd::tl -> hd = a || find_dup a tl
let rec app lst1 lst2 =
match lst1 with
| [] -> lst2
| hd::tl ->
if find_dup hd lst2 then app tl lst2
else hd :: app tl lst2
Now:
app [4; 5; 6; 7] [1; 2; 3; 4]
find_dup 4 [1; 2; 3; 4] -> true
app [5; 6; 7] [1; 2; 3; 4]
find_dup 5 [1; 2; 3; 4] -> false
5 :: app [6; 7] [1; 2; 3; 4]
find_dup 6 [1; 2; 3; 4] -> false
5 :: 6 :: app [7] [1; 2; 3; 4]
find_dup 7 [1; 2; 3; 4]
5 :: 6 :: 7 :: app [] [1; 2; 3; 4]
5 :: 6 :: 5 :: [1; 2; 3; 4]
[5; 6; 7; 1; 2; 3; 4]
Of course, this is inefficient, as we have a loop (find_dup
) within a loop (app
) meaning this has quadratic runtime complexity. We can do better.
Let's just append both lists and then sort the whole. We'd get:
[1; 2; 3; 4; 4; 5; 6; 7]
The sorting is O(n * log n), and concatenating the lists is O(n). Removing duplicates them is an O(n) operation.
# let[@tail_mod_cons] rec uniq = function
| ([] | [_]) as lst -> lst
| a::(b::_ as tl) when a = b -> uniq tl
| a :: tl -> a :: uniq tl;;
val uniq : 'a list -> 'a list = <fun>
# uniq [1; 2; 3; 4; 4; 5; 6; 7];;
- : int list = [1; 2; 3; 4; 5; 6; 7]
Upvotes: 0
Reputation: 29106
You're switching the lists around for every recursive call.
Look at the argument order of the function definition:
let rec app lst2 lst1
and then the recursive function call:
app tl lst2
Also, just to nitpick, find_dup
already exists in the standard library. It's called List.mem
.
Upvotes: 1