0

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... )

ice-wind
  • 690
  • 4
  • 20
  • 1
    What you're describing isn't pattern matching, just conditionals with a different syntax. Why do you think using `match` is better than an `if` expression? It is possible to do this with a `match` expression using `when` guards, but that still isn't pattern matching, and I also don't think it's really what you want. – glennsl Apr 02 '22 at 14:53
  • @glennsl I'm not sure I understand the difference then, maybe ifs are the best way. For some reason they just always feel clunky to me – ice-wind Apr 02 '22 at 14:56
  • 1
    Pattern matching allows matching on structural patterns, binding parts a the structure to names and so on. They're not just (implied) boolean expressions. Patterns are also static in nature, because structure can be described by types and therefore also be optimized at compile time, while boolean expression are dynamic because it relies only on information available at runtime. – glennsl Apr 02 '22 at 15:21
  • There is a bit of a gray area though, because you can use literal patterns and could have some syntactic sugar to specify a range of literals. In fact such a thing does exist for character literals (e.g. `'a'..'z'`). But what you're describing isn't a bounded range, so that wouldn't work anyway. – glennsl Apr 02 '22 at 15:22

2 Answers2

2

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.

Chris
  • 26,361
  • 5
  • 21
  • 42
1

Integer ranges are only supported for char currently (up to OCaml 5.0). And even for char the support is in fact partial: the pattern 'a'..'d' is nothing more than a syntactic sugar for 'a'|'b'|'c'|'d'. But such encoding can only be used for small integer types like char: exploding the interval 0..int_max into int_max pattern is not an option for 63-bits integers.

It would be possible to support full integer ranges (or ranges on fully ordered primitives types) in the pattern matching compiler, but this would require a non-trivial amount of work that has not happened yet.

octachron
  • 17,178
  • 2
  • 16
  • 23