user43944
user43944

Reputation: 159

F# : How to test the equality of sequence/list elements?

I would like to test whether all of elements in a list/sequence equals something

For example,a sequence of integers.

I would like to test if ALL element of the sequence equals to the same number.

My solution so far looks like imperative programming solution.

 let test seq =

    if Seq.forall(fun num -> num =1) then 1
    elif Seq.forall(fun num-> num = 2) then 2
    else None

Upvotes: 1

Views: 2210

Answers (3)

Tomas Petricek
Tomas Petricek

Reputation: 243041

Your solution is fine! Checking that all elements of a sequence have some value is not something you can nicely express using pattern matching - you have to use when clause, but that's doing exactly the same thing as your code (but with longer syntax). In cases like this, there is absolutely nothing wrong with using if.

You can extend pattern matching by definining custom active patterns, which gives you a nice option here. This is fairly advanced F#, but you can define a custom pattern ForAll n that succeeds when the input is a sequence containing just n values:

let (|ForAll|_|) n seq = 
  if Seq.forall (fun num -> num = n) seq then Some() else None

Note that success is represented as Some and failure as None. Now, you can solve your problem very nicely using pattern matching:

let test = function
  | ForAll 1 -> Some 1
  | ForAll 2 -> Some 2
  | _ -> None

This looks quite nice, but it's relying on more advanced features - I would do this if this is something that you need in more than one place. If I needed this just in one place, I'd go with ordinary if.

Upvotes: 5

ajuch
ajuch

Reputation: 415

Instead of the function you could use partial application on the equals operator.

> let yes = [1;1;1];;
val yes : int list = [1; 1; 1]

> let no = [1;2;3];;
val no : int list = [1; 2; 3]

> yes |> List.forall ((=) 1);;
val it : bool = true

> no |> List.forall ((=) 1);;
val it : bool = false

Maybe this looks more functional? And I think you should return Some 1 in your code, otherwise you'd get type errors since Option and int are not the same type...

If you want to check if all elements are equal (not just if they equal some constant), you could do this:

> [1;2] |> List.pairwise |> List.forall (fun (a,b) -> a = b)
;;
val it : bool = false

> [1;1;1] |> List.pairwise |> List.forall (fun (a,b) -> a = b)
;;
val it : bool = true

There you split your list into tuples and checks if the tuples are equal. This means transitively that all elements are equal.

Upvotes: 3

3615
3615

Reputation: 3877

You can rewrite it using pattern matching with a guard clause:

let testList = [2;2;2]
let isOne x = x = 1
let isTwo x = x = 2

let forAll = function 
  | list when list |> List.forall isOne  -> Some 1
  | list when list |> List.forall isTwo  -> Some 2
  | _ -> None

let res = forAll testList //Some 2

Upvotes: 3

Related Questions