7

I'm trying to write a function that takes a list of natural numbers and returns as output the amount of different elements in it. For example, if I have the list [1,2,2,4,1], my function DifElem should output "3". I've tried many things, the closest I've gotten is this:

Fixpoint DifElem (l : list nat) : nat :=
   match l with
   | [] => 0
   | m::tm => 
   let n := listWidth tm in
   if (~ In m tm) then S n else n
end.

My logic is this: if m is not in the tail of the list then add one to the counter. If it is, do not add to the counter, so I'll only be counting once: when it's the last time it appears. I get the error:

Error: The term "~ In m tm" has type "Prop"
which is not a (co-)inductive type.

In is part of Coq's list standard library Coq.Lists.List. It is defined there as:

Fixpoint In (a:A) (l:list A) : Prop :=
   match l with
   | [] => False
   | b :: m => b = a \/ In a m
end.

I think I don't understand well enough how to use If then statements in definitions, Coq's documentation was not helpful enough.

I also tried this definition with nodup from the same library:

Definition Width (A : list nat ) := length (nodup ( A ) ).

In this case what I get as error is:

The term "A" has type "list nat" while it is expected to have
type "forall x y : ?A0, {x = y} + {x <> y}".

And I'm quiet confused as to what's going on here. I'd appreciate your help to solve this issue.

Sara
  • 339
  • 1
  • 8

3 Answers3

12

You seem to be confusing propositions (Prop) and booleans (bool). I'll try to explain in simple terms: a proposition is something you prove (according to Martin-Lof's interpretation it is a set of proofs), and a boolean is a datatype which can hold only 2 values (true / false). Booleans can be useful in computations, when there are only two possible outcomes and no addition information is not needed. You can find more on this topic in this answer by @Ptival or a thorough section on this in the Software Foundations book by B.C. Pierce et al. (see Propositions and Booleans section).

Actually, nodup is the way to go here, but Coq wants you to provide a way of deciding on equality of the elements of the input list. If you take a look at the definition of nodup:

Hypothesis decA: forall x y : A, {x = y} + {x <> y}.

Fixpoint nodup (l : list A) : list A :=
  match l with
    | [] => []
    | x::xs => if in_dec decA x xs then nodup xs else x::(nodup xs)
  end.  

you'll notice a hypothesis decA, which becomes an additional argument to the nodup function, so you need to pass eq_nat_dec (decidable equality fot nats), for example, like this: nodup eq_nat_dec l.

So, here is a possible solution:

Require Import Coq.Arith.Arith.
Require Import Coq.Lists.List.
Import ListNotations.

Definition count_uniques (l : list nat) : nat :=
  length (nodup eq_nat_dec l).

Eval compute in count_uniques [1; 2; 2; 4; 1].
(* = 3 : nat *)

Note: The nodup function works since Coq v8.5.

Anton Trunov
  • 15,074
  • 2
  • 23
  • 43
  • Thank you, I was actually confused about what Coq could take in "If... then..." statements. It can take booleans. Can it take ANY inductive definition with only 2 constructors? Or what are the constraints? Thank you very much for your suggestion. Both the source and how to fix my code were very helpful! – Sara Apr 22 '16 at 13:54
  • 1
    Yes, the `if` statement treats any two-constructor types as boolean. The first constructor is treated as true. More on this you can find in Adam Chlipala's [CPDT](http://adam.chlipala.net/cpdt/) book ([here](http://adam.chlipala.net/cpdt/html/MoreDep.html)). – Anton Trunov Apr 22 '16 at 14:01
  • FYI, the `MoreLogic` chapter of SF has been removed from more recent versions of SF :( I'm not sure where to find a mirror that does not include the solutions. – Carl Patenaude Poulin Jun 18 '17 at 13:49
  • @CarlPatenaudePoulin Thanks! I replaced the broken link with a [new one](https://softwarefoundations.cis.upenn.edu/current/Logic.html#lab186) which IIRC explains the same thing. – Anton Trunov Jun 18 '17 at 14:01
6

In addition to Anton's solution using the standard library I'd like to remark that mathcomp provides specially good support for this use case along with a quite complete theory on count and uniq. Your function becomes:

From mathcomp Require Import ssreflect ssrfun ssrbool eqtype ssrnat seq.

Definition count_uniques (T : eqType) (s : seq T) := size (undup s).

In fact, I think the count_uniques name is redundant, I'd prefer to directly use size (undup s) where needed.

ejgallego
  • 6,709
  • 1
  • 14
  • 29
5

Using sets:

Require Import MSets.
Require List. Import ListNotations.

Module NatSet := Make Nat_as_OT.

Definition no_dup l := List.fold_left (fun s x => NatSet.add x s) l NatSet.empty.
Definition count_uniques l := NatSet.cardinal (no_dup l).

Eval compute in count_uniques [1; 2; 2; 4; 1].
eponier
  • 3,062
  • 9
  • 20