Search code examples
pattern-matchingocamlgrahams-scan

Can you pattern match integers to ranges in OCaml?


As part of Graham's Scan, I am writing a function that determines whether the line is turning left or right at a certain point. Considering we have a function determinant point -> point -> point -> int where determinant p0 p1 p2 returns det(p0p1, p0p2), here is the code :

(* Determines how the line turns at s, when going from t to p*)
let turn t p s =
  let det = det s p t in 
  if det < 0 then
    'r' (* Turning right *)
  else if det > 0 then
    'l'(* Turning left *)
  else 
    's' (* Going straight ahead *)
;;

Is there any way to do this with pattern matching instead ? Here is what I have tried, which obviously doesn't work :

let turn t p s =
  match det s p t with
  | < 0 -> 'r'
  | 0 -> 's'
  | > 0 -> 'l'
  
;;

( Note that the variable / function names are determined by the class ; while I do find single letter names slightly confusing, I may not change them... )


Solution

  • No.

    Well...

    Conditional guards on patterns could do what you're suggesting.

    let turn t p s =
      let det = det s p t in 
      if det < 0 then
        'r' (* Turning right *)
      else if det > 0 then
        'l'(* Turning left *)
      else 
        's' (* Going straight ahead *)'
    

    Would become:

    let turn t p s =
      match det s p t with
      | n when n < 0 -> 'r'
      | n when n > 0 -> 'l'
      | _            -> 's' 
    

    Which of these expresses what's going on more cleanly is a matter of opinion.

    What about...?

    The argument becomes more interesting with more complex data to match on. A very contrived example with far better actual implementations follows.

    type num = Int of int | Float of float
    
    let rec cmp a b =
      match a, b with
      | Int a, Int b ->
        if a < b then -1
        else if a > b then 1
        else 0
      | Float a, Float b -> 
        if a < b then -1
        else a > b then 1
        else 0
      | Int a, Float b ->
        let a = float_of_int a in
        if a < b then -1
        else a > b then 1
        else 0
      | Float a, Int b ->
        let b = float_of_int b in
        if a < b then -1
        else a > b then 1
        else 0
    

    Using conditional guards:

    let rec cmp a b =
      match a, b with
      | Int a, Int b when a < b                -> -1
      | Int a, Int b when a > b                ->  1
      | Int a, Int b                           ->  0
      | Float a, Float b when a < b            -> -1
      | Float a, Float b when a > b            ->  1
      | Float a, Float b                       ->  0
      | Int a, Float b when float_of_int a < b -> -1
      | Int a, Float b when float_of_int a > b ->  1
      | Int a, Float b                         ->  0
      | Float a, Int b when a < float_of_int b -> -1
      | Float a, Int b when a > float_of_int b ->  1
      | _                                      ->  0
    

    Still a matter of opinion, but conditional guards can help to "flatten" nested conditionals in pattern-matching.