4

I am an F# newbie dealing with a trivial problem. How do I check if two integers are co-prime? I found this pythonic approach really interesting and, IMHO, elegant. I am trying to translate it in F#, without fortune. It is mainly due to my inexperience, I suppose.

In any case, this is my "best" attempt so far:

let prime (x, y) =                                          
  if y <> 0 then
      (x, y) <| (y, x % y)
  else 
      x;; 

It should result, i.e., in

prime 23 13;;
- : bool = true

Clearly, it does not work. What is the best way to approach such a problem in F#? I come from R programming, that requires a totally different mindform.

Community
  • 1
  • 1
Worice
  • 3,847
  • 3
  • 28
  • 49

2 Answers2

7

Almost a direct translation of the python code linked.

First we need to define a gcd function using recursion as "looping construct" instead of the python while (FP is more "recursion oriented")

let rec gcd = function
  | x, 0 -> x
  | x, y -> gcd (y, x % y)

That just leaves the coprime function to define which can be easily done in pointfree style by composing the previous gcd function with the partial application of equality with 1

let coprime = gcd >> (=) 1

which is functionaly the same as :

let coprime (x, y) = gcd (x, y) = 1

Aside from that we can make with a few tweaks the code more general (regarding numeric types) though I'm not sure it's worth it
(as one could prefer to use BigInteger.GreatestCommonDivisor when manipulating bigint for example)

open LanguagePrimitives

let inline gcd (x, y) =
  // we need an helper because inline and rec don't mix well
  let rec aux (x, y) =
    if y = GenericZero
    then x
    else aux (y, x % y)
  aux (x, y)

// no pointfree style, only function can be inlined not values
let inline coprime (x, y) = gcd (x, y) = GenericOne

From @Henrik Hansen's answer here is an updated version figuring an active pattern to ease readability and extract common behaviour

let (|LT|EQ|GT|) (x, y) =
  if x < y then LT
  elif x = y then EQ
  else GT

let areCoPrimes x y =
  let rec aux (x, y) =
    match x, y with
    | 0, _ | _, 0 -> false
    | LT          -> aux (x, y - x)
    | EQ          -> x = 1
    | GT          -> aux (x - y, y)
  aux (abs x, abs y)
Community
  • 1
  • 1
Sehnsucht
  • 5,019
  • 17
  • 27
6

Just for the sport: here is a version using the subtraction version of the Euclidean algorithm. Not as elegant as Sehnsuchts use of the division style and problably not as efficient:

let rec areCoPrimes a b =
    match a, b with
    | a, b when a < 0 -> areCoPrimes -a b
    | a, b when b < 0 -> areCoPrimes a -b
    | 0, b  -> false
    | a, 0 -> false
    | a, b when a = b -> a = 1
    | a, b when a > b -> areCoPrimes (a - b) b
    | a, b when a < b -> areCoPrimes a (b - a)

The Euclidean meat is the last three matches while the first four take care of values lesser than or equal to zero.

  • 1
    I am having a hard time switching to this programming paradigm, so that any example is welcome, thanks! – Worice Oct 21 '16 at 18:45
  • Could maybe use an inner auxiliary function (which takes the abs of the args) and use an active pattern to ease readability something along [this pastebin](http://pastebin.com/Yuwb2MsT) – Sehnsucht Oct 21 '16 at 20:41
  • @Sehnsucht: Nice link. My first attempt was somewhat like that without the active pattern, and it was not prettier. But the active pattern helps on that. If it's yours pastebin then update your answer with it. –  Oct 21 '16 at 21:01