Friday, February 1, 2013

atan2 in scala

atan2 function is defined as follows:



I wanted to try a small exercise of implementing this using scala(ignoring the fact that it is already natively implemented in java.lang.Math library).

The simplest implementation that I could think of is with a if expression this way:


def atan2(x: Double, y: Double):Double = {
  if (x >0)                     atan(y/x)
  else if (y >= 0 && x < 0)     Pi + atan(y/x)
  else if (y < 0 && x < 0)      atan(y/x) - Pi
  else if (y > 0 && x==0)       Pi/2
  else if (y < 0 && x==0)       -Pi/2
  else                          Double.NaN
}

However, I wanted to see at this point if it could be reimplemented using a case-match expression, and I could only come up with a souped up version of if/else this way:

def atan2(x: Double, y: Double): Double = {
  true match {
    case _ if (x>0)             => atan(y/x)
    case _ if (y >= 0 && x < 0) => atan(y/x) + Pi
    case _ if (y < 0 && x < 0)  => atan(y/x) - Pi
    case _ if (y > 0 && x==0)   => Pi/2
    case _ if (y <0 && x==0)    => -Pi/2
    case _                      => Double.NaN
  }
}

After reading up a little more on scala extractors, I could come up with a crazier version of the same:
def atan2(x: Double, y: Double): Double = {
  (x, y) match {
    case xgt0()           => atan(y/x)
    case ygte0Andxlt0()   => atan(y/x) + Pi
    case ylt0Andxlt0()    => atan(y/x) - Pi
    case ygt0Andxeq0()    => Pi/2
    case ylt0Andxeq0()    => -Pi/2
    case _                => Double.NaN
  }
}

However, this unfortunately requires defining 5 extractors this way:
object xgt0 {
 def unapply(tup: (Double, Double)): Boolean = tup match { case (x,y) => (x>0)}
}

object ygte0Andxlt0 {
 def unapply(tup: (Double, Double)): Boolean = tup match { case (x, y) => (y >= 0 && x < 0)}
}

object ylt0Andxlt0 {
 def unapply(tup: (Double, Double)): Boolean = tup match { case (x, y) => (y < 0 && x < 0)}
}
 
object ygt0Andxeq0 {
 def unapply(tup: (Double, Double)): Boolean = tup match { case (x, y) => (y > 0 && x == 0)}
}

object ylt0Andxeq0 {
 def unapply(tup: (Double, Double)): Boolean = tup match { case (x, y) => (y < 0 && x == 0)}
}

The only conclusion I could make out of this is that if I had to re-write atan2 in scala the best approach is probably using the first one - using if expression. I am sure there is definitely a more scala way of doing this and would definitely appreciate any suggestions on these approaches

No comments:

Post a Comment