I have a numeric solving function (Double => Double) where I tried to be clever and use the Numeric[T] for keeping the two kinds of numbers apart.
This did not turn out to be easy. The remaining problems are:
Numeric[T] only has plus, minus etc. operators. implicit evidence$1: Numeric[Double] functions (see compiler output below)Ideally, I'd like to say, "A and B are both Double, but tell me if I mix them with each other".
Here's the code:
import scala.annotation.tailrec
class Sweep[A: Numeric, B: Numeric]( fDiff: A => B, initialSeed: A, initialStep: A, bEps: B )
{
val anum= evidence$1
val bnum= evidence$2
assert( anum.signum(initialStep) > 0 )
assert( bnum.lt( fDiff(initialSeed), fDiff( anum.plus(initialSeed,initialStep) )) ) // check that it's an increasing function
@tailrec
private def sweep( seed: A, step: A ): A = {
val bDiff= fDiff(seed)
if ( bnum.lt( bnum.abs(bDiff), bEps) ) { // done
seed
} else if ( bnum.signum(bDiff) != anum.signum(step) ) {
sweep( anum.plus(seed,step), step ) // continue, same step and direction ('bDiff' should go smaller)
} else {
val newStep = anum.toDouble(step) / -2.0
sweep( anum.minus(seed,newStep), newStep ) // reverse, smaller step
}
}
// Make sure we take the initial step in the right direction
//
private lazy val stepSign= -bnum.signum( fDiff(initialSeed) )
def apply: A = sweep( initialSeed, stepSign * initialStep )
}
object TestX extends App {
val t= new Sweep( (a: Double) => (a*a)-2, 1.0, 0.5, 1e-3 )()
println( t, math.sqrt(2.0) )
}
I've tried it also with the older (implicit anum: Numeric[A]) parameters, but was unable to have two such (both for A and B).
Here's what the compiler says (Scala 2.9):
fsc -deprecation -d out-make -unchecked src/xxx.scala
src/xxx.scala:25: error: type mismatch;
found : newStep.type (with underlying type Double)
required: A
sweep( anum.minus(seed,newStep), newStep ) // reverse, smaller step
^
src/xxx.scala:33: error: overloaded method value * with alternatives:
(x: Double)Double <and>
(x: Float)Float <and>
(x: Long)Long <and>
(x: Int)Int <and>
(x: Char)Int <and>
(x: Short)Int <and>
(x: Byte)Int
cannot be applied to (A)
def apply: A = sweep( initialSeed, stepSign * initialStep )
^
src/xxx.scala:38: error: not enough arguments for constructor Sweep: (implicit evidence$1: Numeric[Double], implicit evidence$2: Numeric[Double])Sweep[Double,Double].
Unspecified value parameters evidence$1, evidence$2.
val t= new Sweep( (a: Double) => (a*a)-2, 1.0, 0.5, 1e-3 )()
^
three errors found
Thanks for any ideas.
You want to be working with Fractional instead of Numeric. The following compiles for me:
import scala.annotation.tailrec
import math.Fractional.Implicits._
import Ordering.Implicits._
class Sweep[A: Fractional, B: Fractional](fDiff: A => B, initialSeed: A, initialStep: A, bEps: B) {
val aFractional = implicitly[Fractional[A]]
assert(initialStep.signum > 0)
assert(fDiff(initialSeed) < fDiff(initialSeed + initialStep))
@tailrec
private def sweep(seed: A, step: A): A = {
val bDiff = fDiff(seed)
if (bDiff.abs < bEps) {
seed
} else if (bDiff.signum != step.signum) {
sweep(seed + step, step)
} else {
val one = aFractional.one
val newStep = step / aFractional.fromInt(-2)
sweep(seed - newStep, newStep)
}
}
private lazy val stepSign = aFractional.fromInt(-fDiff(initialSeed).signum)
def apply: A = sweep(initialSeed, stepSign * initialStep)
}
val sweep = new Sweep((a: Double) => (a*a)-2, 1.0, 0.5, 1e-3)
println(sweep.apply, math.sqrt(2.0))
Note that to get things like -2.0 in type A, you'll need to assemble them manually from Fractional.one or use Fractional.fromInt.
The other thing worth pointing out is the use of math.Fractional.Implicits and Ordering.Implicits which will allow you to use normal math syntax (+, <, /, etc.) instead of calling functions like plus and div.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With