Reputation: 348
I'm trying to write a function that takes in a list, and returns the number of successive duplicate elements in the list.
For example, given [1;2;3;3;4;4;5]
, the function should return 2
This is my initial implementation, but unfortunately it always returns 0
. I'm not quite sure where the bug lies.
Any help on how to improve it will be highly appreciated.
let rec count_successive_duplicates (lst: int list) (count: int) : (int) =
match lst with
| [] | [_]-> 0
| x :: y :: tl ->
if x = y then count_successive_duplicates (y::tl) (count + 1) else count_successive_duplicates (y::tl) count
;;
let () =
print_int (count_successive_duplicates [1;2;3;3;4;4;5] 0)
Upvotes: 1
Views: 624
Reputation: 36680
Late answer, but as a further suggestion, we can decompose this problem.
Let's get pairs.
# let rec pairs = function
| [] | [_] -> []
| a::(b::_ as tl) -> (a, b) :: pairs tl;;
val pairs : 'a list -> ('a * 'a) list = <fun>
# pairs [1;2;3;4;5;5;6;7];;
- : (int * int) list = [(1, 2); (2, 3); (3, 4); (4, 5); (5, 5); (5, 6); (6, 7)]
And now let's define count
that counts values in a list which return true for a predicate function.
# let count f lst =
List.fold_left
(fun i x -> if f x then i + 1 else i)
0 lst;;
val count : ('a -> bool) -> 'a list -> int = <fun>
# pairs [1;2;3;4;5;5;6;7]
|> count @@ fun (a, b) -> a = b;;
- : int = 1
But this isn't very efficient, as we have to generate a whole list of pairs and then iterate over it despite the fact that once we're done with a pair we don't really care about it again. Since this question was asked OCaml 4.07+ have the Seq
module which will allow us to make this a lazy operation and avoid the need to keep an entire list in memory.
# let pairs_seq lst =
let rec aux lst () =
match lst with
| [] | [_] -> Seq.Nil
| a::(b::_ as tl) -> Seq.Cons ((a, b), fun () -> aux tl ())
in
aux lst;;
val pairs_seq : 'a list -> unit -> ('a * 'a) Seq.node = <fun>
# let rec seq_count f s =
Seq.fold_left (fun i x -> if f x then i+1 else i) 0 s;;
val seq_count : ('a -> bool) -> 'a Seq.t -> int = <fun>
# [1;2;3;4;5;5;6;7]
|> pairs_seq
|> seq_count @@ fun (a, b) -> a = b;;
- : int = 1
Upvotes: 0
Reputation: 665564
In the end, you'll want to return the accumulator with the count instead of 0
always:
let rec count_successive_duplicates (lst: int list) (count: int) : (int) =
match lst with
| [] | [_] -> count
(* ^^^^^ */)
| x :: y :: tl -> count_successive_duplicates (y::tl) (count + if x = y then 1 else 0)
Upvotes: 1